Skip to content

Commit d147ea1

Browse files
authored
Handle types.GenericAlias types properly during validation (#27633)
1 parent 891349c commit d147ea1

3 files changed

Lines changed: 23 additions & 4 deletions

File tree

sdks/python/apache_beam/typehints/native_type_compatibility_test.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ class _TestGeneric(typing.Generic[T]):
4949
pass
5050

5151

52+
class _TestPair(typing.NamedTuple('TestTuple', [('first', T), ('second', T)]),
53+
typing.Generic[T]):
54+
pass
55+
56+
5257
class NativeTypeCompatibilityTest(unittest.TestCase):
5358
def test_convert_to_beam_type(self):
5459
test_cases = [
@@ -99,6 +104,8 @@ def test_convert_to_beam_type(self):
99104
typehints.List[_TestGeneric]),
100105
('nested generic subscripted', typing.List[_TestGeneric[int]],
101106
typehints.List[_TestGeneric[int]]),
107+
('nested generic with any', typing.List[_TestPair[typing.Any]],
108+
typehints.List[_TestPair[typing.Any]]),
102109
]
103110

104111
for test_case in test_cases:

sdks/python/apache_beam/typehints/typehints.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -355,11 +355,12 @@ def is_typing_generic(type_param):
355355
356356
Such objects are considered valid type parameters.
357357
358-
Always returns false for Python versions below 3.7.
358+
For Python versions 3.9 and above, also permits types.GenericAlias.
359359
"""
360-
if hasattr(typing, '_GenericAlias'):
361-
return isinstance(type_param, typing._GenericAlias)
362-
return False
360+
if hasattr(types, "GenericAlias") and isinstance(type_param,
361+
types.GenericAlias):
362+
return True
363+
return isinstance(type_param, typing._GenericAlias)
363364

364365

365366
def validate_composite_type_param(type_param, error_msg_prefix):

sdks/python/apache_beam/typehints/typehints_test.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,13 @@ def test_builtin_and_type_compatibility(self):
619619
self.assertCompatible(list, typing.List)
620620
self.assertCompatible(list[int], typing.List[int])
621621

622+
def test_is_typing_generic(self):
623+
self.assertTrue(typehints.is_typing_generic(typing.List[str]))
624+
625+
def test_builtin_is_typing_generic(self):
626+
if sys.version_info >= (3, 9):
627+
self.assertTrue(typehints.is_typing_generic(list[str]))
628+
622629

623630
class KVHintTestCase(TypeHintTestCase):
624631
def test_getitem_param_must_be_tuple(self):
@@ -791,6 +798,10 @@ def test_getitem_invalid_composite_type_param(self):
791798
except TypeError:
792799
self.fail("built-in composite raised TypeError unexpectedly")
793800

801+
def test_non_typing_generic(self):
802+
testCase = DummyTestClass1()
803+
self.assertFalse(typehints.is_typing_generic(testCase))
804+
794805
def test_compatibility(self):
795806
hint1 = self.beam_type[typehints.List[str]]
796807
hint2 = self.beam_type[typehints.Tuple[int, int]]

0 commit comments

Comments
 (0)