Skip to content
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
1 change: 1 addition & 0 deletions changelog.d/1406.change.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
`attrs.make_class()` now allows for Unicode class names.
5 changes: 5 additions & 0 deletions src/attr/_make.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import sys
import types
import typing
import unicodedata

from operator import itemgetter

Expand Down Expand Up @@ -2907,7 +2908,11 @@ def make_class(
.. versionadded:: 17.1.0 *bases*
.. versionchanged:: 18.1.0 If *attrs* is ordered, the order is retained.
.. versionchanged:: 23.2.0 *class_body*
.. versionchanged:: 25.2.0 Class names can now be unicode.
"""
# Class identifiers are converted into the normal form NFKC while parsing
name = unicodedata.normalize("NFKC", name)

if isinstance(attrs, dict):
cls_dict = attrs
elif isinstance(attrs, (list, tuple)):
Expand Down
43 changes: 43 additions & 0 deletions tests/test_make.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import inspect
import itertools
import sys
import unicodedata

from operator import attrgetter
from typing import Generic, TypeVar
Expand Down Expand Up @@ -1100,6 +1101,48 @@ def test_attr_args(self):

assert repr(C(1)).startswith("<tests.test_make.C object at 0x")

def test_normalized_unicode_attr_args(self):
"""
Unicode identifiers are valid in Python.
"""
clsname = "ü"

assert clsname == unicodedata.normalize("NFKC", clsname)

attrname = "ß"

assert attrname == unicodedata.normalize("NFKC", attrname)

C = make_class(clsname, [attrname], repr=False)

assert repr(C(1)).startswith("<tests.test_make.ü object at 0x")

kwargs = {"ß": 1}
c = C(**kwargs)

assert 1 == c.ß

def test_unnormalized_unicode_attr_args(self):
"""
Unicode identifiers are normalized to NFKC form in Python.
"""

clsname = "Ŀ"

assert clsname != unicodedata.normalize("NFKC", clsname)

attrname = "ㅁ"

assert attrname != unicodedata.normalize("NFKC", attrname)

C = make_class(clsname, [attrname], repr=False)
assert repr(C(1)).startswith("<tests.test_make.L· object at 0x")

kwargs = {unicodedata.normalize("NFKC", attrname): 1}
c = C(**kwargs)

assert 1 == c.ㅁ

def test_catches_wrong_attrs_type(self):
"""
Raise `TypeError` if an invalid type for attrs is passed.
Expand Down