예제 #1
0
    def callback(self, function: CallbackType[P]) -> None:
        if not inspect.iscoroutinefunction(function):
            raise TypeError(
                f"The callback for the command {function.__name__!r} must be a coroutine function."
            )

        function = function.__func__ if inspect.ismethod(
            function) else function  # HelpCommand.command_callback

        annotations = get_type_hints(function)
        for name, annotation in annotations.items():
            if get_origin(annotation) is converters.Greedy and isinstance(
                    annotation.converter, ForwardRef):
                annotations[name] = converters.Greedy[eval(
                    annotation.converter.__forward_code__,
                    function.__globals__)]

        function.__annotations__ = annotations

        self.params: dict[str, inspect.Parameter] = dict(
            inspect.signature(function).parameters)
        if not self.params:
            raise ClientException(
                f'Callback for {self.name} command is missing a "ctx" parameter.'
            ) from None

        self.module = function.__module__
        self._callback = function
예제 #2
0
    def attr___annotations__(self):
        obj = node_classes.Dict(parent=self._instance)

        if not self._instance.returns:
            returns = None
        else:
            returns = self._instance.returns

        args = self._instance.args
        pair_annotations = itertools.chain(
            zip(args.args or [], args.annotations),
            zip(args.kwonlyargs, args.kwonlyargs_annotations),
            zip(args.posonlyargs or [], args.posonlyargs_annotations),
        )

        annotations = {
            arg.name: annotation for (arg, annotation) in pair_annotations if annotation
        }
        if args.varargannotation:
            annotations[args.vararg] = args.varargannotation
        if args.kwargannotation:
            annotations[args.kwarg] = args.kwargannotation
        if returns:
            annotations["return"] = returns

        items = [
            (node_classes.Const(key, parent=obj), value)
            for (key, value) in annotations.items()
        ]

        obj.postinit(items)
        return obj
예제 #3
0
    def __dask_distributed_annotations_pack__(
        self,
        annotations: Mapping[str, Any] | None = None
    ) -> Mapping[str, Any] | None:
        """Packs Layer annotations for transmission to scheduler

        Callables annotations are fully expanded over Layer keys, while
        other values are simply transmitted as is

        Parameters
        ----------
        annotations : Mapping[str, Any], optional
            A top-level annotations.

        Returns
        -------
        packed_annotations : dict
            Packed annotations.
        """
        annotations = cast(
            "dict[str, Any]",
            toolz.merge(self.annotations or {}, annotations or {}))
        packed = {}
        for a, v in annotations.items():
            if callable(v):
                packed[a] = {stringify(k): v(k) for k in self}
                packed[a]["__expanded_annotations__"] = True
            else:
                packed[a] = v
        return packed
예제 #4
0
def _build_json_api(apiclass: t.Type[JsonApiData],
                    data: t.Dict[str, t.Any]) -> JsonApiData:
    instance = apiclass.__new__(apiclass)
    annotations = t.get_type_hints(apiclass)

    # NOTE
    #   Ignore keys of data which is not defined in the apiclass.
    for key_def, type_def in annotations.items():

        # Handle default values.
        if not (key_def in data or hasattr(apiclass, key_def)):
            raise ApiValidationFailedError(
                f"Raw data has no key named '{key_def}'.")
        if key_def in data:
            val = data.get(key_def)
        else:
            val = getattr(apiclass, key_def)

        # Validate types of values.
        if not _validate_obj(val, type_def):
            raise ApiValidationFailedError(
                "Invalid type was detected in received json data. "
                f"Expected: {type_def.__name__}; "
                f"Detected: {val.__class__.__name__}.")

        # Set values of attributes to an instance of the apiclass.
        # Handle only the case in which the origin is t.Optional.
        origin = get_origin(type_def)
        if origin == t.Union:
            type_def = get_args(type_def)[0]
        _set_non_optional_value(instance, type_def, key_def, val)

    return instance
예제 #5
0
def parse_annotations(
        annotations: Optional[Mapping[LabelName, str]]) -> api.Annotations:
    """Select annotations, if they are valid.

    Kubernetes allows the annotations to be arbitrary byte strings with a
    length of at most 256Kb. The python client will try to decode these with
    utf8, but appears to return raw data if an exception occurs. We have not
    tested whether this will happen. The current commit, when this information
    was obtained, was
    https://github.com/kubernetes/kubernetes/commit/a83cc51a19d1b5f2b2d3fb75574b04f587ec0054

    Since not every annotation can be converted to a HostLabel, we decided to
    only use annotations, which are also valid Kubernetes labels. Kubernetes
    makes sure that the annotation has a valid name, so we only verify, that
    the key is also valid as a label.

    >>> parse_annotations(None)  # no annotation specified for the object
    {}
    >>> parse_annotations({
    ... '1': '',
    ... '2': 'a-',
    ... '3': 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
    ... '4': 'a&a',
    ... '5': 'valid-key',
    ... })
    {'1': '', '5': 'valid-key'}
    """
    if annotations is None:
        return {}
    return {
        k: LabelValue(v)
        for k, v in annotations.items() if _is_valid_label_value(v)
    }
예제 #6
0
    def arguments(self) -> List[StrawberryArgument]:
        # TODO: Move to StrawberryArgument? StrawberryResolver ClassVar?
        SPECIAL_ARGS = {"root", "self", "info"}

        annotations = self.wrapped_func.__annotations__
        parameters = inspect.signature(self.wrapped_func).parameters
        function_arguments = set(parameters) - SPECIAL_ARGS

        annotations = {
            name: annotation
            for name, annotation in annotations.items()
            if name not in (SPECIAL_ARGS | {"return"})
        }

        annotated_arguments = set(annotations)
        arguments_missing_annotations = function_arguments - annotated_arguments

        if any(arguments_missing_annotations):
            raise MissingArgumentsAnnotationsError(
                field_name=self.wrapped_func.__name__,
                arguments=arguments_missing_annotations,
            )

        module = sys.modules[self.wrapped_func.__module__]
        annotation_namespace = module.__dict__
        arguments = []
        for arg_name, annotation in annotations.items():
            parameter = parameters[arg_name]

            argument = StrawberryArgument(
                python_name=arg_name,
                graphql_name=None,
                type_annotation=StrawberryAnnotation(
                    annotation=annotation, namespace=annotation_namespace),
                default=parameter.default,
            )

            arguments.append(argument)

        return arguments
예제 #7
0
    def __bytes__(self) -> bytes:
        try:
            annotations = self.__annotations__
        except AttributeError:
            annotations = {}
        with StructIO() as io:
            for key, annotation in annotations.items():
                if annotation == "int":
                    io.write_u64(getattr(self, key))
                elif annotation == "bool":
                    io.write_u8(getattr(self, key))

            return io.buffer
예제 #8
0
def fix_annotations(
    cls: typing.Type,
    clsdict: Vars,
    global_vars: Vars,
    local_vars: Vars,
) -> None:
    """Fix any forward references to variables defined in the callee scope."""

    # Don't care except when enforcing types.
    if typing.TYPE_CHECKING:
        annotations = clsdict['__annotations__']
        for field, type in annotations.items():
            type = fix_annotation(type, global_vars, local_vars)
            annotations[field] = type
예제 #9
0
def set_slots(
    cls: typing.Type,
    clsdict: Vars,
    slots: bool,
    global_vars: Vars,
    local_vars: Vars,
) -> None:
    """Set default __slots__ implementation."""

    if not slots or '__slots__' in clsdict:
        return
    annotations = clsdict['__annotations__']
    is_cv = lambda x: is_classvar(x, global_vars, local_vars)
    slots = (k for k, v in annotations.items() if not is_cv(v))
    clsdict['__slots__'] = tuple(slots)
예제 #10
0
    def arguments(self) -> List[StrawberryArgument]:
        annotations = self.resolver.__annotations__
        annotations = dict(islice(annotations.items(), 1, None))
        annotations.pop("return", None)

        parameters = inspect.signature(self.resolver).parameters

        module = sys.modules[self.resolver.__module__]
        annotation_namespace = module.__dict__
        arguments = []
        for arg_name, annotation in annotations.items():
            parameter = parameters[arg_name]

            argument = StrawberryArgument(
                python_name=arg_name,
                graphql_name=None,
                type_annotation=StrawberryAnnotation(
                    annotation=annotation, namespace=annotation_namespace),
                default=parameter.default,
            )

            arguments.append(argument)

        return arguments
예제 #11
0
 def __new__(cls, name, bases, namespace, **kwargs):  # noqa C901
     annotations = resolve_annotations(namespace.get("__annotations__", {}),
                                       namespace.get("__module__", None))
     for field, annotation in annotations.items():
         if get_origin(annotation) is not Annotated:
             continue
         namespace[field] = FieldAnnotation.create_field_info(
             annotation=annotation, value=namespace.get(field, Undefined))
         # Pydantic doesn't yet support Annotated annotations [1], so we'll unwrap the root type.
         # This prevents later inspection of the annotations with `get_type_hints`, so we'll
         # preferably avoid.
         #
         # 1: https://github.com/samuelcolvin/pydantic/pull/2147
         if not _pydantic_is_annotated_aware:
             namespace["__annotations__"][field] = get_args(annotation)[0]
     return super().__new__(cls, name, bases, namespace, **kwargs)
예제 #12
0
def resolve_annotations(
    annotations: dict[str, Any],
    module: Optional[ModuleType],
    fullname: str,
) -> dict[str, Any]:
    """
    Given an `annotations` dictionary with type annotations (for example, `cls.__annotations__`),
    this function tries to resolve all types using `pdoc.doc_types.safe_eval_type`.

    Returns: A dictionary with the evaluated types.
    """
    ns = getattr(module, "__dict__", {})

    resolved = {}
    for name, value in annotations.items():
        resolved[name] = safe_eval_type(value, ns, fullname)

    return resolved
예제 #13
0
    def _get_method_arguments(method: nodes.FunctionDef) -> list[str]:
        if method.args.args is None:
            return []

        first_arg = 0 if method.type in {"function", "staticmethod"} else 1
        arguments: list[nodes.AssignName] = method.args.args[first_arg:]

        annotations = dict(zip(arguments, method.args.annotations[first_arg:]))
        for arg in arguments:
            annotation_label = ""
            ann = annotations.get(arg)
            if ann:
                annotation_label = get_annotation_label(ann)
            annotations[arg] = annotation_label

        return [
            f"{arg.name}: {ann}" if ann else f"{arg.name}"
            for arg, ann in annotations.items()
        ]
예제 #14
0
    def _get_fields(cls) -> list[StructField]:
        fields = ListSubclass()

        # We need both to throw type errors in case a field is not annotated
        annotations = typing.get_type_hints(cls._real_cls())

        # Make sure every `StructField` is annotated
        for name in vars(cls._real_cls()):
            value = getattr(cls, name)

            if isinstance(value, StructField) and name not in annotations:
                raise TypeError(
                    f"Field {name!r}={value} must have some annotation."
                    f" Use `None` if it is specified in the `StructField`.")

        # XXX: Python doesn't provide a simple way to get all defined attributes *and*
        #      order them with respect to annotation-only fields.
        #      Every struct field must be annotated.
        for name, annotation in annotations.items():
            field = getattr(cls, name, StructField())

            if not isinstance(field, StructField):
                continue

            field = field.replace(name=name)

            # An annotation of `None` means to use the field's type
            if annotation is not NoneType:
                if field.type is not None and field.type != annotation:
                    raise TypeError(
                        f"Field {name!r} type annotation conflicts with provided type:"
                        f" {annotation} != {field.type}")

                field = field.replace(type=annotation)
            elif field.type is None:
                raise TypeError(f"Field {name!r} has no type")

            fields.append(field)
            setattr(fields, field.name, field)

        return fields
예제 #15
0
    def invoker():
        args = {}

        for param, notes in annotations.items():
            if param == "return":
                continue

            adapt = notes.get("adapt", (lambda x: x))
            color = COLORS.get(notes.get("color", "standard"), RESET)

            while param not in args:
                try:
                    args[param] = adapt(input(f"{color}{param}{RESET}: "))
                except AdaptionRetry:
                    pass

        result = func(**args)

        if annotations["return"].get("print", False):
            print(result)

        return result
예제 #16
0
    def __new__(
        cls, name: str, bases: typing.Tuple[type, ...], namespace: typing.Dict[str, typing.Any]
    ) -> type:
        model_fields: typing.Dict[str, ModelField] = {}

        slots: typing.Set[str] = set()
        for field in namespace['__annotations__']:
            if not field.startswith('__') and not field.endswith('__'):
                slots.add(field)

        if '__slots__' in namespace:
            slots.update(namespace['__slots__'])

        namespace['__slots__'] = tuple(slots)
        namespace['__model_fields__'] = model_fields

        self = type.__new__(cls, name, bases, namespace)

        annotations = typing.get_type_hints(self)
        for field, annotation in annotations.items():
            if not field.startswith('__') and not field.endswith('__'):
                model_fields[field] = ModelField(field, annotation)

        return self
예제 #17
0
def get_arguments_from_annotations(annotations: Any,
                                   parameters: Mapping[str, inspect.Parameter],
                                   origin: Any) -> List[StrawberryArgument]:

    # Deferred to prevent import cycles
    from .types.type_resolver import _resolve_type

    arguments = []

    for name, annotation in annotations.items():
        default_value = parameters[name].default
        default_value = (undefined if default_value is inspect.Parameter.empty
                         or is_unset(default_value) else default_value)

        if get_origin(annotation) is Annotated:
            argument = StrawberryArgument.from_annotated(
                python_name=name,
                annotation=annotation,
                default_value=default_value,
                origin=origin,
            )
        else:
            argument = StrawberryArgument(
                type_=annotation,
                python_name=name,
                graphql_name=None,
                default_value=default_value,
                description=None,
                origin=origin,
            )

        _resolve_type(argument)

        arguments.append(argument)

    return arguments
예제 #18
0
def get_flags(namespace: Dict[str, Any], globals: Dict[str, Any], locals: Dict[str, Any]) -> Dict[str, Flag]:
    annotations = namespace.get('__annotations__', {})
    case_insensitive = namespace['__commands_flag_case_insensitive__']
    flags: Dict[str, Flag] = {}
    cache: Dict[str, Any] = {}
    names: Set[str] = set()
    for name, annotation in annotations.items():
        flag = namespace.pop(name, MISSING)
        if isinstance(flag, Flag):
            flag.annotation = annotation
        else:
            flag = Flag(name=name, annotation=annotation, default=flag)

        flag.attribute = name
        if flag.name is MISSING:
            flag.name = name

        annotation = flag.annotation = resolve_annotation(flag.annotation, globals, locals, cache)

        if flag.default is MISSING and hasattr(annotation, '__commands_is_flag__') and annotation._can_be_constructible():
            flag.default = annotation._construct_default

        if flag.aliases is MISSING:
            flag.aliases = []

        # Add sensible defaults based off of the type annotation
        # <type> -> (max_args=1)
        # List[str] -> (max_args=-1)
        # Tuple[int, ...] -> (max_args=1)
        # Dict[K, V] -> (max_args=-1, override=True)
        # Union[str, int] -> (max_args=1)
        # Optional[str] -> (default=None, max_args=1)

        try:
            origin = annotation.__origin__
        except AttributeError:
            # A regular type hint
            if flag.max_args is MISSING:
                flag.max_args = 1
        else:
            if origin is Union:
                # typing.Union
                if flag.max_args is MISSING:
                    flag.max_args = 1
                if annotation.__args__[-1] is type(None) and flag.default is MISSING:
                    # typing.Optional
                    flag.default = None
            elif origin is tuple:
                # typing.Tuple
                # tuple parsing is e.g. `flag: peter 20`
                # for Tuple[str, int] would give you flag: ('peter', 20)
                if flag.max_args is MISSING:
                    flag.max_args = 1
            elif origin is list:
                # typing.List
                if flag.max_args is MISSING:
                    flag.max_args = -1
            elif origin is dict:
                # typing.Dict[K, V]
                # Equivalent to:
                # typing.List[typing.Tuple[K, V]]
                flag.cast_to_dict = True
                if flag.max_args is MISSING:
                    flag.max_args = -1
                if flag.override is MISSING:
                    flag.override = True
            elif origin is Literal:
                if flag.max_args is MISSING:
                    flag.max_args = 1
            else:
                raise TypeError(f'Unsupported typing annotation {annotation!r} for {flag.name!r} flag')

        if flag.override is MISSING:
            flag.override = False

        # Validate flag names are unique
        name = flag.name.casefold() if case_insensitive else flag.name
        if name in names:
            raise TypeError(f'{flag.name!r} flag conflicts with previous flag or alias.')
        else:
            names.add(name)

        for alias in flag.aliases:
            # Validate alias is unique
            alias = alias.casefold() if case_insensitive else alias
            if alias in names:
                raise TypeError(f'{flag.name!r} flag alias {alias!r} conflicts with previous flag or alias.')
            else:
                names.add(alias)

        flags[flag.name] = flag

    return flags
예제 #19
0
파일: base.py 프로젝트: jakirkham/dask
def annotate(**annotations):
    """Context Manager for setting HighLevelGraph Layer annotations.

    Annotations are metadata or soft constraints associated with
    tasks that dask schedulers may choose to respect: They signal intent
    without enforcing hard constraints. As such, they are
    primarily designed for use with the distributed scheduler.

    Almost any object can serve as an annotation, but small Python objects
    are preferred, while large objects such as NumPy arrays are discouraged.

    Callables supplied as an annotation should take a single *key* argument and
    produce the appropriate annotation. Individual task keys in the annotated collection
    are supplied to the callable.

    Parameters
    ----------
    **annotations : key-value pairs

    Examples
    --------

    All tasks within array A should have priority 100 and be retried 3 times
    on failure.

    >>> import dask
    >>> import dask.array as da
    >>> with dask.annotate(priority=100, retries=3):
    ...     A = da.ones((10000, 10000))

    Prioritise tasks within Array A on flattened block ID.

    >>> nblocks = (10, 10)
    >>> with dask.annotate(priority=lambda k: k[1]*nblocks[1] + k[2]):
    ...     A = da.ones((1000, 1000), chunks=(100, 100))

    Annotations may be nested.

    >>> with dask.annotate(priority=1):
    ...     with dask.annotate(retries=3):
    ...         A = da.ones((1000, 1000))
    ...     B = A + 1
    """

    # Sanity check annotations used in place of
    # legacy distributed Client.{submit, persist, compute} keywords
    if "workers" in annotations:
        if isinstance(annotations["workers"], (list, set, tuple)):
            annotations["workers"] = list(annotations["workers"])
        elif isinstance(annotations["workers"], str):
            annotations["workers"] = [annotations["workers"]]
        elif callable(annotations["workers"]):
            pass
        else:
            raise TypeError(
                "'workers' annotation must be a sequence of str, a str or a callable, but got %s."
                % annotations["workers"]
            )

    if (
        "priority" in annotations
        and not isinstance(annotations["priority"], Number)
        and not callable(annotations["priority"])
    ):
        raise TypeError(
            "'priority' annotation must be a Number or a callable, but got %s"
            % annotations["priority"]
        )

    if (
        "retries" in annotations
        and not isinstance(annotations["retries"], Number)
        and not callable(annotations["retries"])
    ):
        raise TypeError(
            "'retries' annotation must be a Number or a callable, but got %s"
            % annotations["retries"]
        )

    if (
        "resources" in annotations
        and not isinstance(annotations["resources"], dict)
        and not callable(annotations["resources"])
    ):
        raise TypeError(
            "'resources' annotation must be a dict, but got %s"
            % annotations["resources"]
        )

    if (
        "allow_other_workers" in annotations
        and not isinstance(annotations["allow_other_workers"], bool)
        and not callable(annotations["allow_other_workers"])
    ):
        raise TypeError(
            "'allow_other_workers' annotations must be a bool or a callable, but got %s"
            % annotations["allow_other_workers"]
        )

    prev_annotations = config.get("annotations", {})
    new_annotations = {
        **prev_annotations,
        **{f"annotations.{k}": v for k, v in annotations.items()},
    }

    with config.set(new_annotations):
        yield
예제 #20
0
                mro = obj.mro()
            except TypeError:
                return True

            # We have to traverse the class's mro to find annotations since __annotations__
            # doesn't include inherited attributes in-order to make resolving said annotations
            # possible (as you need to know the scope they were defined in).
            for mro_cls in mro:  # obj.mro() includes the class itself at the start.
                try:
                    # Some classes like object just don't have annotations cause thx python.
                    annotations = mro_cls.__annotations__
                except AttributeError:
                    continue

                for name, annotation in filter(_is_public_key,
                                               annotations.items()):
                    if name not in found_attributes:
                        self._handle_annotation(mro_cls.__module__,
                                                f"{path}.{name}", annotation)
                        found_attributes.add(name)

            return True

        return False

    def index_module(self: _ReferenceIndexT,
                     module: types.ModuleType,
                     /,
                     *,
                     recursive: bool = False) -> _ReferenceIndexT:
        """Add a module to the internal index of in-scope modules.
예제 #21
0
    def add_module(self,
                   module: types.ModuleType,
                   cython: bool = False) -> None:
        """Add the given module, its members, and their submembers.

        The first examples are based on the site-package |numpy|: which is passed to
        method |Substituter.add_module|:

        >>> from hydpy.core.autodoctools import Substituter
        >>> substituter = Substituter()
        >>> import numpy
        >>> substituter.add_module(numpy)

        First, the module itself is added:

        >>> substituter.find("|numpy|")
        |numpy| :mod:`~numpy`

        Second, constants like |numpy.nan| are added:

        >>> substituter.find("|numpy.nan|")
        |numpy.nan| :const:`~numpy.nan`

        Third, functions like |numpy.clip| are added:

        >>> substituter.find("|numpy.clip|")
        |numpy.clip| :func:`~numpy.clip`

        Fourth, clases line |numpy.ndarray| are added:

        >>> substituter.find("|numpy.ndarray|")
        |numpy.ndarray| :class:`~numpy.ndarray`

        Method |Substituter.add_module| also searches for available annotations:

        >>> from hydpy.core import timetools
        >>> substituter.add_module(timetools)
        >>> substituter.find("Timegrids.init")
        |Timegrids.initindices| :const:`~hydpy.core.timetools.Timegrids.initindices`
        |Timegrids.init| :attr:`~hydpy.core.timetools.Timegrids.init`
        |timetools.Timegrids.initindices| \
:const:`~hydpy.core.timetools.Timegrids.initindices`
        |timetools.Timegrids.init| :attr:`~hydpy.core.timetools.Timegrids.init`

        >>> from hydpy.auxs import calibtools
        >>> substituter.add_module(calibtools)
        >>> substituter.find("RuleIUH.update_parameters")
        |RuleIUH.update_parameters| \
:attr:`~hydpy.auxs.calibtools.RuleIUH.update_parameters`
        |calibtools.RuleIUH.update_parameters| \
:attr:`~hydpy.auxs.calibtools.RuleIUH.update_parameters`

        Module |typingtools| is unique, as it is the only one for which
        |Substituter.add_module| considers all explicitly exported type aliases:

        >>> from hydpy.core import typingtools
        >>> substituter.add_module(typingtools)
        >>> substituter.find("|NDArrayFloat|")
        |NDArrayFloat| :class:`~hydpy.core.typingtools.NDArrayFloat`

        When adding Cython modules, the `cython` flag should be set |True|:

        >>> from hydpy.cythons import pointerutils
        >>> substituter.add_module(pointerutils, cython=True)
        >>> substituter.find("set_pointer")
        |PPDouble.set_pointer| \
:func:`~hydpy.cythons.autogen.pointerutils.PPDouble.set_pointer`
        |pointerutils.PPDouble.set_pointer| \
:func:`~hydpy.cythons.autogen.pointerutils.PPDouble.set_pointer`
        """
        name_module = module.__name__.split(".")[-1]
        short = f"|{name_module}|"
        long = f":mod:`~{module.__name__}`"
        self.short2long[short] = long
        for (name_member, member) in vars(module).items():
            if self.consider_member(name_member, member, module):
                role = self.get_role(member, cython)
                short = f"|{name_member}|"
                medium = f"|{name_module}.{name_member}|"
                long = f":{role}:`~{module.__name__}.{name_member}`"
                self.add_substitution(short, medium, long, module)
                if inspect.isclass(member):
                    annotations = getattr(member, "__annotations__", {})
                    for name_submember, submember in vars(member).items():
                        if self.consider_member(
                                name_member=name_submember,
                                member=submember,
                                module=module,
                                class_=member,
                                ignore=annotations,
                        ):
                            role = self.get_role(submember, cython)
                            short = f"|{name_member}.{name_submember}|"
                            medium = (f"|{name_module}.{name_member}."
                                      f"{name_submember}|")
                            long = (f":{role}:`~{module.__name__}."
                                    f"{name_member}.{name_submember}`")
                            self.add_substitution(short, medium, long, module)
                    for name_submember, submember in annotations.items():
                        short = f"|{name_member}.{name_submember}|"
                        medium = f"|{name_module}.{name_member}." f"{name_submember}|"
                        long = (f":attr:`~{module.__name__}."
                                f"{name_member}.{name_submember}`")
                        self.add_substitution(short, medium, long, module)
예제 #22
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)
예제 #23
0
파일: flags.py 프로젝트: igoham/discord.py
def get_flags(namespace: Dict[str, Any], globals: Dict[str, Any],
              locals: Dict[str, Any]) -> Dict[str, Flag]:
    annotations = namespace.get('__annotations__', {})
    flags: Dict[str, Flag] = {}
    cache: Dict[str, Any] = {}
    for name, annotation in annotations.items():
        flag = namespace.pop(name, MISSING)
        if isinstance(flag, Flag):
            flag.annotation = annotation
        else:
            flag = Flag(name=name, annotation=annotation, default=flag)

        flag.attribute = name
        if flag.name is MISSING:
            flag.name = name

        annotation = flag.annotation = resolve_annotation(
            flag.annotation, globals, locals, cache)

        # Add sensible defaults based off of the type annotation
        # <type> -> (max_args=1)
        # List[str] -> (max_args=-1)
        # Tuple[int, ...] -> (max_args=1)
        # Dict[K, V] -> (max_args=-1, override=True)
        # Optional[str] -> (default=None, max_args=1)

        try:
            origin = annotation.__origin__
        except AttributeError:
            # A regular type hint
            if flag.max_args is MISSING:
                flag.max_args = 1
        else:
            if origin is Union and annotation.__args__[-1] is type(None):
                # typing.Optional
                if flag.max_args is MISSING:
                    flag.max_args = 1
                if flag.default is MISSING:
                    flag.default = None
            elif origin is tuple:
                # typing.Tuple
                # tuple parsing is e.g. `flag: peter 20`
                # for Tuple[str, int] would give you flag: ('peter', 20)
                if flag.max_args is MISSING:
                    flag.max_args = 1
            elif origin is list:
                # typing.List
                if flag.max_args is MISSING:
                    flag.max_args = -1
            elif origin is dict:
                # typing.Dict[K, V]
                # Equivalent to:
                # typing.List[typing.Tuple[K, V]]
                flag.cast_to_dict = True
                if flag.max_args is MISSING:
                    flag.max_args = -1
                if flag.override is MISSING:
                    flag.override = True
            else:
                raise TypeError(
                    f'Unsupported typing annotation {annotation!r} for {flag.name!r} flag'
                )

        if flag.override is MISSING:
            flag.override = False

        flags[flag.name] = flag

    return flags