Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add optional namespace arguments for make_class #1203

Merged
merged 8 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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