Skip to content

Commit c9e5ed3

Browse files
committed
feat(auth): add subclass and instance checks for lazy bases and fix tests
1 parent c39064b commit c9e5ed3

3 files changed

Lines changed: 62 additions & 0 deletions

File tree

packages/google-auth/google/auth/_helpers.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,14 @@ def __getattribute__(cls, name):
571571
type(cls)._resolve_bases(cls)
572572
return super(LazyBasesMeta, cls).__getattribute__(name)
573573

574+
def __instancecheck__(cls, instance):
575+
type(cls)._resolve_bases(cls)
576+
return super(LazyBasesMeta, cls).__instancecheck__(instance)
577+
578+
def __subclasscheck__(cls, subclass):
579+
type(cls)._resolve_bases(cls)
580+
return super(LazyBasesMeta, cls).__subclasscheck__(subclass)
581+
574582
def _resolve_bases(cls):
575583
try:
576584
resolved = type.__getattribute__(cls, "_lazy_bases_resolved")

packages/google-auth/tests/conftest.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,17 @@
1616
import sys
1717
from unittest import mock
1818

19+
# Clear mTLS environment variables to ensure local environment settings do not leak
20+
# and affect import-time configurations of tested modules.
21+
os.environ.pop("GOOGLE_API_CERTIFICATE_CONFIG", None)
22+
os.environ.pop("CLOUDSDK_CONTEXT_AWARE_USE_CLIENT_CERTIFICATE", None)
23+
os.environ.pop("GOOGLE_API_USE_CLIENT_CERTIFICATE", None)
24+
os.environ.pop("GOOGLE_API_USE_MTLS_ENDPOINT", None)
25+
1926
import pytest # type: ignore
2027

2128

29+
2230
def pytest_configure():
2331
"""Load public certificate and private key."""
2432
pytest.data_dir = os.path.join(os.path.dirname(__file__), "data")

packages/google-auth/tests/test__helpers.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,3 +697,49 @@ def test_out_of_range(self):
697697
with pytest.raises((OverflowError, OSError, ValueError)):
698698
# Large enough to fail on most systems (Year 300,000+)
699699
_helpers.utcfromtimestamp(9999999999999)
700+
701+
702+
class TestLazyBasesMeta:
703+
def test_subclass_and_instance_check(self):
704+
class RealBase(object):
705+
pass
706+
707+
class DummyBase(_helpers.HeapDummy):
708+
pass
709+
710+
class LazyClass(DummyBase, metaclass=_helpers.LazyBasesMeta):
711+
pass
712+
713+
class LazyClassMeta(_helpers.LazyBasesMeta):
714+
def _perform_resolve_bases(cls):
715+
cls.__bases__ = (RealBase,)
716+
717+
# Re-define LazyClass with the proper metaclass that resolves to RealBase
718+
class LazyClassResolved(DummyBase, metaclass=LazyClassMeta):
719+
pass
720+
721+
# Verify initial bases
722+
assert type.__getattribute__(LazyClassResolved, "__bases__") == (DummyBase,)
723+
724+
# Trigger subclass check where lazy class is the second argument (supported)
725+
# This should resolve the bases of LazyClassResolved
726+
assert not issubclass(int, LazyClassResolved)
727+
728+
# Verify bases are resolved
729+
assert type.__getattribute__(LazyClassResolved, "__bases__") == (RealBase,)
730+
731+
# Now the first-argument subclass check should pass
732+
assert issubclass(LazyClassResolved, RealBase)
733+
734+
# Reset resolved state for instance check test
735+
class LazyClassForInstance(DummyBase, metaclass=LazyClassMeta):
736+
pass
737+
738+
assert type.__getattribute__(LazyClassForInstance, "__bases__") == (DummyBase,)
739+
740+
# Trigger instance check (supported) to resolve the bases of LazyClassForInstance
741+
assert not isinstance(123, LazyClassForInstance)
742+
743+
# Verify bases are resolved
744+
assert type.__getattribute__(LazyClassForInstance, "__bases__") == (RealBase,)
745+

0 commit comments

Comments
 (0)