Skip to content

Commit 75723b7

Browse files
Accept valid unicode identifiers as class parameter to make_class (#1406)
* Accept valid unicode identifiers as class parameter to make_class Related to #1404 * Test code style * Add changelog --------- Co-authored-by: Hynek Schlawack <hs@ox.cx>
1 parent 5820ce7 commit 75723b7

File tree

3 files changed

+49
-0
lines changed

3 files changed

+49
-0
lines changed

changelog.d/1406.change.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
`attrs.make_class()` now allows for Unicode class names.

src/attr/_make.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import sys
1414
import types
1515
import typing
16+
import unicodedata
1617

1718
from operator import itemgetter
1819

@@ -2907,7 +2908,11 @@ def make_class(
29072908
.. versionadded:: 17.1.0 *bases*
29082909
.. versionchanged:: 18.1.0 If *attrs* is ordered, the order is retained.
29092910
.. versionchanged:: 23.2.0 *class_body*
2911+
.. versionchanged:: 25.2.0 Class names can now be unicode.
29102912
"""
2913+
# Class identifiers are converted into the normal form NFKC while parsing
2914+
name = unicodedata.normalize("NFKC", name)
2915+
29112916
if isinstance(attrs, dict):
29122917
cls_dict = attrs
29132918
elif isinstance(attrs, (list, tuple)):

tests/test_make.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import inspect
1111
import itertools
1212
import sys
13+
import unicodedata
1314

1415
from operator import attrgetter
1516
from typing import Generic, TypeVar
@@ -1100,6 +1101,48 @@ def test_attr_args(self):
11001101

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

1104+
def test_normalized_unicode_attr_args(self):
1105+
"""
1106+
Unicode identifiers are valid in Python.
1107+
"""
1108+
clsname = "ü"
1109+
1110+
assert clsname == unicodedata.normalize("NFKC", clsname)
1111+
1112+
attrname = "ß"
1113+
1114+
assert attrname == unicodedata.normalize("NFKC", attrname)
1115+
1116+
C = make_class(clsname, [attrname], repr=False)
1117+
1118+
assert repr(C(1)).startswith("<tests.test_make.ü object at 0x")
1119+
1120+
kwargs = {"ß": 1}
1121+
c = C(**kwargs)
1122+
1123+
assert 1 == c.ß
1124+
1125+
def test_unnormalized_unicode_attr_args(self):
1126+
"""
1127+
Unicode identifiers are normalized to NFKC form in Python.
1128+
"""
1129+
1130+
clsname = "Ŀ"
1131+
1132+
assert clsname != unicodedata.normalize("NFKC", clsname)
1133+
1134+
attrname = "ㅁ"
1135+
1136+
assert attrname != unicodedata.normalize("NFKC", attrname)
1137+
1138+
C = make_class(clsname, [attrname], repr=False)
1139+
assert repr(C(1)).startswith("<tests.test_make.L· object at 0x")
1140+
1141+
kwargs = {unicodedata.normalize("NFKC", attrname): 1}
1142+
c = C(**kwargs)
1143+
1144+
assert 1 == c.
1145+
11031146
def test_catches_wrong_attrs_type(self):
11041147
"""
11051148
Raise `TypeError` if an invalid type for attrs is passed.

0 commit comments

Comments
 (0)