Skip to content

Commit 7265811

Browse files
committed
feat: Allow lambda functions in mappings
1 parent 0e91f49 commit 7265811

File tree

2 files changed

+21
-3
lines changed

2 files changed

+21
-3
lines changed

automapper/mapper.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,15 @@
2828
T = TypeVar("T")
2929
ClassifierFunction = Callable[[Type[T]], bool]
3030
SpecFunction = Callable[[Type[T]], Iterable[str]]
31-
FieldsMap = Optional[Dict[str, Any]]
31+
FieldsMap = Optional[Dict[str, Union[Callable[[S], Any], Any]]]
3232

3333

3434
def _try_get_field_value(
3535
field_name: str, original_obj: Any, custom_mapping: FieldsMap
3636
) -> Tuple[bool, Any]:
37-
if field_name in (custom_mapping or {}):
37+
if field_name in (custom_mapping or {}): # type: ignore [index]
38+
if callable(custom_mapping[field_name]): # type: ignore [index]
39+
return True, custom_mapping[field_name](original_obj) # type: ignore [index]
3840
return True, custom_mapping[field_name] # type: ignore [index]
3941
if hasattr(original_obj, field_name):
4042
return True, getattr(original_obj, field_name)
@@ -184,7 +186,8 @@ def map(
184186
obj (object): Source object to map to `target class`.
185187
skip_none_values (bool, optional): Skip None values when creating `target class` obj. Defaults to False.
186188
fields_mapping (FieldsMap, optional): Custom mapping.
187-
Specify dictionary in format {"field_name": value_object}. Defaults to None.
189+
Specify dictionary in format {"field_name": value_object | lambda soure_obj}. Can take a lamdba
190+
funtion as argument, that will get the source_cls as argument. Defaults to None.
188191
use_deepcopy (bool, optional): Apply deepcopy to all child objects when copy from source to target object.
189192
Defaults to True.
190193

tests/test_predefined_mapping.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ def __init__(self, text: Optional[str], num: int) -> None:
3030
self.text = text
3131
self.num = num
3232

33+
class ComplexClass:
34+
def __init__(self, text: Optional[str], num: int) -> None:
35+
self.data = AnotherClass(text, num)
3336

3437
class ClassWithoutInitAttrDef:
3538
def __init__(self, **kwargs: Any) -> None:
@@ -138,3 +141,15 @@ def test_map__pass_none_values_from_source_object(self):
138141
assert "num" in obj.data
139142
assert obj.data.get("text") is None
140143
assert obj.data.get("num") == 11
144+
145+
def test_add__lambda_resolver_works_with_lambda_function(self):
146+
self.mapper.add(ComplexClass, AnotherClass, fields_mapping={
147+
"text": lambda x: x.data.text.upper(),
148+
"num": lambda x: x.data.num * 2
149+
})
150+
result: AnotherClass = self.mapper.map(ComplexClass("test_message", 10))
151+
152+
assert isinstance(result, AnotherClass)
153+
assert result.text == "TEST_MESSAGE"
154+
assert result.num == 20
155+

0 commit comments

Comments
 (0)