From 2ec61e154747fe5d9f53a8b7402cbc5addfdaf4b Mon Sep 17 00:00:00 2001 From: Jack McCluskey Date: Mon, 18 Aug 2025 11:51:17 -0400 Subject: [PATCH 1/4] Introduce Beartype Dependency for Type Checking --- sdks/python/apache_beam/typehints/typehints.py | 7 ++++++- sdks/python/setup.py | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/sdks/python/apache_beam/typehints/typehints.py b/sdks/python/apache_beam/typehints/typehints.py index 3b6afcc0a116..a9f04108113d 100644 --- a/sdks/python/apache_beam/typehints/typehints.py +++ b/sdks/python/apache_beam/typehints/typehints.py @@ -72,6 +72,8 @@ import typing from collections import abc +from beartype.door import is_subhint + __all__ = [ 'Any', 'Union', @@ -1522,7 +1524,10 @@ def is_consistent_with(sub, base): # Cannot check unsupported parameterized generic which will cause issubclass # to fail with an exception. return False - return issubclass(sub, base) + try: + return is_subhint(sub, base) + except: + return False def regex_consistency(sub, base) -> bool: diff --git a/sdks/python/setup.py b/sdks/python/setup.py index 9702ba4746ea..b88034174804 100644 --- a/sdks/python/setup.py +++ b/sdks/python/setup.py @@ -401,6 +401,7 @@ def get_portability_package_data(): 'typing-extensions>=3.7.0', 'zstandard>=0.18.0,<1', 'pyyaml>=3.12,<7.0.0', + 'beartype>=0.21.0,<0.22.0', # Dynamic dependencies must be specified in a separate list, otherwise # Dependabot won't be able to parse the main list. Any dynamic # dependencies will not receive updates from Dependabot. From d92eb60d77a85b88a63b2c66f27a411c879fd36d Mon Sep 17 00:00:00 2001 From: jrmccluskey Date: Mon, 18 Aug 2025 17:15:13 +0000 Subject: [PATCH 2/4] update container requirements --- sdks/python/container/ml/py310/base_image_requirements.txt | 5 +++-- sdks/python/container/ml/py311/base_image_requirements.txt | 5 +++-- sdks/python/container/ml/py312/base_image_requirements.txt | 5 +++-- sdks/python/container/ml/py39/base_image_requirements.txt | 5 +++-- sdks/python/container/py310/base_image_requirements.txt | 5 +++-- sdks/python/container/py311/base_image_requirements.txt | 5 +++-- sdks/python/container/py312/base_image_requirements.txt | 5 +++-- sdks/python/container/py313/base_image_requirements.txt | 7 ++++--- sdks/python/container/py39/base_image_requirements.txt | 5 +++-- 9 files changed, 28 insertions(+), 19 deletions(-) diff --git a/sdks/python/container/ml/py310/base_image_requirements.txt b/sdks/python/container/ml/py310/base_image_requirements.txt index d5849d98a9eb..ba2ee0d85340 100644 --- a/sdks/python/container/ml/py310/base_image_requirements.txt +++ b/sdks/python/container/ml/py310/base_image_requirements.txt @@ -33,6 +33,7 @@ astunparse==1.6.3 async-timeout==5.0.1 attrs==25.3.0 backports.tarfile==1.2.0 +beartype==0.21.0 beautifulsoup4==4.13.4 bs4==0.0.2 build==1.3.0 @@ -191,7 +192,7 @@ pytest-timeout==2.4.0 pytest-xdist==3.8.0 python-dateutil==2.9.0.post0 python-dotenv==1.1.1 -python-tds==1.16.1 +python-tds==1.17.0 pytz==2025.2 PyYAML==6.0.2 redis==5.3.1 @@ -244,4 +245,4 @@ wheel==0.45.1 wrapt==1.17.3 yarl==1.20.1 zipp==3.23.0 -zstandard==0.23.0 +zstandard==0.24.0 diff --git a/sdks/python/container/ml/py311/base_image_requirements.txt b/sdks/python/container/ml/py311/base_image_requirements.txt index 88b9d111496a..15a4050ab0f3 100644 --- a/sdks/python/container/ml/py311/base_image_requirements.txt +++ b/sdks/python/container/ml/py311/base_image_requirements.txt @@ -32,6 +32,7 @@ asn1crypto==1.5.1 astunparse==1.6.3 attrs==25.3.0 backports.tarfile==1.2.0 +beartype==0.21.0 beautifulsoup4==4.13.4 bs4==0.0.2 build==1.3.0 @@ -189,7 +190,7 @@ pytest-timeout==2.4.0 pytest-xdist==3.8.0 python-dateutil==2.9.0.post0 python-dotenv==1.1.1 -python-tds==1.16.1 +python-tds==1.17.0 pytz==2025.2 PyYAML==6.0.2 redis==5.3.1 @@ -241,4 +242,4 @@ wheel==0.45.1 wrapt==1.17.3 yarl==1.20.1 zipp==3.23.0 -zstandard==0.23.0 +zstandard==0.24.0 diff --git a/sdks/python/container/ml/py312/base_image_requirements.txt b/sdks/python/container/ml/py312/base_image_requirements.txt index e653b06f5ca1..488e4e27f486 100644 --- a/sdks/python/container/ml/py312/base_image_requirements.txt +++ b/sdks/python/container/ml/py312/base_image_requirements.txt @@ -31,6 +31,7 @@ anyio==4.10.0 asn1crypto==1.5.1 astunparse==1.6.3 attrs==25.3.0 +beartype==0.21.0 beautifulsoup4==4.13.4 bs4==0.0.2 build==1.3.0 @@ -188,7 +189,7 @@ pytest-timeout==2.4.0 pytest-xdist==3.8.0 python-dateutil==2.9.0.post0 python-dotenv==1.1.1 -python-tds==1.16.1 +python-tds==1.17.0 pytz==2025.2 PyYAML==6.0.2 redis==5.3.1 @@ -239,4 +240,4 @@ wheel==0.45.1 wrapt==1.17.3 yarl==1.20.1 zipp==3.23.0 -zstandard==0.23.0 +zstandard==0.24.0 diff --git a/sdks/python/container/ml/py39/base_image_requirements.txt b/sdks/python/container/ml/py39/base_image_requirements.txt index d1630d2463b0..3785f612a4af 100644 --- a/sdks/python/container/ml/py39/base_image_requirements.txt +++ b/sdks/python/container/ml/py39/base_image_requirements.txt @@ -33,6 +33,7 @@ astunparse==1.6.3 async-timeout==5.0.1 attrs==25.3.0 backports.tarfile==1.2.0 +beartype==0.21.0 beautifulsoup4==4.13.4 bs4==0.0.2 build==1.3.0 @@ -191,7 +192,7 @@ pytest-timeout==2.4.0 pytest-xdist==3.8.0 python-dateutil==2.9.0.post0 python-dotenv==1.1.1 -python-tds==1.16.1 +python-tds==1.17.0 pytz==2025.2 PyYAML==6.0.2 redis==5.3.1 @@ -244,4 +245,4 @@ wheel==0.45.1 wrapt==1.17.3 yarl==1.20.1 zipp==3.23.0 -zstandard==0.23.0 +zstandard==0.24.0 diff --git a/sdks/python/container/py310/base_image_requirements.txt b/sdks/python/container/py310/base_image_requirements.txt index fd4e424c6f54..3f4deee29713 100644 --- a/sdks/python/container/py310/base_image_requirements.txt +++ b/sdks/python/container/py310/base_image_requirements.txt @@ -31,6 +31,7 @@ asn1crypto==1.5.1 async-timeout==5.0.1 attrs==25.3.0 backports.tarfile==1.2.0 +beartype==0.21.0 beautifulsoup4==4.13.4 bs4==0.0.2 build==1.3.0 @@ -155,7 +156,7 @@ pytest-timeout==2.4.0 pytest-xdist==3.8.0 python-dateutil==2.9.0.post0 python-dotenv==1.1.1 -python-tds==1.16.1 +python-tds==1.17.0 pytz==2025.2 PyYAML==6.0.2 redis==5.3.1 @@ -194,4 +195,4 @@ wheel==0.45.1 wrapt==1.17.3 yarl==1.20.1 zipp==3.23.0 -zstandard==0.23.0 +zstandard==0.24.0 diff --git a/sdks/python/container/py311/base_image_requirements.txt b/sdks/python/container/py311/base_image_requirements.txt index a558a975c8ab..d67181c22ba7 100644 --- a/sdks/python/container/py311/base_image_requirements.txt +++ b/sdks/python/container/py311/base_image_requirements.txt @@ -30,6 +30,7 @@ anyio==4.10.0 asn1crypto==1.5.1 attrs==25.3.0 backports.tarfile==1.2.0 +beartype==0.21.0 beautifulsoup4==4.13.4 bs4==0.0.2 build==1.3.0 @@ -153,7 +154,7 @@ pytest-timeout==2.4.0 pytest-xdist==3.8.0 python-dateutil==2.9.0.post0 python-dotenv==1.1.1 -python-tds==1.16.1 +python-tds==1.17.0 pytz==2025.2 PyYAML==6.0.2 redis==5.3.1 @@ -191,4 +192,4 @@ wheel==0.45.1 wrapt==1.17.3 yarl==1.20.1 zipp==3.23.0 -zstandard==0.23.0 +zstandard==0.24.0 diff --git a/sdks/python/container/py312/base_image_requirements.txt b/sdks/python/container/py312/base_image_requirements.txt index f9b50f19aebf..35f29dbfa644 100644 --- a/sdks/python/container/py312/base_image_requirements.txt +++ b/sdks/python/container/py312/base_image_requirements.txt @@ -29,6 +29,7 @@ annotated-types==0.7.0 anyio==4.10.0 asn1crypto==1.5.1 attrs==25.3.0 +beartype==0.21.0 beautifulsoup4==4.13.4 bs4==0.0.2 build==1.3.0 @@ -152,7 +153,7 @@ pytest-timeout==2.4.0 pytest-xdist==3.8.0 python-dateutil==2.9.0.post0 python-dotenv==1.1.1 -python-tds==1.16.1 +python-tds==1.17.0 pytz==2025.2 PyYAML==6.0.2 redis==5.3.1 @@ -190,4 +191,4 @@ wheel==0.45.1 wrapt==1.17.3 yarl==1.20.1 zipp==3.23.0 -zstandard==0.23.0 +zstandard==0.24.0 diff --git a/sdks/python/container/py313/base_image_requirements.txt b/sdks/python/container/py313/base_image_requirements.txt index f9cd657b9486..fd7516f43295 100644 --- a/sdks/python/container/py313/base_image_requirements.txt +++ b/sdks/python/container/py313/base_image_requirements.txt @@ -29,6 +29,7 @@ annotated-types==0.7.0 anyio==4.10.0 asn1crypto==1.5.1 attrs==25.3.0 +beartype==0.21.0 beautifulsoup4==4.13.4 bs4==0.0.2 build==1.3.0 @@ -98,7 +99,7 @@ jeepney==0.9.0 Jinja2==3.1.6 joblib==1.5.1 jsonpickle==3.4.2 -jsonschema==4.25.0 +jsonschema==4.25.1 jsonschema-specifications==2025.4.1 keyring==25.6.0 keyrings.google-artifactregistry-auth==1.1.2 @@ -150,7 +151,7 @@ pytest-timeout==2.4.0 pytest-xdist==3.8.0 python-dateutil==2.9.0.post0 python-dotenv==1.1.1 -python-tds==1.16.1 +python-tds==1.17.0 pytz==2025.2 PyYAML==6.0.2 redis==5.3.1 @@ -187,4 +188,4 @@ wheel==0.45.1 wrapt==1.17.3 yarl==1.20.1 zipp==3.23.0 -zstandard==0.23.0 +zstandard==0.24.0 diff --git a/sdks/python/container/py39/base_image_requirements.txt b/sdks/python/container/py39/base_image_requirements.txt index 9956cc3028ca..423c66980410 100644 --- a/sdks/python/container/py39/base_image_requirements.txt +++ b/sdks/python/container/py39/base_image_requirements.txt @@ -31,6 +31,7 @@ asn1crypto==1.5.1 async-timeout==5.0.1 attrs==25.3.0 backports.tarfile==1.2.0 +beartype==0.21.0 beautifulsoup4==4.13.4 bs4==0.0.2 build==1.3.0 @@ -155,7 +156,7 @@ pytest-timeout==2.4.0 pytest-xdist==3.8.0 python-dateutil==2.9.0.post0 python-dotenv==1.1.1 -python-tds==1.16.1 +python-tds==1.17.0 pytz==2025.2 PyYAML==6.0.2 redis==5.3.1 @@ -194,4 +195,4 @@ wheel==0.45.1 wrapt==1.17.3 yarl==1.20.1 zipp==3.23.0 -zstandard==0.23.0 +zstandard==0.24.0 From 4d7edbeb3aef11224e735f6b1bedc9a1319eb2b5 Mon Sep 17 00:00:00 2001 From: Jack McCluskey Date: Mon, 18 Aug 2025 17:03:28 -0400 Subject: [PATCH 3/4] replace bare except case --- sdks/python/apache_beam/typehints/typehints.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sdks/python/apache_beam/typehints/typehints.py b/sdks/python/apache_beam/typehints/typehints.py index a9f04108113d..299ee043d3eb 100644 --- a/sdks/python/apache_beam/typehints/typehints.py +++ b/sdks/python/apache_beam/typehints/typehints.py @@ -73,6 +73,7 @@ from collections import abc from beartype.door import is_subhint +from beartype.roar import BeartypeDoorException __all__ = [ 'Any', @@ -1526,7 +1527,7 @@ def is_consistent_with(sub, base): return False try: return is_subhint(sub, base) - except: + except (BeartypeDoorException): return False From e98c3943fbd60030437f340cfb6b97b231845f62 Mon Sep 17 00:00:00 2001 From: Jack McCluskey Date: Wed, 20 Aug 2025 15:55:06 -0400 Subject: [PATCH 4/4] swap to optional beartype usage --- sdks/python/apache_beam/typehints/typehints.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/sdks/python/apache_beam/typehints/typehints.py b/sdks/python/apache_beam/typehints/typehints.py index 299ee043d3eb..54eef4ee1a1c 100644 --- a/sdks/python/apache_beam/typehints/typehints.py +++ b/sdks/python/apache_beam/typehints/typehints.py @@ -1486,7 +1486,7 @@ def normalize(x, none_as_type=False): }) -def is_consistent_with(sub, base): +def is_consistent_with(sub, base, use_beartype: bool = False) -> bool: """Checks whether sub a is consistent with base. This is according to the terminology of PEP 483/484. This relationship is @@ -1525,10 +1525,13 @@ def is_consistent_with(sub, base): # Cannot check unsupported parameterized generic which will cause issubclass # to fail with an exception. return False - try: - return is_subhint(sub, base) - except (BeartypeDoorException): - return False + if use_beartype: + try: + return is_subhint(sub, base) + except (BeartypeDoorException): + return False + else: + return issubclass(sub, base) def regex_consistency(sub, base) -> bool: