def is_leq_informative(left, right): if not (is_type(left) and is_type(right)): raise ValueError('Both parameters need to be types') if (get_origin(left) is Generic or get_origin(right) is Generic): raise ValueError("typing Generic not supported") if left is right: result = True elif left is Unknown: result = True elif right is Unknown: result = False elif right is Any: result = True elif left is Any: result = False elif is_union_type(right): result = is_leq_informative_union(left, right) elif is_union_type(left): result = False elif is_parameterized(right): result = is_leq_informative_parameterized_right(left, right) elif is_parametrical(right): if is_parameterized(left): left = get_origin(left) result = issubclass(left, right) elif is_parametrical(left) or is_parameterized(left): result = False elif left in type_order and right in type_order[left]: result = True else: result = issubclass(left, right) return result
def _is_instance_of_dict_type(_object: Any, required_dict_type: Type) -> bool: key_type, value_type = get_args(required_dict_type) if is_optional_type(key_type) or is_union_type(key_type): key_type = get_args(key_type) if is_optional_type(value_type) or is_union_type(value_type): value_type = get_args(value_type) return not (not isinstance(_object, dict) or any( not isinstance(k, key_type) for k in _object.keys()) or any(not isinstance(v, value_type) for v in _object.values()))
def __init__( self, dependency: Union[T, str], *, namespace: str = None, group: str = None, exclude_groups: Sequence[str] = None, lazy: bool = False, ): optional = False multiple = False if typing_inspect.is_optional_type(dependency): dependency = typing_inspect.get_args(dependency, evaluate=True)[0] optional = True elif typing_inspect.is_union_type(dependency): raise TypeError( f"Autowired Union can only be used to indicate" f" optional autowiring in the forms 'Union[T, None]' or" f" 'Optional[T]'") if is_sequence(typing_inspect.get_origin(dependency) or dependency): subscripted_types = typing_inspect.get_args(dependency, evaluate=True) if subscripted_types == typing_inspect.get_args(Sequence): raise TypeError(f"Type not defined for Autowired list") subscripted_type = subscripted_types[0] if typing_inspect.is_optional_type(subscripted_type): raise TypeError( f"List of Optional is invalid for autowiring. Use" f" 'Autowired(Optional[List[...]])' instead.") elif typing_inspect.is_union_type(subscripted_type): raise TypeError( f"Only one type should be defined for Autowired list") dependency = subscripted_type multiple = True elif is_raw_sequence(dependency): if len(dependency) != 1: raise TypeError( f"Only one type should be defined for Autowired" f" {dependency.__class__.__qualname__}") dependency = dependency[0] multiple = True self.optional = optional self.multiple = multiple self.dependency = sanitize_if_forward_ref(dependency) self.namespace = namespace self.group = group self.exclude_groups = exclude_groups self.lazy = lazy
def collapse_unions(obj1, obj2, union_rewriter=None): if not union_rewriter: union_rewriter = IdentityUnionRewriter() union = make_union([obj1, obj2]) union = union_rewriter.rewrite(union) if not typing_inspect.is_union_type(union): return union for obj in [obj1, obj2]: if typing_inspect.is_union_type(obj) and len(union.__args__) <= len( obj.__args__): return union return NOT_SIMPLIFIED
def _obtain_referred_type(type_) -> List[type]: """ Obtain the type referred by the type hint. Example.: class A() first: List[str] second: List[Foo] third: Union[Foo, Bar] fourth: List[Union[Foo, Bar]] fifth: Union[str, int] Running `_ObtainReferredType` on each field will return: first => [str] second => [Foo] third => [Foo, Bar] fourth => [Foo, Bar] fifth => [str, int] If type_ is not either a List or Union, an TypeError exception will be raised. """ if is_list(type_): type_ = typing_inspect.get_args(type_)[0] return (_obtain_referred_type(type_) if _is_from_typing_module(type_) else [type_]) if typing_inspect.is_union_type(type_): return [i for i in type_.__args__ if i is not None.__class__] if is_dict(type_): return type_.__args__[1] raise TypeError( f"type_ must be a List or Union referring other types, got {type_}")
def _parse_annotation(self, raw_annotation: Type) -> None: """Parse key information from annotation. :param annotation: A subscripted type. :returns: Annotation """ self.raw_annotation = raw_annotation self.origin = self.arg = None self.optional = typing_inspect.is_optional_type(raw_annotation) if self.optional and typing_inspect.is_union_type(raw_annotation): # Annotated with Optional or Union[..., NoneType] # get_args -> (pandera.typing.Index[str], <class 'NoneType'>) raw_annotation = typing_inspect.get_args(raw_annotation)[0] self.origin = typing_inspect.get_origin(raw_annotation) # Replace empty tuple returned from get_args by None args = typing_inspect.get_args(raw_annotation) or None self.arg = args[0] if args else args self.metadata = getattr(self.arg, "__metadata__", None) if self.metadata: self.arg = typing_inspect.get_args(self.arg)[0] self.literal = typing_inspect.is_literal_type(self.arg) if self.literal: self.arg = typing_inspect.get_args(self.arg)[0] self.default_dtype = getattr(raw_annotation, "default_dtype", None)
def _generate_field_schema(field_name: str, field: Field, schemas: Dict[str, Schema]) -> Tuple[bool, Schema]: is_optional, annotation = extract_optional_annotation(field.annotation) if is_schema(annotation): field_schema_name = _generate_schema(annotation, schemas) field_schema = Schema(ref=_make_ref_path(field_schema_name)) elif is_generic_type(annotation): origin = get_origin(annotation) if origin in _LIST_TYPES: arguments = get_args(annotation) if arguments and is_schema(arguments[0]): item_schema_name = _generate_schema(arguments[0], schemas) field_schema = Schema("array", items=_make_schema_ref(item_schema_name)) else: field_schema = _generate_primitive_schema(annotation) elif origin in _DICT_TYPES: # TODO: Add support for additionalFields. field_schema = _generate_primitive_schema(dict) else: # pragma: no cover raise ValueError( f"Unsupported type {origin} for field {field.name!r}.") elif is_union_type(annotation): sub_schemas = [] for arg in get_args(annotation): _, sub_schema = _generate_field_schema("", Field(annotation=arg), schemas) sub_schemas.append(dump_schema(sub_schema, sparse=True)) field_schema = Schema(any_of=sub_schemas) else: field_schema = _generate_primitive_schema(annotation) if field_schema is not None: field_schema.description = field.description if field.request_name != field.response_name: if field_name == field.request_name: field_schema.write_only = True else: field_schema.read_only = True elif field.response_only: field_schema.read_only = True elif field.request_only: field_schema.write_only = True for option, value in field.validator_options.items(): if option in Schema._FIELDS: setattr(field_schema, option, value) return is_optional, field_schema
async def generate_signature_error(self, ctx, error): command = ctx.command argument = "" found = False def check_converter(_error): if isinstance(_error, commands.BadArgument): frames = [*traceback.walk_tb(_error.__traceback__)] last_trace = frames[-1] frame = last_trace[0] converter = frame.f_locals["self"] return getattr( discord, converter.__class__.__name__.replace("Converter", "")) if _class := getattr(error, "converter", check_converter(error)): signature = inspect.signature(command.callback).parameters for typing in signature.values(): if typing_inspect.is_union_type(typing): checking = typing.annotation.__args__ else: checking = (typing.annotation, ) for convert in checking: if convert is _class: found = True argument = typing.name break
def _parse_annotation(self, raw_annotation: Type) -> None: """Parse key information from annotation. :param annotation: A subscripted type. :returns: Annotation """ self.raw_annotation = raw_annotation self.origin = self.arg = None self.optional = typing_inspect.is_optional_type(raw_annotation) if self.optional and typing_inspect.is_union_type(raw_annotation): # Annotated with Optional or Union[..., NoneType] if LEGACY_TYPING: # pragma: no cover # get_args -> ((pandera.typing.Index, <class 'str'>), <class 'NoneType'>) self.origin, self.arg = typing_inspect.get_args( raw_annotation)[0] # get_args -> (pandera.typing.Index[str], <class 'NoneType'>) raw_annotation = typing_inspect.get_args(raw_annotation)[0] if not (self.optional and LEGACY_TYPING): self.origin = typing_inspect.get_origin(raw_annotation) args = typing_inspect.get_args(raw_annotation) self.arg = args[0] if args else args self.metadata = getattr(self.arg, "__metadata__", None) if self.metadata: self.arg = typing_inspect.get_args(self.arg)[0] self.literal = typing_inspect.is_literal_type(self.arg) if self.literal: self.arg = typing_inspect.get_args(self.arg)[0]
def is_instance_of_type(_object: Any, required_type: Type) -> Optional[bool]: """ Check if `object` is of type `required_type`. Works same as `isinstance`, but also has limited support of typing generics (such as Union, Optional, etc). Works on small subset of types, can fail on complex types. See tests (tests/tests_unit/test_type_utils.py) for examples. """ type_to_check_first = required_type if is_optional_type(required_type) or is_union_type(required_type): type_to_check_first = get_args(required_type) try: return isinstance(_object, type_to_check_first) except TypeError: pass if is_optional_type(required_type): if _object is None: return True required_type = type_to_check_first[0] # content of Optional[...] type_to_checker_map = { list: _is_instance_of_list_type, dict: _is_instance_of_dict_type, Mapping: _is_instance_of_dict_type, } if get_origin(required_type) in type_to_checker_map: return type_to_checker_map[get_origin(required_type)](_object, required_type) return None
def is_matching_type(_type: Type, annotation: Any) -> bool: """Return whether a specified type matches an annotation. This function does a bit more than a simple comparison, and performs the following extra checks: - If the :param`annotation` is a union, then the union is unwrapped and each of its types is compared against :param:`_type`. - If the specified :param:`_type` is generic, it will verify that all of its parameters match those of a matching annotation. """ # Look for an easy win. if _type == annotation: return True # If annotation is Union, we unwrap it to check against each of the possible inner # types. if is_union_type(annotation): if any(_type == tt for tt in get_args(annotation, evaluate=True)): return True # If both the global type and the argument annotation can be reduced to # the same base type, and have equivalent argument tuples, we can # assume that they are equivalent. # TODO return False
def _add_callable_dependencies( self, call: GenericCallableAccessibleObject, recursion_level: int ) -> None: """Add required dependencies. Args: call: The object whose parameter types should be added as dependencies. recursion_level: The current level of recursion of the search """ self._logger.debug("Find dependencies for %s", call) if recursion_level > config.INSTANCE.max_cluster_recursion: self._logger.debug("Reached recursion limit. No more dependencies added.") return for param_name, type_ in call.inferred_signature.parameters.items(): self._logger.debug("Resolving '%s' (%s)", param_name, type_) types = {type_} if is_union_type(type_): types = set(get_args(type_)) for elem in types: if is_primitive_type(elem): self._logger.debug("Not following primitive argument.") continue if inspect.isclass(elem): assert elem self._logger.debug("Adding dependency for class %s", elem) self._dependencies_to_solve.add( DependencyPair(elem, recursion_level) ) else: self._logger.debug("Found typing annotation %s, skipping", elem)
def robust_isinstance(inst, typ) -> bool: """ Similar to isinstance, but if 'typ' is a parametrized generic Type, it is first transformed into its base generic class so that the instance check works. It is also robust to Union and Any. :param inst: :param typ: :return: """ if typ is Any: return True if is_typevar(typ): if hasattr(typ, '__constraints__') and typ.__constraints__ is not None: typs = get_args(typ, evaluate=True) return any(robust_isinstance(inst, t) for t in typs) elif hasattr(typ, '__bound__') and typ.__bound__ is not None: return robust_isinstance(inst, typ.__bound__) else: # a raw TypeVar means 'anything' return True else: if is_union_type(typ): typs = get_args(typ, evaluate=True) return any(robust_isinstance(inst, t) for t in typs) else: return isinstance(inst, get_base_generic_type(typ))
def is_valid_pep484_type_hint(typ_hint, allow_forward_refs: bool = False): """ Returns True if the provided type is a valid PEP484 type hint, False otherwise. Note: string type hints (forward references) are not supported by default, since callers of this function in parsyfiles lib actually require them to be resolved already. :param typ_hint: :param allow_forward_refs: :return: """ # most common case first, to be faster try: if isinstance(typ_hint, type): return True except: pass # optionally, check forward reference try: if allow_forward_refs and is_forward_ref(typ_hint): return True except: pass # finally check unions and typevars try: return is_union_type(typ_hint) or is_typevar(typ_hint) except: return False
def is_collection(object_type, strict: bool = False) -> bool: """ Utility method to check if a type is a subclass of typing.{List,Dict,Set,Tuple} or of list, dict, set, tuple. If strict is set to True, the method will return True only if the class IS directly one of the base collection classes :param object_type: :param strict: if set to True, this method will look for a strict match. :return: """ if object_type is None or object_type is Any or is_union_type( object_type) or is_typevar(object_type): return False elif strict: return object_type == dict \ or object_type == list \ or object_type == tuple \ or object_type == set \ or get_base_generic_type(object_type) == Dict \ or get_base_generic_type(object_type) == List \ or get_base_generic_type(object_type) == Set \ or get_base_generic_type(object_type) == Tuple else: return issubclass(object_type, Dict) \ or issubclass(object_type, List) \ or issubclass(object_type, Set) \ or issubclass(object_type, Tuple) \ or issubclass(object_type, dict) \ or issubclass(object_type, list) \ or issubclass(object_type, tuple) \ or issubclass(object_type, set)
def _check_type_get_basemodels(t: type) -> List[Type[BaseModel]]: """ Checks if collection type is subclass of BaseModel or Union of them, returns list of BaseModels involved. In future untyped collection or Colelction[dict] will be supported. :param t: Type of collection. :return: List of BaseModels, which collection can hold. :raise: TypeError if collection type is improper. """ result = [] if typing_inspect.is_union_type(t): for tt in typing_inspect.get_args(t): if not issubclass(tt, BaseModel): raise TypeError( f"Args of Union must be BaseModels, {t} not.") result.append(tt) else: try: if issubclass(t, BaseModel): result.append(t) except TypeError: pass from pymotyc import MotycModel for tt in [*result]: for parent in tt.__mro__: if parent in [MotycModel, BaseModel]: break if parent not in result: result.append(parent) if not result: raise TypeError(f"Improper type {t} of the Collection.") return result
def _default_field_for_attribute(cls: type, attribute: attr.Attribute, tp: type, field_kwargs: Mapping[str, Any], field_for_attr: FIELD_FOR_ATTR) -> Field: origin = get_origin(tp) args = get_args(tp) if origin == list: return marshmallow.fields.List( field_for_attr(cls, attribute, args[0], {}), **field_kwargs) elif origin == dict: return marshmallow.fields.Dict( keys=field_for_attr(cls, attribute, args[0], {}), values=field_for_attr(cls, attribute, args[1], {}), **field_kwargs) elif is_union_type(tp): return field_for_attr(cls, attribute, args[0], field_kwargs) elif hasattr(tp, "Schema"): return NestedField(tp.Schema, **field_kwargs) # Self reference elif tp == cls or isinstance(tp, str) and tp == cls.__name__ \ or isinstance(tp, ForwardRef) and tp.__forward_arg__ == cls.__name__: return NestedField("self", **field_kwargs) elif issubclass(tp, Enum): return EnumField(tp, **field_kwargs) elif issubclass(tp, PurePath): return PathField(tp, **field_kwargs) else: return _SIMPLE_TYPES.get(tp, Raw)(**field_kwargs)
def _all_subclasses(typ, *, module=None): """ Return all subclasses of a given type. The type must be one of - :class:`GTScriptAstNode` (returns all subclasses of the given class) - :class:`Union` (return the subclasses of the united) - :class:`ForwardRef` (resolve the reference given the specified module and return its subclasses) - built-in python type: :class:`str`, :class:`int`, `type(None)` (return as is) """ if inspect.isclass(typ) and issubclass(typ, gtscript_ast.GTScriptASTNode): result = { typ, *typ.__subclasses__(), *[ s for c in typ.__subclasses__() for s in PyToGTScript._all_subclasses(c) if not inspect.isabstract(c) ], } return result elif inspect.isclass(typ) and typ in [ gtc.common.AssignmentKind, gtc.common.UnaryOperator, gtc.common.BinaryOperator, ]: # note: other types in gtc.common, e.g. gtc.common.DataType are not valid leaf nodes here as they # map to symbols in the gtscript ast and are resolved there assert issubclass(typ, enum.Enum) return {typ} elif typing_inspect.is_union_type(typ): return { sub_cls for el_cls in typing_inspect.get_args(typ) for sub_cls in PyToGTScript._all_subclasses(el_cls, module=module) } elif isinstance(typ, typing.ForwardRef): type_name = typing_inspect.get_forward_arg(typ) if not hasattr(module, type_name): raise ValueError( f"Reference to type `{type_name}` in `ForwardRef` not found in module {module.__name__}" ) return PyToGTScript._all_subclasses(getattr(module, type_name), module=module) elif typ in [ type_definitions.StrictStr, type_definitions.StrictInt, type_definitions.StrictFloat, str, int, float, type(None), ]: # TODO(tehrengruber): enhance return {typ} raise ValueError(f"Invalid field type {typ}")
def _check_annotation(f_type, f_fullname, f_default): if typing_inspect.is_tuple_type(f_type): if f_default is not None: raise RuntimeError(f'invalid type annotation on {f_fullname}: ' f'default is defined for tuple type') f_default = tuple elif typing_inspect.is_union_type(f_type): for t in typing_inspect.get_args(f_type, evaluate=True): _check_annotation(t, f_fullname, f_default) elif typing_inspect.is_generic_type(f_type): if f_default is not None: raise RuntimeError(f'invalid type annotation on {f_fullname}: ' f'default is defined for container type ' f'{f_type!r}') ot = typing_inspect.get_origin(f_type) if ot is None: raise RuntimeError( f'cannot find origin of a generic type {f_type}') if ot in (list, List, collections.abc.Sequence): f_default = list elif ot in (set, Set): f_default = set elif ot in (frozenset, FrozenSet): f_default = frozenset elif ot in (dict, Dict): f_default = dict else: raise RuntimeError(f'invalid type annotation on {f_fullname}: ' f'{f_type!r} is not supported') elif f_type is not None: if f_type is Any: f_type = object if not isinstance(f_type, type): raise RuntimeError(f'invalid type annotation on {f_fullname}: ' f'{f_type!r} is not a type') if typeutils.is_container_type(f_type): if f_default is not None: raise RuntimeError(f'invalid type annotation on {f_fullname}: ' f'default is defined for container type ' f'{f_type!r}') # Make sure that we can actually construct an empty # version of this type before we decide it is the default. try: f_type() except TypeError: pass else: f_default = f_type return f_default
def _is_direct_assignment_possible(self, a: Type[Any], b: Type[Any]) -> bool: b = self._resolve_forward_ref(b) if b is Any: return True elif is_union_type(b): return any([self._is_direct_assignment_possible(a, t) for t in get_args(b)]) elif issubclass(a, b): return True return False
def check_type(obj, tp): if is_union_type(tp): return any(check_type(obj, a) for a in tp.__args__) else: origin = get_origin(tp) if origin is None or origin == tp: return isinstance(obj, tp) else: return check_type(obj, origin)
def decode_union(decoder, typ, json_value): if not inspect.is_union_type(typ): return Unsupported union_types = inspect.get_args(typ) for union_type in union_types: try: return decoder.decode(union_type, json_value) except JsonError: pass raise JsonError(f'Value {json_value} can not be deserialized as {typ}')
def _validate_generic_return(self, arg_type: Type, value: Any) -> None: """Validate List or Dict types. :note: we return any here because the calling function also returns any. """ if issubclass(typing_inspect.get_origin(arg_type), list): if not isinstance(value, list): raise exceptions.ServerError( f"Invalid return value, type needs to be a list. Argument type should be {arg_type}" ) el_type = typing_inspect.get_args(arg_type, evaluate=True)[0] if el_type is Any: return for el in value: if typing_inspect.is_union_type(el_type): self._validate_union_return(el_type, el) elif not isinstance(el, el_type): raise exceptions.ServerError(f"Element {el} of returned list is not of type {el_type}.") elif issubclass(typing_inspect.get_origin(arg_type), dict): if not isinstance(value, dict): raise exceptions.ServerError( f"Invalid return value, type needs to be a dict. Argument type should be {arg_type}" ) el_type = typing_inspect.get_args(arg_type, evaluate=True)[1] if el_type is Any: return for k, v in value.items(): if not isinstance(k, str): raise exceptions.ServerError("Keys of return dict need to be strings.") if typing_inspect.is_union_type(el_type): self._validate_union_return(el_type, v) elif not isinstance(v, el_type): raise exceptions.ServerError(f"Element {v} of returned list is not of type {el_type}.") else: # This should not happen because of MethodProperties validation raise exceptions.BadRequest( f"Failed to validate generic type {arg_type} of return value, only List and Dict are supported" )
def encode_union(encoder, typ, value): if not inspect.is_union_type(typ): return Unsupported union_types = inspect.get_args(typ) for union_type in union_types: try: return encoder.encode(value, union_type) except JsonError: pass raise JsonError(f'Value {value} can not be deserialized as {typ}')
def issubclass(cls, classinfo): if classinfo is dataclass: return False if classinfo is Union or is_union_type(cls): return classinfo is Union and is_union_type(cls) if original_isinstance(classinfo, GenericMeta): return (original_isinstance(cls, GenericMeta) and classinfo.__args__ is None and get_origin(cls) is classinfo) if original_isinstance(cls, GenericMeta): origin = get_origin(cls) if isinstance(origin, GenericMeta): origin = origin.__base__ return origin is classinfo return original_issubclass(cls, classinfo)
def __call__(self, tp): if inspect.isclass(tp): return self.iter_mro(tp) if ti.is_optional_type(tp): return self.iter_optional(tp) if ti.is_tuple_type(tp): return self.iter_generic(tp) if ti.is_union_type(tp): return self.iter_generic(tp) if ti.is_generic_type(tp): return self.iter_generic(tp)
def get_args(cls) -> Tuple: if typing_inspect.is_union_type(cls): try: return cls.__union_params__ except AttributeError: pass return cls.__args__ elif issubclass(cls, List): return cls.__args__ else: raise ValueError("Cannot get type arguments for {}".format(cls))
def is_leq_informative_union(left, right): type_parameters_right = get_args(right) if is_union_type(left): type_parameters_left = get_args(left) return all( any(is_leq_informative(l, r) for r in type_parameters_right) for l in type_parameters_left) else: return any( is_leq_informative(left, parameter) for parameter in get_args(right))
def is_type(type_): return ( isinstance(type_, type) or type_ is Unknown or type_ is Any or is_parameterized(type_) or is_parametrical(type_) or is_typevar(type_) or is_union_type(type_) )
def select_concrete_type(self, select_from: Optional[Type]) -> Optional[Type]: """Select a concrete type from the given type. This is required e.g. when handling union types. Currently only unary types, Any and Union are handled.""" if select_from == Any: return randomness.choice(self.get_all_generatable_types()) if is_union_type(select_from): possible_types = get_args(select_from) if possible_types is not None and len(possible_types) > 0: return randomness.choice(possible_types) return None return select_from