Пример #1
0
 def __setattr__(self, key, value):
     if hasattr(self, key) or key in get_type_hints(self.__class__,
                                                    include_extras=True):
         if not check_int((self.__class__, key),
                          value,
                          suppress_warning_for_unresolved_hints=True):
             typ = get_type_hints(self.__class__, include_extras=True)[key]
             r = get_range(typ)
             if r is not None:
                 raise IntegerBoundError(
                     f(
                         _("The value '{value}' does not fit into the field '{key}'. "
                           "The value must fit into the range [{r.min},{r.max}]."
                           )))
     super().__setattr__(key, value)
Пример #2
0
 def _infer_multiple_outputs(self):
     try:
         return_type = typing_extensions.get_type_hints(self.function).get("return", Any)
     except Exception:  # Can't evaluate retrurn type.
         return False
     ttype = getattr(return_type, "__origin__", return_type)
     return ttype == dict or ttype == Dict
Пример #3
0
    def _collect_fields(cls) -> Dict[str, Tuple[AnnotationInfo, FieldInfo]]:
        """Centralize publicly named fields and their corresponding annotations."""
        annotations = get_type_hints(  # pylint:disable=unexpected-keyword-arg
            cls, include_extras=True)
        attrs = cls._get_model_attrs()

        missing = []
        for name, attr in attrs.items():
            if inspect.isroutine(attr):
                continue
            if not _is_field(name):
                annotations.pop(name, None)
            elif name not in annotations:
                missing.append(name)

        if missing:
            raise SchemaInitError(f"Found missing annotations: {missing}")

        fields = {}
        for field_name, annotation in annotations.items():
            field = attrs[field_name]  # __init_subclass__ guarantees existence
            if not isinstance(field, FieldInfo):
                raise SchemaInitError(
                    f"'{field_name}' can only be assigned a 'Field', " +
                    f"not a '{type(field)}.'")
            fields[field.name] = (AnnotationInfo(annotation), field)
        return fields
Пример #4
0
def get_return_type_hints(func: Callable) -> Any:
    """Return function return types.

    This is because `get_type_hints` result in error for some types in older
    versions of python and also that `__annotations__` contains only string, not types.

    Note:
        Sometimes it may use eval as literal_eval cannot use users globals so types like pd.DataFrame would
        fail. Therefore do not use it for evaluating types of users input for sake of security.

    Args:
        func (Callable): Function with type hints.

    Returns:
        Any: Type of return.

    Example:
        >>> # You can use Union as well as Literal
        >>> def union_return() -> int | float:
        ...     return 1
        >>> inferred_type = get_return_type_hints(union_return)
        >>> 'int' in str(inferred_type) and 'float' in str(inferred_type)
        True
        >>> def literal_return() -> Literal[1, 2, 3]:
        ...     return 1
        >>> inferred_type = get_return_type_hints(literal_return)
        >>> 'Literal' in str(inferred_type)
        True
    """
    if isinstance(func, staticmethod):
        func = func.__func__

    try:
        types = get_type_hints(func).get("return")
    except Exception:
        types = func.__annotations__.get("return")

    if isinstance(types, str) and "Union" in types:
        types = eval(types, func.__globals__)

    # If Union operator |, e.g. int | str - get_type_hints() result in TypeError
    # Convert it to Union
    elif isinstance(types, str) and "|" in types:
        str_types = [i.strip() for i in types.split("|")]
        for i, j in enumerate(str_types):
            for k in ["list", "dict", "tuple"]:
                if k in j:
                    str_types[i] = j.replace(k, k.capitalize())
        try:
            evaluated_types = [eval(i, {**globals(), **func.__globals__}) for i in str_types]
        except Exception:
            raise RuntimeError("Evaluating of function return type failed. Try it on python 3.9+.")

        types = Union[evaluated_types[0], evaluated_types[1]]  # type: ignore

        if len(evaluated_types) > 2:
            for i in evaluated_types[2:]:
                types = Union[types, i]

    return types
Пример #5
0
Файл: tcp.py Проект: rho2/alpyro
 def decode(self, msg: RosMessage, buffer: bytes, offset: int = 0) -> int:
     for name, t in get_type_hints(msg, include_extras=True).items():  # type: ignore
         if get_origin(t) is Final:
             continue
         val, offset = self._decode_value(msg, buffer, offset, t)
         setattr(msg, name, val)
     return offset
Пример #6
0
def args_to_matchers(
    function: Callable[..., Any], ) -> Dict[str, libcst_typing.Matcher]:
    """Extract node matchers from the function arguments.

    Untyped arguments will get a `DoNotCare` matcher, while arguments typed and
    annoated with a `BaseMatcherNode` will return that matcher.
    """
    matchers: Dict[str, libcst_typing.Matcher] = {}

    # Create default matchers for all arguments
    args = function.__code__.co_varnames[:function.__code__.co_argcount]
    if args:
        for name in args:
            if name == "self":
                continue
            matchers[name] = libcst.matchers.DoNotCare()

    # Check if any of the arguments was annotated with a Matcher
    for name, type_declaration in typing_extensions.get_type_hints(
            function, include_extras=True).items():
        if typing_extensions.get_origin(type_declaration) is Annotated:
            # Check if there is a matcher
            arg = typing_extensions.get_args(type_declaration)[1]
            if isinstance(arg, libcst.matchers.BaseMatcherNode):
                matchers[name] = arg

    return matchers
Пример #7
0
def eval_field_types(dataclass: DataClass) -> None:
    """Evaluate field types of a dataclass or its object."""
    hints = get_type_hints(dataclass, include_extras=True)  # type: ignore

    for field in dataclass.__dataclass_fields__.values():
        if isinstance(field.type, str):
            field.type = hints[field.name]
Пример #8
0
    def add_api_route(
        self,
        path: str,
        endpoint: Callable[..., Any],
        **kwargs: Any,
    ) -> None:
        return_type = get_type_hints(endpoint)["return"]

        success_result, failure_result = return_type.__args__

        endpoint = self._unpacked_container(endpoint)

        responses = {}
        if get_origin(failure_result) is Union:
            for error in failure_result.__args__:
                annotation = error.__annotations__["detail"]
                responses[error.status_code] = {"model": ApiErrorSchema[annotation]}  # type: ignore[valid-type]
        else:
            annotation = failure_result.__annotations__["detail"]
            responses[failure_result.status_code] = {"model": ApiErrorSchema[annotation]}  # type: ignore[valid-type]

        if kwargs["response_model"] is None:
            kwargs["response_model"] = success_result

        if kwargs["responses"] is None:
            kwargs["responses"] = responses

        return super().add_api_route(path, endpoint, **kwargs)
Пример #9
0
    def __initialize__(self):
        """Effectively gather configuration information"""
        # Check if not initialized
        if self.__initialized__:
            return
        self.__initialized__ = True

        from .objects import Task

        # Get the module
        module = inspect.getmodule(self.originaltype)
        self._file = Path(inspect.getfile(self.originaltype)).absolute()
        self._module = module.__name__
        self._package = module.__package__

        # The class of the object

        self._arguments = ChainMap(
            {}, *(tp.arguments
                  for tp in self.parents()))  # type: ChainMap[Argument, Any]

        # Add arguments from annotations
        for annotation in self.annotations:
            annotation.process(self)

        # Add task
        if self.taskcommandfactory is not None:
            self.task = self.taskcommandfactory(self)
        elif issubclass(self.basetype, Task):
            self.task = self.getpythontaskcommand()

        # Add arguments from type hints
        from .arguments import TypeAnnotation

        if hasattr(self.basetype, "__annotations__"):
            typekeys = set(
                self.basetype.__dict__.get("__annotations__", {}).keys())
            hints = get_type_hints(self.basetype, include_extras=True)
            for key, typehint in hints.items():
                # Filter out hints from parent classes
                if key in typekeys:
                    options = None
                    if isinstance(typehint, _AnnotatedAlias):
                        for value in typehint.__metadata__:
                            if isinstance(value, TypeAnnotation):
                                options = value(options)
                        if options is not None:
                            try:
                                self.addArgument(
                                    options.create(key, self.objecttype,
                                                   typehint.__args__[0]))
                            except Exception:
                                logger.error(
                                    "while adding argument %s of %s",
                                    key,
                                    self.objecttype,
                                )
                                raise
Пример #10
0
def napari_type_hints(obj: Any) -> Dict[str, Any]:
    import napari

    from .. import layers, viewer

    return get_type_hints(obj, {
        'napari': napari,
        **viewer.__dict__,
        **layers.__dict__
    })
Пример #11
0
def _is_valid_typeddict_item(
    td: type[TypedDict], key: str, value: Any  # type: ignore [valid-type]
) -> bool:
    """Check if `key` and `value` form a valid item for the TypedDict `td`."""
    annotations = get_type_hints(td)
    if key not in annotations:
        return False
    if get_origin(annotations[key]) is Literal:
        return value in get_args(annotations[key])
    return isinstance(value, annotations[key])
Пример #12
0
def napari_type_hints(obj: Any) -> Dict[str, Any]:
    """variant of get_type_hints with napari namespace awareness."""
    import napari

    return get_type_hints(
        obj,
        {
            'napari': napari,
            **viewer.__dict__,
            **layers.__dict__,
            **components.__dict__,
        },
    )
Пример #13
0
def napari_type_hints(obj: Any) -> Dict[str, Any]:
    import napari

    from .. import components, layers, viewer

    return get_type_hints(
        obj,
        {
            'napari': napari,
            **viewer.__dict__,
            **layers.__dict__,
            'LayerList': components.LayerList,
        },
    )
Пример #14
0
    def _infer_multiple_outputs(self):
        try:
            return_type = typing_extensions.get_type_hints(self.function).get("return", Any)
        except Exception:  # Can't evaluate retrurn type.
            return False

        # Get the non-subscripted type. The ``__origin__`` attribute is not
        # stable until 3.7, but we need to use ``__extra__`` instead.
        # TODO: Remove the ``__extra__`` branch when support for Python 3.6 is
        # dropped in Airflow 2.3.
        if sys.version_info < (3, 7):
            ttype = getattr(return_type, "__extra__", return_type)
        else:
            ttype = getattr(return_type, "__origin__", return_type)

        return ttype == dict or ttype == Dict
Пример #15
0
def provider(func: C) -> C:
    """Decorator that declares `func` as a provider of its return type.

    Note, If func returns `Optional[Type]`, it will be registered as a provider
    for Type.

    Examples
    --------
    >>> @provider
    >>> def provides_int() -> int:
    ...     return 42
    """
    return_hint = get_type_hints(func).get('return')
    if get_origin(return_hint) == Union:
        if ((args := get_args(return_hint)) and len(args) == 2
                and type(None) in args):
            return_hint = next(a for a in args if a is not type(None))  # noqa
Пример #16
0
Файл: tcp.py Проект: rho2/alpyro
    def encode(self, msg: RosMessage, buffer: Optional[bytearray] = None) -> bytearray:
        if buffer is None:
            buffer = bytearray()

        for name, t in get_type_hints(msg, include_extras=True).items():  # type: ignore
            origin = get_origin(t)

            if origin is Final:
                continue

            def_factory = t
            if origin is Annotated and get_origin(get_args(t)[0]) is list:
                def_factory = list

            val = getattr(msg, name, def_factory())
            self._encode_value(val, t, buffer)
        return buffer
Пример #17
0
    def _tf_extension_type_fields(cls):  # pylint: disable=no-self-argument
        """An ordered list describing the fields of this ExtensionType.

    Returns:
      A list of `ExtensionTypeField` objects.  Forward references are resolved
      if possible, or left unresolved otherwise.
    """
        if '_tf_extension_type_cached_fields' in cls.__dict__:  # do not inherit.
            return cls._tf_extension_type_cached_fields

        try:
            # Using include_extras=False will replace all Annotated[T, ...] with T.
            # The typing_extensions module is used since this is only supported in
            # Python 3.9.
            type_hints = typing_extensions.get_type_hints(cls,
                                                          include_extras=False)
            ok_to_cache = True  # all forward references have been resolved.
        except (NameError, AttributeError):
            # Unresolved forward reference -- gather type hints manually.
            # * NameError comes from an annotation like `Foo` where class
            #   `Foo` hasn't been defined yet.
            # * AttributeError comes from an annotation like `foo.Bar`, where
            #   the module `foo` exists but `Bar` hasn't been defined yet.
            # Note: If a user attempts to instantiate a `ExtensionType` type that
            # still has unresolved forward references (e.g., because of a typo or a
            # missing import), then the constructor will raise an exception.
            type_hints = {}
            for base in reversed(cls.__mro__):
                type_hints.update(base.__dict__.get('__annotations__', {}))
            ok_to_cache = False

        fields = []
        for (name, value_type) in type_hints.items():
            default = getattr(
                cls, name, extension_type_field.ExtensionTypeField.NO_DEFAULT)
            fields.append(
                extension_type_field.ExtensionTypeField(
                    name, value_type, default))
        fields = tuple(fields)

        if ok_to_cache:
            cls._tf_extension_type_cached_fields = fields

        return fields
Пример #18
0
def transform_function_to_interface(
        fn: typing.Callable,
        docstring: Optional[Docstring] = None) -> Interface:
    """
    From the annotations on a task function that the user should have provided, and the output names they want to use
    for each output parameter, construct the TypedInterface object

    For now the fancy object, maybe in the future a dumb object.

    """

    type_hints = get_type_hints(fn, include_extras=True)
    signature = inspect.signature(fn)
    return_annotation = type_hints.get("return", None)

    outputs = extract_return_annotation(return_annotation)
    for k, v in outputs.items():
        outputs[k] = _change_unrecognized_type_to_pickle(v)
    inputs = OrderedDict()
    for k, v in signature.parameters.items():
        annotation = type_hints.get(k, None)
        default = v.default if v.default is not inspect.Parameter.empty else None
        # Inputs with default values are currently ignored, we may want to look into that in the future
        inputs[k] = (_change_unrecognized_type_to_pickle(annotation), default)

    # This is just for typing.NamedTuples - in those cases, the user can select a name to call the NamedTuple. We
    # would like to preserve that name in our custom collections.namedtuple.
    custom_name = None
    if hasattr(return_annotation, "__bases__"):
        bases = return_annotation.__bases__
        if len(bases) == 1 and bases[0] == tuple and hasattr(
                return_annotation, "_fields"):
            if hasattr(return_annotation,
                       "__name__") and return_annotation.__name__ != "":
                custom_name = return_annotation.__name__

    return Interface(inputs,
                     outputs,
                     output_tuple_name=custom_name,
                     docstring=docstring)
Пример #19
0
def generate_function_stub(func) -> Tuple[Set[str], str]:
    """Generate a stub and imports for a function."""
    sig = inspect.signature(func)

    globalns = {**getattr(func, '__globals__', {})}
    globalns.update(vars(typing))
    globalns.update(getattr(func, '_forwardrefns_', {}))

    hints = get_type_hints(func, globalns)
    sig = sig.replace(
        parameters=[
            p.replace(annotation=hints.get(p.name, p.empty))
            for p in sig.parameters.values()
        ],
        return_annotation=hints.get('return', inspect.Parameter.empty),
    )
    imports = set()
    for hint in hints.values():
        imports.update(set(_iter_imports(hint)))
    imports -= {'typing'}

    doc = f'"""{func.__doc__}"""' if func.__doc__ else '...'
    return imports, f'def {func.__name__}{sig}:\n    {doc}\n'
Пример #20
0
def processor(func: C) -> C:
    """Decorator that declares `func` as a processor of its first parameter type.

    Examples
    --------
    >>> @processor
    >>> def processes_image(image: napari.layers.Image):
    ...     ... # do something with the image
    """
    hints = get_type_hints(func)
    hints.pop("return", None)
    if not hints:
        raise TypeError(
            f"{func} has no argument type hints. Cannot be a processor.")
    hint0 = list(hints.values())[0]

    if hint0 is not None:
        if get_origin(hint0) == Union:
            for arg in get_args(hint0):
                if arg is not None:
                    _PROCESSORS[arg] = func
        else:
            _PROCESSORS[hint0] = func
    return func
Пример #21
0
def generate_class_stubs(cls: Type) -> Tuple[Set[str], str]:
    """Generate a stub and imports for a class."""
    bases = ", ".join(f'{b.__module__}.{b.__name__}' for b in cls.__bases__)

    methods = []
    attrs = []
    imports = set()

    local_names = set(cls.__dict__).union(set(cls.__annotations__))
    for sup in cls.mro()[1:]:
        local_names.difference_update(set(sup.__dict__))

    for methname in sorted(_get_subclass_methods(cls)):
        method = getattr(cls, methname)
        if not callable(method):
            continue
        _imports, stub = generate_function_stub(method)
        imports.update(_imports)
        methods.append(stub)
    hints = get_type_hints(cls)
    for name, type_ in hints.items():
        if name not in local_names:
            continue
        if hasattr(type_, '__name__'):
            hint = f'{type_.__module__}.{type_.__name__}'
        else:
            hint = repr(type_).replace('typing.', '')
        attrs.append(f'{name}: {hint.replace("builtins.", "")}')
        imports.update(set(_iter_imports(type_)))

    doc = f'"""{cls.__doc__.lstrip()}"""' if cls.__doc__ else '...'
    stub = f'class {cls.__name__}({bases}):\n    {doc}\n'
    stub += textwrap.indent("\n".join(attrs), '    ')
    stub += "\n" + textwrap.indent("\n".join(methods), '    ')

    return imports, stub
Пример #22
0
def extract_return_annotation(
        return_annotation: Union[Type, Tuple, None]) -> Dict[str, Type]:
    """
    The purpose of this function is to sort out whether a function is returning one thing, or multiple things, and to
    name the outputs accordingly, either by using our default name function, or from a typing.NamedTuple.

        # Option 1
        nt1 = typing.NamedTuple("NT1", x_str=str, y_int=int)
        def t(a: int, b: str) -> nt1: ...

        # Option 2
        def t(a: int, b: str) -> typing.NamedTuple("NT1", x_str=str, y_int=int): ...

        # Option 3
        def t(a: int, b: str) -> typing.Tuple[int, str]: ...

        # Option 4
        def t(a: int, b: str) -> (int, str): ...

        # Option 5
        def t(a: int, b: str) -> str: ...

        # Option 6
        def t(a: int, b: str) -> None: ...

        # Options 7/8
        def t(a: int, b: str) -> List[int]: ...
        def t(a: int, b: str) -> Dict[str, int]: ...

    Note that Options 1 and 2 are identical, just syntactic sugar. In the NamedTuple case, we'll use the names in the
    definition. In all other cases, we'll automatically generate output names, indexed starting at 0.
    """

    # Handle Option 6
    # We can think about whether we should add a default output name with type None in the future.
    if return_annotation in (None, type(None), inspect.Signature.empty):
        return {}

    # This statement results in true for typing.Namedtuple, single and void return types, so this
    # handles Options 1, 2. Even though NamedTuple for us is multi-valued, it's a single value for Python
    if isinstance(return_annotation, Type) or isinstance(
            return_annotation, TypeVar):
        # isinstance / issubclass does not work for Namedtuple.
        # Options 1 and 2
        bases = return_annotation.__bases__  # type: ignore
        if len(bases) == 1 and bases[0] == tuple and hasattr(
                return_annotation, "_fields"):
            logger.debug(f"Task returns named tuple {return_annotation}")
            return dict(get_type_hints(return_annotation, include_extras=True))

    if hasattr(return_annotation, "__origin__"
               ) and return_annotation.__origin__ is tuple:  # type: ignore
        # Handle option 3
        logger.debug(f"Task returns unnamed typing.Tuple {return_annotation}")
        if len(return_annotation.__args__) == 1:  # type: ignore
            raise FlyteValidationException(
                "Tuples should be used to indicate multiple return values, found only one return variable."
            )
        return OrderedDict(
            zip(list(output_name_generator(len(return_annotation.__args__))),
                return_annotation.__args__)  # type: ignore
        )
    elif isinstance(return_annotation, tuple):
        if len(return_annotation) == 1:
            raise FlyteValidationException(
                "Please don't use a tuple if you're just returning one thing.")
        return OrderedDict(
            zip(list(output_name_generator(len(return_annotation))),
                return_annotation))

    else:
        # Handle all other single return types
        logger.debug(f"Task returns unnamed native tuple {return_annotation}")
        return {default_output_name(): return_annotation}
Пример #23
0
def unannotate(hint: Any) -> Any:
    """Recursively remove Annotated type hints."""
    class Temp:
        __annotations__ = dict(hint=hint)

    return get_type_hints(Temp)["hint"]
Пример #24
0
    def match(self, pattern):
        class _Temporary:
            p: pattern

        parsed_pattern = get_type_hints(_Temporary,
                                        localns={'pattern': pattern},
                                        include_extras=True)['p']
        if get_origin(parsed_pattern) is Annotated:
            parsed_pattern, recursive = get_args(parsed_pattern)
        else:
            recursive = False

        annotations = get_type_hints(parsed_pattern, include_extras=True)
        for name, expected in annotations.items():
            #print(f'{expected=} {self[name]=}')
            expected_any = expected is Any
            got_something = self[name] is not None
            if expected_any and not got_something:
                break

            expected_tuple = get_origin(expected) is tuple
            got_something = self[name] is not None
            if expected_tuple and not got_something:
                break
            elif expected_tuple and got_something:
                exp_timestamp, exp_value = get_args(expected)
                got_timestamp, got_value = self[name]

                #print(f'{exp_timestamp=} {exp_value=}')
                #print(f'{got_timestamp=} {got_value=}')

                expected_any = exp_timestamp is Any
                got_something = got_timestamp is not None
                if expected_any and not got_something:
                    break

                expected_any = exp_value is Any
                got_something = got_value is not None
                if expected_any and not got_something:
                    break

                if get_origin(exp_value) is Literal:
                    exp_value = get_args(exp_value)[0]
                    if exp_value != got_value:
                        break

            expected_list = get_origin(expected) is list
            got_something = self[name] is not None
            if expected_list and not got_something:
                break
            elif expected_list and got_something:
                exp_class = get_args(expected)[0]

                for tree in self.children:
                    if next(tree.match(exp_class), None) is not None:
                        break

        else:
            yield self
            return

        if recursive:
            for tree in self.children:
                yield from tree.match(pattern)
Пример #25
0
    def __init__(
        self,
        tp: type,
        identifier: Union[str, Identifier] = None,
    ):
        """Creates a type"""
        from .objects import Config, TypeConfig

        # Task related attributes
        self.taskcommandfactory = None
        self.task = None
        self._title = None

        # Get the identifier
        if identifier is None and hasattr(tp, "__xpmid__"):
            __xpmid__ = getattr(tp, "__xpmid__")
            if inspect.ismethod(__xpmid__):
                identifier = Identifier(__xpmid__())
            elif "__xpmid__" in tp.__dict__:
                identifier = Identifier(__xpmid__)

        package = tp.__module__.lower()
        name = tp.__qualname__.lower()

        if identifier is None:
            qname = f"{package}.{name}"
            assert (getattr(sys, "_called_from_test", False)
                    or "<locals>" not in qname
                    ), "Configurations should not be within functions"
            identifier = Identifier(qname)

        super().__init__(identifier, None)

        # --- Creates the config type and not config type

        self.originaltype = tp
        if not issubclass(tp, Config):
            # Adds Config as a base class if not present
            __bases__ = () if tp.__bases__ == (object, ) else tp.__bases__
            __dict__ = dict(tp.__dict__)

            __dict__ = {
                key: value
                for key, value in tp.__dict__.items()
                if key not in ObjectType.FORBIDDEN_KEYS
            }
            self.basetype = type(tp.__name__, (Config, ) + __bases__, __dict__)
            self.basetype.__module__ = tp.__module__
            self.basetype.__qualname__ = tp.__qualname__
        else:
            self.basetype = tp

        # Create the type-specific configuration class
        __configbases__ = tuple(
            s.__getxpmtype__().configtype for s in tp.__bases__
            if issubclass(s, Config) and (s is not Config)) or (TypeConfig, )

        self.configtype = type("TypeConfig",
                               __configbases__ + (self.basetype, ), {})
        self.configtype.__qualname__ = f"{self.basetype.__qualname__}.TypeConfig"
        self.configtype.__module__ = tp.__module__

        # Create the type-specific object class
        # (now, the same as basetype - TODO: remove references)
        self.objecttype = self.basetype  # type: type
        self.basetype._ = self.configtype

        # Return type is used by tasks to change the output
        if hasattr(self.basetype, "taskoutputs") or False:
            self.returntype = get_type_hints(
                getattr(self.basetype,
                        "taskoutputs")).get("return", typing.Any)
        else:
            self.returntype = self.objecttype

        # Registers ourselves
        self.basetype.__xpmtype__ = self
        self.configtype.__xpmtype__ = self

        # Other initializations
        self.__initialized__ = False
        self._runtype = None
        self.annotations = []
        self._deprecated = False
Пример #26
0
from typing_extensions import Annotated


class Description:
    def __init__(self, description: str) -> None:
        self.description = description


def hello(*, name: Annotated[str, Description("the name of person")]) -> None:
    print(f"hello {name}")


if __name__ == "__main__":
    from typing_extensions import get_type_hints

    print(get_type_hints(hello))
    # {'name': <class 'str'>, 'return': <class 'NoneType'>}

    print(get_type_hints(hello, include_extras=True))
    # {'name': typing_extensions.Annotated[str, <__main__.Description object at 0x10cd1e730>], 'return': <class 'NoneType'>}

    from typing_extensions import get_args, get_origin

    hints = get_type_hints(hello, include_extras=True)
    print(get_args(hints["name"]))
    # (<class 'str'>, <__main__.Description object at 0x106427730>)

    print(get_origin(hints["name"]))
    # <class 'typing_extensions.Annotated'>
Пример #27
0
from typing_extensions import Annotated


class Description:
    def __init__(self, description: str) -> None:
        self.description = description


def hello(*, name: Annotated[str, Description("the name of person")]) -> None:
    print(f"hello {name}")


if __name__ == "__main__":
    from typing_extensions import get_type_hints

    hints = get_type_hints(hello, include_extras=True)

    import typing_inspect

    print(typing_inspect.get_args(hints["name"]))
    # (<class 'str'>,)

    print(hasattr(hints["name"], "__metadata__"))
    # True
    print(hints["name"].__metadata__)
    # (<__main__.Description object at 0x104b9b730>,)
Пример #28
0
from typing import ClassVar, Optional, TypeVar, Type, Union

from typing_extensions import Annotated, get_type_hints


class Param:
    def __init__(self, help: str = None):
        self.help = help


class A:
    x: Annotated[int, Param(help="help")] = 2

    def a(self):
        return self.x + 1


print(get_type_hints(A, include_extras=True))

a = A()
x = a.a()
print(x)