def detect_link(field: ModelField) -> Optional[LinkInfo]: """ It detects link and returns LinkInfo if any found. :param field: ModelField :return: Optional[LinkInfo] """ if field.type_ == Link: if field.allow_none is True: return LinkInfo( field=field.name, model_class=field.sub_fields[0].type_, # type: ignore link_type=LinkTypes.OPTIONAL_DIRECT, ) return LinkInfo( field=field.name, model_class=field.sub_fields[0].type_, # type: ignore link_type=LinkTypes.DIRECT, ) if (inspect.isclass(get_origin(field.outer_type_)) and issubclass(get_origin(field.outer_type_), list) # type: ignore and len(field.sub_fields) == 1 # type: ignore ): internal_field = field.sub_fields[0] # type: ignore if internal_field.type_ == Link: if internal_field.allow_none is True: return None return LinkInfo( field=field.name, model_class=internal_field.sub_fields[0].type_, # type: ignore link_type=LinkTypes.LIST, ) return None
def is_list_type(t: Any, test_type: Optional[type] = None) -> bool: """Check if `t` is list type. And optionally check if the list items are of `test_type` >>> is_list_type(List[int]) True >>> is_list_type(Optional[List[int]]) True >>> is_list_type(Optional[List[int]], int) True >>> is_list_type(Optional[List[int]], str) False >>> is_list_type(Optional[int]) False >>> is_list_type(List[Tuple[int, int]]) True >>> is_list_type(List[Tuple[int, int]], int) False >>> is_list_type(List[Tuple[int, int]], Tuple[int, int]) True >>> is_list_type(List[strEnum], Enum) True >>> is_list_type(int) False >>> is_list_type(Literal[1,2,3]) False >>> is_list_type(List[Union[str, int]]) True >>> is_list_type(List[Union[str, int]], Union[str, int]) False >>> is_list_type(List[Union[str, int]], str) True >>> is_list_type(List[Union[str, int]], int) False >>> is_list_type(List[Union[str, int]], Union[int, int]) False """ if get_origin(t): if is_optional_type(t) or is_union_type(t): for arg in get_args(t): if is_list_type(arg, test_type): return True elif get_origin(t) == Literal: # type:ignore return False # Literal cannot contain lists see pep 586 elif issubclass(get_origin(t), list): # type: ignore if test_type and get_args(t): first_arg = get_args(t)[0] # To support a list with union of multiple product blocks. if is_union_type(first_arg) and get_args(first_arg) and not is_union_type(test_type): first_arg = get_args(first_arg)[0] return is_of_type(first_arg, test_type) else: return True return False
def is_union_type(t: Any, test_type: Optional[type] = None) -> bool: """Check if `t` is union type (Union[Type, AnotherType]). Optionally check if T is of `test_type` We cannot check for literal Nones. >>> is_union_type(Union[int, str]) True >>> is_union_type(Union[int, str], str) True >>> is_union_type(Union[int, str], Union[int, str]) True >>> is_union_type(Union[int, None]) True >>> is_union_type(int) False """ if get_origin(t) not in union_types: return False if not test_type: return True if is_of_type(t, test_type): return True for arg in get_args(t): result = is_of_type(arg, test_type) if result: return result return False
def is_optional_type(t: Any, test_type: Optional[type] = None) -> bool: """Check if `t` is optional type (Union[None, ...]). And optionally check if T is of `test_type` >>> is_optional_type(Optional[int]) True >>> is_optional_type(Union[None, int]) True >>> is_optional_type(Union[int, str, None]) True >>> is_optional_type(Optional[int], int) True >>> is_optional_type(Optional[int], str) False >>> is_optional_type(Optional[State], int) False >>> is_optional_type(Optional[State], State) True """ if get_origin(t) in union_types and None.__class__ in get_args(t): for arg in get_args(t): if arg is None.__class__: continue return not test_type or is_of_type(arg, test_type) return False
def is_of_type(t: Any, test_type: Any) -> bool: """Check if annotation type is valid for type. >>> is_of_type(list, list) True >>> is_of_type(List[int], List[int]) True >>> is_of_type(strEnum, str) True >>> is_of_type(strEnum, Enum) True >>> is_of_type(int, str) False """ if is_union_type(test_type): return any(get_origin(t) is get_origin(arg) for arg in get_args(test_type)) if ( get_origin(t) and get_origin(test_type) and get_origin(t) is get_origin(test_type) and get_args(t) == get_args(test_type) ): return True if test_type is t: # Test type is a typing type instance and matches return True # Workaround for the fact that you can't call issubclass on typing types try: return issubclass(t, test_type) except TypeError: return False
def wrap_validate_singleton( self: ModelField, v: Any, values: Dict[str, Any], loc: "LocStr", cls: Optional["ModelOrDc"], ) -> "ValidateReturn": if self.sub_fields: if get_origin(self.type_) is Union: for field in self.sub_fields: if v.__class__ is field.outer_type_: return v, None for field in self.sub_fields: try: if isinstance(v, field.outer_type_): return v, None except TypeError: pass return upstream_validate_singleton(self, v, values, loc, cls)
def test_get_origin(input_value, output_value): if input_value is None: pytest.skip('Skipping undefined hint for this python version') assert get_origin(input_value) is output_value
def _analyze_arg_type(self, arg_type: Optional[type]): if arg_type is None: return origin = get_origin(arg_type) if origin: shape = None dtype = None if origin is Union: allow_types = [] for arg in get_args(arg_type): if arg is not None: allow_types.append(arg) self.shape = ArgTypeShape.UNION self.outer_type = [self._get_arg_type(t) for t in allow_types] return if inspect.isclass(origin): if issubclass(origin, List): shape = ArgTypeShape.LIST args = get_args(arg_type) if args and len(args) == 1: vt = args[0] if not inspect.isclass(vt): vt = None dtype = self._get_arg_type(vt) elif issubclass(origin, Mapping): shape = ArgTypeShape.DICT args = get_args(arg_type) if args and len(args) == 2: kt = args[0] vt = args[1] dtype = (self._get_arg_type(kt), self._get_arg_type(vt)) else: dtype = (None, None) elif issubclass(origin, Set): shape = ArgTypeShape.SET args = get_args(arg_type) if args and len(args) == 1: vt = args[0] if not inspect.isclass(vt): vt = None dtype = self._get_arg_type(vt) if shape is None: raise AssertionError( f'Unsupported generic argument type: {arg_type}') self.shape = shape self.outer_type = dtype return if isinstance(arg_type, str): if hasattr(builtins, arg_type): arg_type = getattr(builtins, arg_type) self.outer_type = arg_type if not inspect.isclass(arg_type): raise AssertionError( f'Non-generic argument type must be a class, but got: {arg_type}' ) if issubclass(arg_type, List): self.shape = ArgTypeShape.LIST self.outer_type = None elif issubclass(arg_type, Mapping): self.shape = ArgTypeShape.DICT self.outer_type = (None, None) elif issubclass(arg_type, Set): self.shape = ArgTypeShape.SET self.outer_type = None