Skip to content

Commit 823c52d

Browse files
committed
moving utils to separate file
1 parent 14317e0 commit 823c52d

File tree

3 files changed

+43
-39
lines changed

3 files changed

+43
-39
lines changed

automapper/mapper.py

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import inspect
22
from copy import deepcopy
3-
from enum import Enum
43
from typing import (
54
Any,
65
Callable,
@@ -22,6 +21,7 @@
2221
DuplicatedRegistrationError,
2322
MappingError,
2423
)
24+
from .utils import is_enum, is_primitive, is_sequence, object_contains
2525

2626
# Custom Types
2727
S = TypeVar("S")
@@ -30,32 +30,6 @@
3030
SpecFunction = Callable[[Type[T]], Iterable[str]]
3131
FieldsMap = Optional[Dict[str, Any]]
3232

33-
__PRIMITIVE_TYPES = {int, float, complex, str, bytes, bytearray, bool}
34-
35-
36-
def _is_sequence(obj: Any) -> bool:
37-
"""Check if object implements `__iter__` method"""
38-
return hasattr(obj, "__iter__")
39-
40-
41-
def _is_subscriptable(obj: Any) -> bool:
42-
"""Check if object implements `__getitem__` method"""
43-
return hasattr(obj, "__getitem__")
44-
45-
46-
def _object_contains(obj: Any, field_name: str) -> bool:
47-
return _is_subscriptable(obj) and field_name in obj
48-
49-
50-
def _is_primitive(obj: Any) -> bool:
51-
"""Check if object type is primitive"""
52-
return type(obj) in __PRIMITIVE_TYPES
53-
54-
55-
def _is_enum(obj: Any) -> bool:
56-
"""Check if object type is enum"""
57-
return issubclass(type(obj), Enum)
58-
5933

6034
def _try_get_field_value(
6135
field_name: str, original_obj: Any, custom_mapping: FieldsMap
@@ -64,7 +38,7 @@ def _try_get_field_value(
6438
return True, custom_mapping[field_name] # type: ignore [index]
6539
if hasattr(original_obj, field_name):
6640
return True, getattr(original_obj, field_name)
67-
if _object_contains(original_obj, field_name):
41+
if object_contains(original_obj, field_name):
6842
return True, original_obj[field_name]
6943
return False, None
7044

@@ -275,7 +249,7 @@ def _map_subobject(
275249
self, obj: S, _visited_stack: Set[int], skip_none_values: bool = False
276250
) -> Any:
277251
"""Maps subobjects recursively"""
278-
if _is_primitive(obj) or _is_enum(obj):
252+
if is_primitive(obj) or is_enum(obj):
279253
return obj
280254

281255
obj_id = id(obj)
@@ -290,7 +264,7 @@ def _map_subobject(
290264
else:
291265
_visited_stack.add(obj_id)
292266

293-
if _is_sequence(obj):
267+
if is_sequence(obj):
294268
if isinstance(obj, dict):
295269
result = {
296270
k: self._map_subobject(

automapper/utils.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from enum import Enum
2+
from typing import Any
3+
4+
__PRIMITIVE_TYPES = {int, float, complex, str, bytes, bytearray, bool}
5+
6+
7+
def is_sequence(obj: Any) -> bool:
8+
"""Check if object implements `__iter__` method"""
9+
return hasattr(obj, "__iter__")
10+
11+
12+
def is_subscriptable(obj: Any) -> bool:
13+
"""Check if object implements `__getitem__` method"""
14+
return hasattr(obj, "__getitem__")
15+
16+
17+
def object_contains(obj: Any, field_name: str) -> bool:
18+
return is_subscriptable(obj) and field_name in obj
19+
20+
21+
def is_primitive(obj: Any) -> bool:
22+
"""Check if object type is primitive"""
23+
return type(obj) in __PRIMITIVE_TYPES
24+
25+
26+
def is_enum(obj: Any) -> bool:
27+
"""Check if object type is enum"""
28+
return issubclass(type(obj), Enum)

tests/test_issue_18_implicit_child_field_mapping.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,30 +12,32 @@ class UserDomain:
1212

1313

1414
@dataclass
15-
class TodoDomain:
15+
class InputDomain:
1616
description: str
1717
user: UserDomain
1818

1919

2020
@dataclass
21-
class TodoModel:
21+
class OutputModel:
2222
description: str
2323
user: UserDomain
2424
user_id: Optional[int] = None
2525

2626

2727
def test_map__implicit_mapping_of_child_obj_field_to_parent_obj_field_not_supported():
2828
user = UserDomain(id=123, name="carlo", email="mail_carlo")
29-
todo = TodoDomain(description="todo_carlo", user=user)
29+
domain = InputDomain(description="todo_carlo", user=user)
3030

31-
mapper.add(TodoDomain, TodoModel)
32-
todo_model: TodoModel = mapper.map(todo)
31+
mapper.add(InputDomain, OutputModel)
32+
model_with_none_user_id: OutputModel = mapper.map(domain)
3333

3434
# Implicit field mapping between parent and child objects is not supported
35-
# TodoDomain.user.user_id should not map to TodoModel.user_id implicitly
36-
assert todo_model.user_id is None
35+
# InputDomain.user.user_id should not map to OutputModel.user_id implicitly
36+
assert model_with_none_user_id.user_id is None
3737

3838
# Workaround: use explicit mapping
39-
todo_model1: TodoModel = mapper.map(todo, fields_mapping={"user_id": todo.user.id})
39+
todo_model_complete: OutputModel = mapper.map(
40+
domain, fields_mapping={"user_id": domain.user.id}
41+
)
4042

41-
assert todo_model1.user_id == 123
43+
assert todo_model_complete.user_id == 123

0 commit comments

Comments
 (0)