Example #1
0
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
Example #3
0
 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))
Example #4
0
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
Example #5
0
        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
Example #6
0
 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,))
Example #7
0
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))
Example #8
0
        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
Example #9
0
 def _parameterize(t, params=_tvars):
     ps = get_parameters(t)
     if ps:
         return t[params[: len(ps)]]
     return t
Example #10
0
def get_generic_params(t):
    return get_parameters(t) or ()
Example #11
0
    _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):
Example #12
0
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