Skip to content

Commit

Permalink
Add optional namespace arguments for make_class (#1203)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
aarnphm and hynek authored Nov 28, 2023
1 parent d40d9f3 commit 1fcd29f
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 1 deletion.
2 changes: 2 additions & 0 deletions changelog.d/1203.change.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Added *class_body* argument to `attrs.make_class()` to provide additional attributes for newly created classes.
It is, for example, now possible to attach methods.
1 change: 1 addition & 0 deletions src/attr/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,7 @@ def make_class(
name: str,
attrs: Union[List[str], Tuple[str, ...], Dict[str, Any]],
bases: Tuple[type, ...] = ...,
class_body: Optional[Dict[str, Any]] = ...,
repr_ns: Optional[str] = ...,
repr: bool = ...,
cmp: Optional[_EqOrderType] = ...,
Expand Down
9 changes: 8 additions & 1 deletion src/attr/_make.py
Original file line number Diff line number Diff line change
Expand Up @@ -2867,7 +2867,9 @@ def __setstate__(self, state):
Factory = _add_hash(_add_eq(_add_repr(Factory, attrs=_f), attrs=_f), attrs=_f)


def make_class(name, attrs, bases=(object,), **attributes_arguments):
def make_class(
name, attrs, bases=(object,), class_body=None, **attributes_arguments
):
r"""
A quick way to create a new class called *name* with *attrs*.
Expand All @@ -2883,13 +2885,16 @@ def make_class(name, attrs, bases=(object,), **attributes_arguments):
:param tuple bases: Classes that the new class will subclass.
:param dict class_body: An optional dictionary of class attributes for the new class.
:param attributes_arguments: Passed unmodified to `attr.s`.
:return: A new class with *attrs*.
:rtype: type
.. versionadded:: 17.1.0 *bases*
.. versionchanged:: 18.1.0 If *attrs* is ordered, the order is retained.
.. versionchanged:: 23.2.0 *class_body*
"""
if isinstance(attrs, dict):
cls_dict = attrs
Expand All @@ -2904,6 +2909,8 @@ def make_class(name, attrs, bases=(object,), **attributes_arguments):
user_init = cls_dict.pop("__init__", None)

body = {}
if class_body is not None:
body.update(class_body)
if pre_init is not None:
body["__attrs_pre_init__"] = pre_init
if post_init is not None:
Expand Down
12 changes: 12 additions & 0 deletions tests/test_make.py
Original file line number Diff line number Diff line change
Expand Up @@ -1106,6 +1106,18 @@ class D:
assert D in cls.__mro__
assert isinstance(cls(), D)

def test_additional_class_body(self):
"""
Additional class_body is added to newly created class.
"""

def echo_func(cls, *args):
return args

cls = make_class("C", {}, class_body={"echo": classmethod(echo_func)})

assert ("a", "b") == cls.echo("a", "b")

def test_clean_class(self, slots):
"""
Attribute definitions do not appear on the class body.
Expand Down

0 comments on commit 1fcd29f

Please sign in to comment.