Skip to content

Commit

Permalink
Support PEP 695 type aliases
Browse files Browse the repository at this point in the history
  • Loading branch information
oxan committed Oct 19, 2023
1 parent c82da5b commit 9d8f931
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 4 deletions.
11 changes: 7 additions & 4 deletions rest_framework_dataclasses/typing_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ def get_resolved_type_hints(tp: type) -> typing.Dict[str, type]:
# typing.get_type_hints() does the heavy lifting for us, except:
# - when using PEP 585 generic types that contain a stringified type hint, on Python 3.9 and 3.10. See
# https://bugs.python.org/issue41370
# - when using PEP 695 type aliases
def _resolve_type(context_type: type, resolve_type: typing.Union[str, type]) -> type:
if isinstance(resolve_type, str):
globalsns = sys.modules[context_type.__module__].__dict__
Expand All @@ -67,12 +68,14 @@ def _resolve_type(context_type: type, resolve_type: typing.Union[str, type]) ->
return _resolve_type_hint(context_type, resolve_type)

def _resolve_type_hint(context_type: type, resolve_type: type) -> type:
if not hasattr(types, 'GenericAlias') or not isinstance(resolve_type, types.GenericAlias):
if hasattr(types, 'GenericAlias') and isinstance(resolve_type, types.GenericAlias):
args = tuple(_resolve_type(context_type, arg) for arg in resolve_type.__args__)
return typing.cast(type, types.GenericAlias(resolve_type.__origin__, args))
elif hasattr(typing, 'TypeAliasType') and isinstance(resolve_type, typing.TypeAliasType):
return _resolve_type_hint(context_type, resolve_type.__value__)
else:
return resolve_type

args = tuple(_resolve_type(context_type, arg) for arg in resolve_type.__args__)
return typing.cast(type, types.GenericAlias(resolve_type.__origin__, args))

return {k: _resolve_type_hint(tp, v) for k, v in typing.get_type_hints(tp).items()}


Expand Down
21 changes: 21 additions & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import unittest
import sys


def load_tests(loader: unittest.TestLoader, tests, pattern):
# Manually load tests to avoid loading tests with syntax that's incompatible with the current Python version
for module in (
'test_field_generation',
'test_field_utils',
'test_fields',
'test_functional',
'test_issues',
'test_serializers',
'test_typing_utils',
):
tests.addTests(loader.loadTestsFromName('tests.' + module))

if sys.version_info >= (3, 12, 0):
tests.addTests(loader.loadTestsFromName('tests.test_py312'))

return tests
23 changes: 23 additions & 0 deletions tests/test_py312.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import typing
import unittest
import sys

from rest_framework_dataclasses import typing_utils


@unittest.skipIf(sys.version_info < (3, 12, 0), 'Python 3.12 required')
class Python312Test(unittest.TestCase):
def test_resolve_pep695(self):
type Str = str
type StrList = list[str]
type GenericList[T] = list[T]

class Hinted:
a: Str
b: StrList
c: GenericList

hints = typing_utils.get_resolved_type_hints(Hinted)
self.assertEqual(hints['a'], str)
self.assertEqual(hints['b'], list[str])
self.assertEqual(typing.get_origin(hints['c']), list)

0 comments on commit 9d8f931

Please sign in to comment.