Skip to content

Commit 1fcd29f

Browse files
aarnphmhynek
andauthored
Add optional namespace arguments for make_class (#1203)
* feat(make_class): add optional namespace arguments to pass through to `make_class` Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com> * chore: add changelog Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com> * Rename `namespaces` to `class_body` Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com> * Fix versionchanged for correct features Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com> * Correctly name test case Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com> * Update changelog Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com> * Adjust changelog --------- Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com> Co-authored-by: Hynek Schlawack <hs@ox.cx>
1 parent d40d9f3 commit 1fcd29f

File tree

4 files changed

+23
-1
lines changed

4 files changed

+23
-1
lines changed

changelog.d/1203.change.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Added *class_body* argument to `attrs.make_class()` to provide additional attributes for newly created classes.
2+
It is, for example, now possible to attach methods.

src/attr/__init__.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,7 @@ def make_class(
490490
name: str,
491491
attrs: Union[List[str], Tuple[str, ...], Dict[str, Any]],
492492
bases: Tuple[type, ...] = ...,
493+
class_body: Optional[Dict[str, Any]] = ...,
493494
repr_ns: Optional[str] = ...,
494495
repr: bool = ...,
495496
cmp: Optional[_EqOrderType] = ...,

src/attr/_make.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2867,7 +2867,9 @@ def __setstate__(self, state):
28672867
Factory = _add_hash(_add_eq(_add_repr(Factory, attrs=_f), attrs=_f), attrs=_f)
28682868

28692869

2870-
def make_class(name, attrs, bases=(object,), **attributes_arguments):
2870+
def make_class(
2871+
name, attrs, bases=(object,), class_body=None, **attributes_arguments
2872+
):
28712873
r"""
28722874
A quick way to create a new class called *name* with *attrs*.
28732875
@@ -2883,13 +2885,16 @@ def make_class(name, attrs, bases=(object,), **attributes_arguments):
28832885
28842886
:param tuple bases: Classes that the new class will subclass.
28852887
2888+
:param dict class_body: An optional dictionary of class attributes for the new class.
2889+
28862890
:param attributes_arguments: Passed unmodified to `attr.s`.
28872891
28882892
:return: A new class with *attrs*.
28892893
:rtype: type
28902894
28912895
.. versionadded:: 17.1.0 *bases*
28922896
.. versionchanged:: 18.1.0 If *attrs* is ordered, the order is retained.
2897+
.. versionchanged:: 23.2.0 *class_body*
28932898
"""
28942899
if isinstance(attrs, dict):
28952900
cls_dict = attrs
@@ -2904,6 +2909,8 @@ def make_class(name, attrs, bases=(object,), **attributes_arguments):
29042909
user_init = cls_dict.pop("__init__", None)
29052910

29062911
body = {}
2912+
if class_body is not None:
2913+
body.update(class_body)
29072914
if pre_init is not None:
29082915
body["__attrs_pre_init__"] = pre_init
29092916
if post_init is not None:

tests/test_make.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,6 +1106,18 @@ class D:
11061106
assert D in cls.__mro__
11071107
assert isinstance(cls(), D)
11081108

1109+
def test_additional_class_body(self):
1110+
"""
1111+
Additional class_body is added to newly created class.
1112+
"""
1113+
1114+
def echo_func(cls, *args):
1115+
return args
1116+
1117+
cls = make_class("C", {}, class_body={"echo": classmethod(echo_func)})
1118+
1119+
assert ("a", "b") == cls.echo("a", "b")
1120+
11091121
def test_clean_class(self, slots):
11101122
"""
11111123
Attribute definitions do not appear on the class body.

0 commit comments

Comments
 (0)