def check_args(cls: Type, generic_base_class: Type, typevar: TypeVar) -> int: """ Performs argument-validation for get_argument_to_typevar. :param cls: The sub-class specifying the argument. :param generic_base_class: The generic base-class specifying the type variable. :param typevar: The type variable to get the argument for. :return: The index of the typevar in the base class' parameters. """ # Make sure the class derives from the base-class if not issubclass(cls, generic_base_class): raise ValueError( f"{cls.__name__} does not derive from {generic_base_class.__name__}" ) # Make sure the base class is generic if not typing_inspect.is_generic_type(generic_base_class): raise TypeError(f"{generic_base_class.__name__} is not a generic type") # Get the type parameters to the generic base class parameters = typing_inspect.get_parameters(generic_base_class) # Make sure the type variable is a parameter to the base class if typevar not in parameters: raise ValueError( f"{typevar} is not a generic parameter of {generic_base_class.__name__}" ) return parameters.index(typevar)
def get_argument_to_typevar(cls: Type, generic_base_class: Type, typevar: TypeVar): """ Gets the argument given to a type variable parameterising a generic base class in a particular sub-class of that base. :param cls: The sub-class specifying the argument. :param generic_base_class: The generic base-class specifying the type variable. :param typevar: The type variable to get the argument for. :return: The argument to the type variable. """ # Check the arguments typevar_index: int = check_args(cls, generic_base_class, typevar) # Get the decendency path from derived to base class bases = [cls] while bases[-1] is not generic_base_class: # Keep track of if we found a base base_found = False # Try and find a generic base for base in typing_inspect.get_generic_bases(bases[-1]): if issubclass(base, generic_base_class): bases.append(base) base_found = True break # If we didn't find a generic base, find a non-generic base if not base_found: for base in bases[-1].__bases__: if issubclass(base, generic_base_class): bases.append(base) break # Search the dependency path for the type variable's final argument arg = None while len(bases) > 1: # Get the arguments to the generic base class args = typing_inspect.get_args(bases[-2]) # If no arguments are given, the signature stays the same if len(args) == 0: bases = bases[:-2] + bases[-1:] continue # Get the argument to this typevar arg = args[typevar_index] # If it's another type variable, keep looking for the argument to this # type variable if typing_inspect.is_typevar(arg): typevar_index = typing_inspect.get_parameters(bases[-2]).index(arg) bases = bases[:-1] continue # Otherwise return the argument to the type variable return arg return arg
def test_parameters(self): T = TypeVar('T') S_co = TypeVar('S_co', covariant=True) U = TypeVar('U') self.assertEqual(get_parameters(int), ()) self.assertEqual(get_parameters(Generic), ()) self.assertEqual(get_parameters(Union), ()) self.assertEqual(get_parameters(List[int]), ()) self.assertEqual(get_parameters(Generic[T]), (T,)) self.assertEqual(get_parameters(Tuple[List[T], List[S_co]]), (T, S_co)) self.assertEqual(get_parameters(Union[S_co, Tuple[T, T]][int, U]), (U,)) self.assertEqual(get_parameters(Mapping[T, Tuple[S_co, T]]), (T, S_co))
def get_origin_type(t: typing.Type) -> typing.Type: """ Takes in a type, and if it is a generic type, returns the original type hint. typing.List[int] -> typing.List[T] """ t = get_origin(t) or t params = typing_inspect.get_parameters(t) if params: return t[params] return t
def wrapper(*a: object, **kw: Dict[str, object]) -> object: #debug('short circuit wrapper ', original) space = optional_context_statespace() if (not self.engaged) or (not space) or space.running_framework_code: return original(*a, **kw) # We *heavily* bias towards concrete execution, because it's often the case # that a single short-circuit will render the path useless. TODO: consider # decaying short-crcuit probability over time. use_short_circuit = space.fork_with_confirm_or_else(0.95) if not use_short_circuit: #debug('short circuit: Choosing not to intercept', original) return original(*a, **kw) try: self.engaged = False debug('short circuit: Short circuiting over a call to ', original) self.intercepted = True return_type = sig.return_annotation # Deduce type vars if necessary if len(typing_inspect.get_parameters(sig.return_annotation)) > 0 or typing_inspect.is_typevar(sig.return_annotation): typevar_bindings: typing.ChainMap[object, type] = collections.ChainMap( ) bound = sig.bind(*a, **kw) bound.apply_defaults() for param in sig.parameters.values(): argval = bound.arguments[param.name] value_type = python_type(argval) #debug('unify', value_type, param.annotation) if not dynamic_typing.unify(value_type, param.annotation, typevar_bindings): debug( 'aborting intercept due to signature unification failure') return original(*a, **kw) #debug('unify bindings', typevar_bindings) return_type = dynamic_typing.realize( sig.return_annotation, typevar_bindings) debug('short circuit: Deduced return type was ', return_type) # adjust arguments that may have been mutated assert subconditions is not None bound = sig.bind(*a, **kw) mutable_args = subconditions.mutable_args for argname, arg in bound.arguments.items(): if mutable_args is None or argname in mutable_args: forget_contents(arg) if return_type is type(None): return None # note that the enforcement wrapper ensures postconditions for us, so we # can just return a free variable here. return proxy_for_type(return_type, 'proxyreturn' + space.uniq()) finally: self.engaged = True
def test_parameters(self): T = TypeVar('T') S_co = TypeVar('S_co', covariant=True) U = TypeVar('U') self.assertEqual(get_parameters(int), ()) self.assertEqual(get_parameters(Generic), ()) self.assertEqual(get_parameters(Union), ()) if not LEGACY_TYPING: self.assertEqual(get_parameters(List[int]), ()) else: # in 3.5.3 a list has no __args__ and instead they are used in __parameters__ # in 3.5.1 the behaviour is normal again. pass self.assertEqual(get_parameters(Generic[T]), (T,)) self.assertEqual(get_parameters(Tuple[List[T], List[S_co]]), (T, S_co)) if EXISTING_UNIONS_SUBSCRIPTABLE: self.assertEqual(get_parameters(Union[S_co, Tuple[T, T]][int, U]), (U,)) self.assertEqual(get_parameters(Mapping[T, Tuple[S_co, T]]), (T, S_co)) if PY39: self.assertEqual(get_parameters(dict[int, T]), (T,))
def test_type_inspect(): ty = typing basic_type = int list_type = ty.List[int] dict_type = ty.Dict[int, str] tuple_type = ty.Tuple[ty.Dict[int, str], str, ty.List[str]] union_type = ty.Union[list_type, dict_type, None] type_a = ty.TypeVar('TA') type_b = ty.TypeVar('TB') gen_list_type = ty.List[type_a] gen_dict_type = ty.Dict[type_a, type_b] gen_tuple_type = ty.Tuple[type_a, type_b] test_types = [ basic_type, list_type, dict_type, tuple_type, union_type, gen_list_type, gen_dict_type, gen_tuple_type ] print("ti.get_origin:\n") for t in test_types: print(" ", ti.get_origin(t)) print("ti.get_parameters:\n") for t in test_types: print(" ", ti.get_parameters(t)) print("ti.get_args:\n") for t in test_types: print(" ", ti.get_args(t)) print("ti.get_generic_type:\n") for t in test_types: print(" ", ti.get_generic_type(t)) print("ti.get_generic_bases:\n") for t in test_types: print(" ", ti.get_generic_bases(t)) print("ti.typed_dict_keys:\n") for t in test_types: print(" ", ti.get_generic_bases(t))
async def wrapper(*args, **kwargs): nonlocal cls reply = await f(*args, **kwargs) if cls is None: return reply if 'error' in reply: cls = CLASSES['Error'] if typing_inspect.is_generic_type(cls) and issubclass(typing_inspect.get_origin(cls), Sequence): parameters = typing_inspect.get_parameters(cls) result = [] item_cls = parameters[0] for item in reply: result.append(item_cls.from_json(item)) """ if 'error' in item: cls = CLASSES['Error'] else: cls = item_cls result.append(cls.from_json(item)) """ else: result = cls.from_json(reply['response']) return result
def _parameterize(t, params=_tvars): ps = get_parameters(t) if ps: return t[params[: len(ps)]] return t
def get_generic_params(t): return get_parameters(t) or ()
_tvars = (TypeVar("A"), TypeVar("B"), TypeVar("C"), TypeVar("D")) _tvars_for_base = { typing.Counter: (_tvars[0], int), typing.ByteString: (Union[int, bytes, bytearray],), } if sys.version_info >= (3, 8): abstract_types = (typing.Protocol, typing.Generic) else: abstract_types = (typing._Protocol, typing.Generic) generics = { t for t in vars(typing).values() if t not in abstract_types and isinstance(t, typing._GenericAlias) and get_parameters(t) } generics.add(Callable) def _parameterize(t, params=_tvars): ps = get_parameters(t) if ps: return t[params[: len(ps)]] return t def _set_typing_bases(): global typing_bases typing_bases = collections.defaultdict(set) for u, t in combinations(generics, 2):
def buildTypes(schema, capture): INDENT = " " for kind in sorted((k for k in schema.types if not isinstance(k, str)), key=lambda x: str(x)): name = schema.types[kind] if name in capture and name not in NAUGHTY_CLASSES: continue args = Args(schema, kind) # Write Factory class for _client.py make_factory(name) # Write actual class source = [""" class {}(Type): _toSchema = {} _toPy = {} def __init__(self{}{}, **unknown_fields): ''' {} '''""".format( name, # pprint these to get stable ordering across regens pprint.pformat(args.PyToSchemaMapping(), width=999), pprint.pformat(args.SchemaToPyMapping(), width=999), ", " if args else "", args.as_kwargs(), textwrap.indent(args.get_doc(), INDENT * 2))] if not args: source.append("{}self.unknown_fields = unknown_fields".format(INDENT * 2)) else: # do the validation first, before setting the variables for arg in args: arg_name = name_to_py(arg[0]) arg_type = arg[1] arg_type_name = strcast(arg_type) if arg_type in basic_types or arg_type is typing.Any: source.append("{}{}_ = {}".format(INDENT * 2, arg_name, arg_name)) elif type(arg_type) is typing.TypeVar: source.append("{}{}_ = {}.from_json({}) " "if {} else None".format(INDENT * 2, arg_name, arg_type_name, arg_name, arg_name)) elif typing_inspect.is_generic_type(arg_type) and issubclass(typing_inspect.get_origin(arg_type), Sequence): parameters = typing_inspect.get_parameters(arg_type) value_type = ( parameters[0] if len(parameters) else None ) if type(value_type) is typing.TypeVar: source.append( "{}{}_ = [{}.from_json(o) " "for o in {} or []]".format(INDENT * 2, arg_name, strcast(value_type), arg_name)) else: source.append("{}{}_ = {}".format(INDENT * 2, arg_name, arg_name)) elif typing_inspect.is_generic_type(arg_type) and issubclass(typing_inspect.get_origin(arg_type), Mapping): parameters = typing_inspect.get_parameters(arg_type) value_type = ( parameters[0] if len(parameters) else None ) if type(value_type) is typing.TypeVar: source.append( "{}{}_ = {{k: {}.from_json(v) " "for k, v in ({} or dict()).items()}}".format( INDENT * 2, arg_name, strcast(value_type), arg_name)) else: source.append("{}{}_ = {}".format(INDENT * 2, arg_name, arg_name)) else: source.append("{}{}_ = {}".format(INDENT * 2, arg_name, arg_name)) if len(args) > 0: source.append('\n{}# Validate arguments against known Juju API types.'.format(INDENT * 2)) for arg in args: arg_name = "{}_".format(name_to_py(arg[0])) arg_type, arg_sub_type, ok = kind_to_py(arg[1]) if ok: source.append('{}'.format(buildValidation(arg_name, arg_type, arg_sub_type, ident=INDENT * 2))) for arg in args: arg_name = name_to_py(arg[0]) source.append('{}self.{} = {}_'.format(INDENT * 2, arg_name, arg_name)) # Ensure that we take the kwargs (unknown_fields) and put it on the # Results/Params so we can inspect it. source.append("{}self.unknown_fields = unknown_fields".format(INDENT * 2)) source = "\n".join(source) capture.clear(name) capture[name].write(source) capture[name].write("\n\n") co = compile(source, __name__, "exec") ns = _getns(schema) exec(co, ns) cls = ns[name] CLASSES[name] = cls