def _build_value(type_: Type, data: Any, config: Config) -> Any: if is_union(type_): return _build_value_for_union(union=type_, data=data, config=config) elif is_generic_collection(type_) and is_instance(data, extract_origin_collection(type_)): return _build_value_for_collection(collection=type_, data=data, config=config) elif is_dataclass(type_) and is_instance(data, Data): return from_dict(data_class=type_, data=data, config=config) return data
def _build_value_for_collection(collection: Type, data: Any, config: Config) -> Any: data_type = data.__class__ if is_instance(data, Mapping): item_type = extract_generic(collection, defaults=(Any, Any))[1] return data_type((key, _build_value(type_=item_type, data=value, config=config)) for key, value in data.items()) elif is_instance(data, tuple): types = extract_generic(collection) if len(types) == 2 and types[1] == Ellipsis: return data_type(_build_value(type_=types[0], data=item, config=config) for item in data) return data_type( _build_value(type_=type_, data=item, config=config) for item, type_ in zip_longest(data, types) ) item_type = extract_generic(collection, defaults=(Any,))[0] return data_type(_build_value(type_=item_type, data=item, config=config) for item in data)
def _build_value_for_collection(collection: Type, data: Any, config: Config) -> Any: if is_instance(data, Mapping): return data.__class__( (key, _build_value(type_=extract_generic(collection)[1], data=value, config=config)) for key, value in data.items() ) return data.__class__(_build_value(type_=extract_generic(collection)[0], data=item, config=config) for item in data)
def _build_value_for_union(union: Type, data: Any, config: Config) -> Any: types = extract_generic(union) if is_optional(union) and len(types) == 2: return _build_value(type_=types[0], data=data, config=config) union_matches = {} for inner_type in types: try: # noinspection PyBroadException try: data = transform_value(type_hooks=config.type_hooks, cast=config.cast, target_type=inner_type, value=data) except Exception: # pylint: disable=broad-except continue value = _build_value(type_=inner_type, data=data, config=config) if is_instance(value, inner_type): if config.strict_unions_match: union_matches[inner_type] = value else: return value except DaciteError: pass if config.strict_unions_match: if len(union_matches) > 1: raise StrictUnionMatchError(union_matches) return union_matches.popitem()[1] if not config.check_types: return data raise UnionMatchError(field_type=union, value=data)
def test_is_instance_with_not_supported_generic_types(): T = TypeVar("T") class X(Generic[T]): pass assert not is_instance(X[str](), X[str])
def from_dict(data_class: Type[T], data: Data, config: Optional[Config] = None) -> T: """Create a data class instance from a dictionary. :param data_class: a data class type :param data: a dictionary of a input data :param config: a configuration of the creation process :return: an instance of a data class """ init_values: Data = {} post_init_values: Data = {} config = config or Config() try: data_class_hints = get_type_hints(data_class, globalns=config.forward_references) except NameError as error: raise ForwardReferenceError(str(error)) data_class_fields = get_fields(data_class) if config.strict: extra_fields = set(data.keys()) - {f.name for f in data_class_fields} if extra_fields: raise UnexpectedDataError(keys=extra_fields) for field in data_class_fields: field = copy.copy(field) field.type = data_class_hints[field.name] try: try: field_data = data[field.name] transformed_value = transform_value( type_hooks=config.type_hooks, cast=config.cast, target_type=field.type, value=field_data) value = _build_value(type_=field.type, data=transformed_value, config=config) except DaciteFieldError as error: error.update_path(field.name) raise if config.check_types and not is_instance(value, field.type): raise WrongTypeError(field_path=field.name, field_type=field.type, value=value) except KeyError: try: value = get_default_value_for_field(field) except DefaultValueNotFoundError: if not field.init: continue raise MissingValueError(field.name) if field.init: init_values[field.name] = value else: post_init_values[field.name] = value return create_instance(data_class=data_class, init_values=init_values, post_init_values=post_init_values)
def _build_value_for_union(union: Type, data: Any, config: Config) -> Any: types = extract_generic(union) if is_optional(union) and len(types) == 2: return _build_value(type_=types[0], data=data, config=config) for inner_type in types: try: value = _build_value(type_=inner_type, data=data, config=config) if is_instance(value, inner_type): return value except DaciteError: pass if not config.check_types: return data raise UnionMatchError(field_type=union, value=data)
def from_dict(data_class: Type[T], data: Data, config: Optional[Config] = None) -> T: """Create a data class instance from a dictionary. :param data_class: a data class type :param data: a dictionary of a input data :param config: a configuration of the creation process :return: an instance of a data class """ init_values: Data = {} post_init_values: Data = {} config = config or Config() config.validate(data_class, data) try: data_class_hints = get_type_hints(data_class, globalns=config.forward_references) except NameError as error: raise ForwardReferenceError(str(error)) for field in fields(data_class): field = copy.copy(field) field.type = data_class_hints[field.name] try: try: value = _build_value(type_=field.type, data=config.get_value(field, data), config=config.make_inner(field)) except DaciteFieldError as error: error.update_path(field.name) raise if config.check_types and not is_instance(value, field.type): raise WrongTypeError(field_path=field.name, field_type=field.type, value=value) except ValueNotFoundError: try: value = get_default_value_for_field(field) except DefaultValueNotFoundError: if not field.init: continue raise MissingValueError(field.name) if field.init: init_values[field.name] = value else: post_init_values[field.name] = value return create_instance(data_class=data_class, init_values=init_values, post_init_values=post_init_values)
def from_config( cls, input_: Union[str, tuple[int, int]] ) -> "Prefix": if not isinstance(input_, str): if not is_instance( input_, cast(Type[Any], Tuple[int, int]) ): raise TypeError( f"Expected string or integer pair, got {input_!r}" ) value, bits = input_ return cls(value, bits) for char in input_.lower(): if char not in "0123456789abcdef": raise ValueError( f"Invalid charecter {char!r} in prefix {input_!r}" ) return cls(int(input_, 16), len(input_) * 4)
def _build_value_for_collection(collection: Type, data: Any, config: Config) -> Any: generic = extract_generic(collection) if is_instance(data, Mapping): return data.__class__( (key, _build_value(type_=generic[1], data=value, config=config)) for key, value in data.items()) collection_type = extract_origin_collection(collection) if collection_type == tuple and not (len(generic) == 2 and generic[1] == Ellipsis): return data.__class__( _build_value(type_=item_type, data=item, config=config) for item, item_type in zip(data, generic)) return data.__class__( _build_value(type_=generic[0], data=item, config=config) for item in data)
def test_is_instance_with_any_type(): assert is_instance(1, Any)
def test_is_instance_with_optional_and_not_matching_value_type(): assert not is_instance(1, Optional[str])
def test_is_instance_with_nested_generic_collection_and_not_matching_item_type( ): assert not is_instance([["test"]], List[List[int]])
def test_is_instance_with_generic_abstract_collection_and_matching_value_type( ): assert is_instance([1], Collection[int])
def test_is_instance_with_generic_collection_without_specified_inner_types_and_not_matching_value_type( ): assert not is_instance([1], Dict)
def test_is_instance_with_init_var_and_not_matching_value_type(): assert not is_instance(1, InitVar[str])
def test_is_instance_with_numeric_tower(): assert is_instance(1, float)
def test_is_instance_with_union_and_not_matching_value_type(): assert not is_instance("test", Union[int, float])
def test_is_instance_with_generic_mapping_and_not_matching_mapping_key_type(): assert not is_instance({1: 1}, Dict[str, int])
def test_is_instance_with_built_in_type_and_matching_value_type(): assert is_instance(1, int)
def test_is_instance_with_numeric_tower_and_new_type(): assert is_instance(1, NewType("NewType", float))
def test_is_instance_with_numeric_tower_and_optional(): assert is_instance(1, Optional[float])
def test_is_instance_with_new_type_and_not_matching_value_type(): assert not is_instance(1, NewType("MyStr", str))
def test_is_instance_with_init_var_and_matching_value_type(): assert is_instance(1, InitVar[int])
def test_is_instance_with_literal_and_matching_type(): from typing import Literal assert is_instance("A", Literal["A", "B"])
def test_is_instance_with_generic_mapping_and_not_matching_mapping_value_type( ): assert not is_instance({"test": "test"}, Dict[str, int])
def test_is_instance_with_built_in_type_and_not_matching_value_type(): assert not is_instance("test", int)
def test_is_instance_with_optional_literal_and_not_matching_type(): from typing import Literal assert not is_instance("C", Optional[Literal["A", "B"]])
def test_is_instance_with_generic_collection_and_matching_value_type(): assert is_instance([1], List[int])
def test_is_instance_with_optional_literal_and_none(): from typing import Literal assert is_instance(None, Optional[Literal["A", "B"]])