From 50ca4f18ca58254da06f84005d575044c4e2896a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20B=C3=B6hn?= Date: Thu, 11 Oct 2018 23:07:01 -0400 Subject: [PATCH 1/2] bpo-34963: Updated NewType according to latest PR notes ... also added appropriate tests --- Lib/test/test_typing.py | 19 ++++++++++ Lib/typing.py | 35 +++++++++++++------ .../2018-10-11-23-14-13.bpo-34963.fNy13b.rst | 3 ++ 3 files changed, 46 insertions(+), 11 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2018-10-11-23-14-13.bpo-34963.fNy13b.rst diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 6d8cc5319fb121..11ef0a49c71e41 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -2414,6 +2414,25 @@ def test_basic(self): self.assertIsInstance(UserName('Joe'), str) self.assertEqual(UserId(5) + 1, 6) + def test_definitions(self): + UserId = NewType('UserId', int) + UserName = NewType('UserName', str) + self.assertEqual(UserId.__name__, 'UserId') + self.assertEqual(UserId.__qualname__, 'UserId') + self.assertIs(UserId.__supertype__, int) + self.assertIsInstance(UserId, NewType) + self.assertEqual(UserName.__name__, 'UserName') + self.assertEqual(UserName.__qualname__, 'UserName') + self.assertIs(UserName.__supertype__, str) + self.assertIsInstance(UserName, NewType) + self.assertNotEqual(hash(UserId), hash(UserName)) + + def test_repr(self): + UserId = NewType('UserId', int) + UserName = NewType('UserName', str) + self.assertEqual(repr(UserId), "NewType") + self.assertEqual(repr(UserName), "NewType") + def test_errors(self): UserId = NewType('UserId', int) UserName = NewType('UserName', str) diff --git a/Lib/typing.py b/Lib/typing.py index cfcbb3b7632842..19f550625da3d7 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1409,12 +1409,13 @@ def __new__(self, typename, fields=None, **kwargs): " can be provided to NamedTuple, not both") return _make_nmtuple(typename, fields) +class NewType: -def NewType(name, tp): - """NewType creates simple unique types with almost zero - runtime overhead. NewType(name, tp) is considered a subtype of tp - by static type checkers. At runtime, NewType(name, tp) returns - a dummy function that simply returns its argument. Usage:: + """NewType creates simple unique types with almost zero runtime + overhead. `NewType(name, tp)` is considered a subtype of `tp` + by static type checkers. At runtime, NewType(name, tp) creates + a callable instance that simply returns its argument when called. + Usage:: UserId = NewType('UserId', int) @@ -1429,18 +1430,30 @@ def name_by_id(user_id: UserId) -> str: num = UserId(5) + 1 # type: int """ - def new_type(x): - return x + __slots__ = ('__name__', '__qualname__', '__supertype__') + + def __init__(self, name, tp): + self.__name__ = self.__qualname__ = name + self.__supertype__ = tp + + @staticmethod + def __call__(arg): + return arg - new_type.__name__ = name - new_type.__supertype__ = tp - return new_type + def __repr__(self): + """ NewType reprs are in the form: + “NewTyprClassName” + """ + return f"{type(self).__name__}<" \ + f"{self.__qualname__}:" \ + f"{self.__supertype__.__name__}>" + def __hash__(self): + return hash((self.__name__, self.__supertype__)) # Python-version-specific alias (Python 2: unicode; Python 3: str) Text = str - # Constant that's True when type checking, but False here. TYPE_CHECKING = False diff --git a/Misc/NEWS.d/next/Library/2018-10-11-23-14-13.bpo-34963.fNy13b.rst b/Misc/NEWS.d/next/Library/2018-10-11-23-14-13.bpo-34963.fNy13b.rst new file mode 100644 index 00000000000000..eed1b450fbdbc9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-10-11-23-14-13.bpo-34963.fNy13b.rst @@ -0,0 +1,3 @@ +Updated the `typing.NewType(…)` function to create real callable types – +This allows the generated types to furnish a real __repr__ function, while +retaining the same behavior (specifically, the near-zero runtime overhead). From 6a753398d74cb79099a4d8e88dc8eb7343e8c806 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20B=C3=B6hn?= Date: Tue, 16 Oct 2018 13:24:12 -0400 Subject: [PATCH 2/2] =?UTF-8?q?Corrected=20typos=20in=20NewType.=5F=5Frepr?= =?UTF-8?q?=5F=5F(=E2=80=A6)=20docstring?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Lib/typing.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/typing.py b/Lib/typing.py index 19f550625da3d7..7973a7c4aafc9d 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1442,7 +1442,8 @@ def __call__(arg): def __repr__(self): """ NewType reprs are in the form: - “NewTyprClassName” + “NewTClassName”, e.g.: + `NewType` """ return f"{type(self).__name__}<" \ f"{self.__qualname__}:" \