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_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 is_subclass(lhs: Type, rhs: Type) -> object: if is_sequence_type(lhs): if not is_sequence_type(rhs): return False return is_subclass(unpack_type_argument(lhs), unpack_type_argument(rhs)) if is_optional_type(lhs): if not is_optional_type(rhs): return False return is_subclass(unpack_type_argument(lhs), unpack_type_argument(rhs)) return issubclass(lhs, rhs)
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 structure_attrs_fromdict(self, obj: typing.Mapping, cl: typing.Type) -> typing.Any: """Instantiate an attrs class from a mapping (dict).""" conv_obj = {} dispatch = self._structure_func.dispatch for a in attr.fields(cl): # We detect the type by metadata. type_ = a.type if type_ is None: # No type. continue name = a.name try: val = obj[name] except KeyError: if typing_inspect.is_optional_type(type_): if a.default in (missing, attr.NOTHING): val = None else: val = a.default elif a.default in (missing, attr.NOTHING): raise ValueError("Attribute is missing", a.name) else: continue if a.converter is None: val = dispatch(type_)(val, type_) conv_obj[name] = val return cl(**conv_obj)
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 is_opt(typ) -> bool: """ Test if the type is `typing.Optional`. """ args = typing_inspect.get_args(typ) return typing_inspect.is_optional_type(typ) and len( args) == 2 and not is_none(args[0]) and is_none(args[1])
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 get_settings_def(type_def: Type[Generic]) -> Type[Settings]: """Get settings definition from object type definition. :param type_def: :return: """ sig = inspect.signature(type_def.__init__) try: param = sig.parameters["settings"] except KeyError: raise Arcor2Exception("Type has no settings.") if typing_inspect.is_optional_type(param.annotation): settings_cls = typing_inspect.get_args(param.annotation)[0] else: settings_cls = param.annotation try: if not issubclass(settings_cls, Settings): raise Arcor2Exception("Settings have invalid type.") except TypeError: raise Arcor2Exception("Settings have invalid annotation.") if not is_dataclass(settings_cls): raise Arcor2Exception("Settings misses @dataclass decorator.") return settings_cls
def parse_annotation(raw_annotation: Type) -> AnnotationInfo: """Parse key information from annotation. :param annotation: A subscripted type. :returns: Annotation """ optional = typing_inspect.is_optional_type(raw_annotation) if optional: # e.g: Typing.Union[pandera.typing.Index[str], NoneType] if _LEGACY_TYPING: # pragma: no cover # get_args -> ((pandera.typing.Index, <class 'str'>), <class 'NoneType'>) origin, arg = typing_inspect.get_args(raw_annotation)[0] return AnnotationInfo(origin, arg, optional) # get_args -> (pandera.typing.Index[str], <class 'NoneType'>) raw_annotation = typing_inspect.get_args(raw_annotation)[0] origin = typing_inspect.get_origin(raw_annotation) args = typing_inspect.get_args(raw_annotation) arg = args[0] if args else args literal = typing_inspect.is_literal_type(arg) if literal: arg = typing_inspect.get_args(arg)[0] return AnnotationInfo(origin=origin, arg=arg, optional=optional, literal=literal)
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.optional = typing_inspect.is_optional_type(raw_annotation) if self.optional: # e.g: Typing.Union[pandera.typing.Index[str], 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.literal = typing_inspect.is_literal_type(self.arg) if self.literal: self.arg = typing_inspect.get_args(self.arg)[0]
def config_repr_callable_args( init, param_dict=None, skip_self=False, only_required_args=None, literal_defaults=None, ): if only_required_args is None: only_required_args = ONLY_REQUIRED_ARGS if literal_defaults is None: literal_defaults = LITERAL_DEFAULTS if isinstance(init, Signature): sig = init globals_ = None else: sig = signature(init) globals_ = get_globals(init) concretize = param_dict or globals_ append_positionals = has_varargs(sig) arg_reprs, kwarg_reprs = [], {} parameter_iter = iter(sig.parameters.items()) if skip_self: next(parameter_iter) for name, param in parameter_iter: if literal_defaults and (param.default not in (Parameter.empty, None)): r = param.default else: if concretize: type_ = fully_concretize_type(param.annotation, param_dict, globals_) else: type_ = param.annotation r = config_repr(type_) if param.kind is Parameter.POSITIONAL_ONLY: arg_reprs.append(r) elif param.kind is Parameter.VAR_POSITIONAL: arg_reprs.extend([r, ellipsis_]) elif param.kind is Parameter.VAR_KEYWORD: kwarg_reprs[ellipsis_] = r elif (only_required_args and param.default is not Parameter.empty and is_optional_type(param.annotation)): # only skip once we're past the positionals, to avoid skipping args continue elif param.kind is Parameter.KEYWORD_ONLY: kwarg_reprs[name] = r else: if append_positionals: arg_reprs.append(r) else: kwarg_reprs[name] = r repr_ = {KWARGS_KEY: kwarg_reprs} if arg_reprs: repr_[ARGS_KEY] = arg_reprs return repr_, sig
def get_optional(tp: Type): if is_optional_type(tp): if get_origin(tp) == Union: for t in get_args(tp): if t is None: continue return t
def _from_json_like(type_hint, value): optional = is_optional_type(type_hint) (real_type, ) = ((t for t in get_args(type_hint) if not is_none_type(t)) if optional else (type_hint, )) if real_type is Any: return value return from_json_like(real_type, value, optional)
def optional_core(t: Type) -> Tuple[bool, Type]: """ :return (is_optional, t.core | t | NoneType) """ if t is type(None): return True, type(None) if is_optional_type(t): first, second = get_args(t) return True, (second if isinstance(None, first) else first) else: return False, t
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 _repr(val: t.Any) -> str: assert val is not None if types.is_none_type(val): return 'NoneType' elif ti.is_literal_type(val): return str(val) elif ti.is_new_type(val): nested_type = val.__supertype__ return f'{_qualified_name(val)}[{get_repr(nested_type)}]' elif ti.is_typevar(val): tv_constraints = ti.get_constraints(val) tv_bound = ti.get_bound(val) if tv_constraints: constraints_repr = (get_repr(tt) for tt in tv_constraints) return f'typing.TypeVar(?, {", ".join(constraints_repr)})' elif tv_bound: return get_repr(tv_bound) else: return 'typing.Any' elif ti.is_optional_type(val): optional_args = ti.get_args(val, True)[:-1] nested_union = len(optional_args) > 1 optional_reprs = (get_repr(tt) for tt in optional_args) if nested_union: return f'typing.Optional[typing.Union[{", ".join(optional_reprs)}]]' else: return f'typing.Optional[{", ".join(optional_reprs)}]' elif ti.is_union_type(val): union_reprs = (get_repr(tt) for tt in ti.get_args(val, True)) return f'typing.Union[{", ".join(union_reprs)}]' elif ti.is_generic_type(val): attr_name = val._name generic_reprs = (get_repr(tt) for tt in ti.get_args(val, evaluate=True)) return f'typing.{attr_name}[{", ".join(generic_reprs)}]' else: val_name = _qualified_name(val) maybe_td_entries = getattr(val, '__annotations__', {}).copy() if maybe_td_entries: # we are dealing with typed dict # that's quite lovely td_keys = sorted(maybe_td_entries.keys()) internal_members_repr = ', '.join( '{key}: {type}'.format(key=k, type=get_repr(maybe_td_entries.get(k))) for k in td_keys ) return f'{val_name}{{{internal_members_repr}}}' elif 'TypedDict' == getattr(val, '__name__', ''): return 'typing_extensions.TypedDict' else: return val_name
def auto_any(cls) -> schema.Schema: if inspect.isclass(cls): if issubclass(cls, bool): return schema.BooleanSchema() elif issubclass(cls, float): return schema.NumberSchema() elif issubclass(cls, int): return schema.IntSchema() elif issubclass(cls, str): return schema.StringSchema() elif issubclass(cls, datetime): return schema.StringSchema(format='date-time') elif issubclass(cls, date): return schema.StringSchema(format='date') elif issubclass(cls, timedelta): return schema.NumberSchema(format='double') elif issubclass(cls, List): vt, = get_args(cls) subschema = auto_any(vt) return schema.ArraySchema(items=subschema) elif issubclass(cls, Dict): kt, vt = get_args(cls) if not issubclass(kt, str): raise NotImplementedError(('class3', cls, kt)) subschema = auto_any(vt) return ObjectSchema(additional=subschema, ) elif is_dataclass(cls): return auto_dataclass(cls) else: raise NotImplementedError(('class2', cls)) else: if is_optional_type(cls): item, _ = get_last_args(cls) trc('1').debug('%s %s', item, _) r = auto_any(item) if isinstance(r, schema.Nullable): r.nullable = True else: raise NotImplementedError('can not be nullable') trc('2').debug('%s', r) return r else: raise NotImplementedError(('class', cls))
def _get_attr_value(type_: type, indent: int, key: str = "<Unknown>") -> str: """ Indentation is a parameter used for Union and Dict. If the type parameter is a "Optional" type, only the args are used (without the NoneType) """ if is_optional_type(type_): referred_type = typing_inspect.get_args(type_, evaluate=True)[0] type_ = referred_type for predicate_function, handle_function in LIST_OF_IMPLEMENTATIONS: if predicate_function(type_): return handle_function(type_, indent=indent) raise RuntimeError( f"Alfacase Schema does not know how to handle {type_}.\n" f"Perhaps you forgot to add the type hint to attribute named '{key}'?")
def is_opt(typ) -> bool: """ Test if the type is `typing.Optional`. >>> is_opt(Optional[int]) True >>> is_opt(Optional) True >>> is_opt(None.__class__) False """ args = type_args(typ) if args: return typing_inspect.is_optional_type(typ) and len( args) == 2 and not is_none(args[0]) and is_none(args[1]) else: return typ is Optional
def _get_attribute_schema(key: str, value: attr.ib, indent: int = 2) -> str: """ Helper method that return the equivalent schema for the given key and value. Keep in mind that for stricyyaml schema, an Optional type means that the user don't need to inform a value. While for type hint, an Optional type means that the attribute accepts None. """ if value.default is attr.NOTHING and is_optional_type(value.type): msg = ( "StrictYAML doesn't support None value (only missing keys denote a value), " "therefore Optional type are only allowed when the case has a default value." ) raise TypeError(msg) attribute_name = _get_attr_name(key, value) attribute_value = _get_attr_value(value.type, indent=indent, key=key) return f"{INDENTANTION * indent}{attribute_name}: {attribute_value}" + ","
async def _get_dict_value_from_message( self, arg: str, dict_prefix: str, dict_with_prefixed_names: Dict[str, Any] ) -> Dict[str, Any]: value = {k[len(dict_prefix) :]: v for k, v in dict_with_prefixed_names.items()} # Check if the values should be converted to lists type_args = self._argspec.annotations.get(arg) if typing_inspect.is_optional_type(type_args): # If optional, get the type args from the not None type argument dict_args = typing_inspect.get_args(typing_inspect.get_args(type_args, evaluate=True)[0], evaluate=True) else: dict_args = typing_inspect.get_args(self._argspec.annotations.get(arg), evaluate=True) dict_value_arg_type = ( typing_inspect.get_origin(dict_args[1]) if typing_inspect.get_origin(dict_args[1]) else dict_args[1] ) if issubclass(dict_value_arg_type, list): value = {key: [val] if not isinstance(val, list) else val for key, val in value.items()} return value
def unwrap_optional(cls: typing.Type) -> typing.Type: assert typing_inspect.is_optional_type(cls) if not typing_inspect.is_union_type(cls): raise NotImplementedError origin_args = typing_inspect.get_args(cls) selected_tt = None for tt in origin_args: if tt is type(None): continue elif selected_tt is None: selected_tt = tt else: raise TypeError return selected_tt
def _validate_value(value: object, target_type: Type[object]) -> None: if target_type is Any: return elif _is_list(target_type): _validate_list(value, cast(Type[List[object]], target_type)) elif _is_dictionary(target_type): _validate_dictionary(value, cast(Type[Dict[object, object]], target_type)) elif _is_typed_dictionary(target_type): _validate_typed_dictionary(value, target_type) elif is_optional_type(target_type): if value is None: return _validate_value(value, get_args(target_type)[0]) else: if target_type not in [int, float, str, bool]: raise InvalidJson(f"Invalid value type {target_type}") if not isinstance(value, target_type): raise InvalidJson(f"`{value}` is not a {target_type}")
def get_key_and_value_types(dict_type: Type[Dict], Serializable=Serializable) -> Tuple[Optional[Type], Optional[Type]]: args = get_type_arguments(dict_type) if len(args) != 2: logger.debug(f"Weird.. the type {dict_type} doesn't have 2 args: {args}") return None, None K_ = args[0] V_ = args[1] # Get rid of Unions or ForwardRefs or Optionals V_ = get_actual_type(V_) logger.debug(f"K_: {K_}, V_: {V_}") if isinstance(V_, tuple): V_ = get_first_non_None_type(V_) elif tpi.is_optional_type(V_): logger.debug(f"V_ is optional: {V_}") V_ = get_first_non_None_type(V_) return K_, V_
def _analyze_signature_set_oas_set( request_body: oas.OASRequestBody, body_arg: t.Type[t.Any], ) -> t.Tuple[t.Set[exceptions.Error], bool]: logger.trace('Operation defines both request body and argument handler') is_required = request_body.required is_arg_required = not ti.is_optional_type(body_arg) if is_required and not is_arg_required: return { exceptions.Error( param_name='body', reason=exceptions.IncorrectTypeReason( actual=body_arg, expected=model.BODY_TYPES, ), ), }, True return set(), True
def get_type_hint_type(type_hint): """Given a type hint, return the type, whether it's contained in a List and whether it's Optional.""" optional = typing_inspect.is_optional_type(type_hint) if optional: type_hint = typing_inspect.get_args(type_hint)[0] origin = typing_inspect.get_origin(type_hint) is_list = False if origin in (list, List, tuple, Tuple): is_list = True args = typing_inspect.get_args(type_hint) if args and not type(args[0]) == TypeVar: type_ = args[0] else: type_ = origin else: type_ = type_hint return type_, is_list, optional
def dump_type(stream: Writer, typ: Type): if is_optional_type(typ): stream.write('Optional', color=Color.Green) stream.write('[') dump_type(stream, unpack_type_argument(typ)) stream.write(']') elif is_sequence_type(typ): stream.write('Sequence', color=Color.Green) stream.write('[') dump_type(stream, unpack_type_argument(typ)) stream.write(']') elif is_generic_type(typ): stream.write(get_origin(typ).__name__, color=Color.Green) stream.write('[') for idx, element in enumerate(get_args(typ)): if idx: stream.write(', ') dump_type(stream, element) stream.write(']') else: stream.write(typ.__name__, color=Color.Green)
def json_to(type: Type[_V]) -> Callable[[object], _V]: # pylint: disable=redefined-builtin, too-many-return-statements try: return type.json_to # type: ignore except AttributeError: if is_optional_type(type): # needs to come before 'issubclass' return _json_to_optional(get_args(type)[0]) # type: ignore if is_generic_type(type): # needs to come before 'issubclass' if get_origin(type) == list: return _json_to_list(get_args(type)[0]) # type: ignore if get_origin(type) == dict: return _json_to_dict(get_args(type)[1]) # type: ignore if issubclass(type, bool): # needs to come before 'int' return _json_to_bool # type: ignore if issubclass(type, int): return _json_to_int # type: ignore if issubclass(type, str): return _json_to_str # type: ignore if issubclass(type, list): raise TypeError('no element type specified for list') if issubclass(type, dict): raise TypeError('no value type specified for dict') raise TypeError('not a JSON value type')
def field_to_schema(field): """Return the schema (key, value) pair for given dataclass field.""" metadata = field.metadata or dict() if (field.default is not dataclasses.MISSING or field.default_factory is not dataclasses.MISSING): is_optional = True elif typing_inspect.is_optional_type(field.type): is_optional = True else: is_optional = False key_kwargs = dict(description=metadata.get('description', None)) if is_optional: key_cls = DataclassSchemaOptional if dataclasses.is_dataclass(field.type): # for nested dataclass, we pass to the optional subclass key_kwargs.update(dataclass_cls=field.type) else: # regular optional type # we also need the spacial subclass of optional # to handle more gracefully the default factory # of the other fileds pass key_kwargs.update(default=_get_field_default(field)) else: key_cls = schema.Literal schema_key = key_cls(field.name, **key_kwargs) # If the schema value was already defined by the user predefined_schema_value = metadata.get("schema", None) if predefined_schema_value is not None: schema_value = predefined_schema_value else: # Otherwise we infer schema value from the field type if dataclasses.is_dataclass(field.type): schema_value = getattr(field.type, 'schema', field.type) else: schema_value = field.type return (schema_key, schema_value)