From 6e4fff5be42cd88cc71fc001189692c3c3aa4b6f Mon Sep 17 00:00:00 2001 From: Stefan van der Walt Date: Tue, 17 May 2022 13:10:40 -0700 Subject: [PATCH 1/2] Avoid conflicts when function is implemented in same-named submodule --- .github/workflows/test.yml | 2 +- .pre-commit-config.yaml | 2 +- lazy_loader/__init__.py | 14 ++++++++++++-- tests/fake_pkg/__init__.py | 5 +++++ tests/fake_pkg/some_func.py | 3 +++ tests/test_lazy_loader.py | 9 +++++++++ 6 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 tests/fake_pkg/__init__.py create mode 100644 tests/fake_pkg/some_func.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cc76b77..7f7c1d1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -34,4 +34,4 @@ jobs: - name: Test run: | - pytest + PYTHONPATH=. pytest diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fbca482..29ef555 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/psf/black - rev: 22.1.0 + rev: 22.3.0 hooks: - id: black - repo: https://gitlab.com/pycqa/flake8 diff --git a/lazy_loader/__init__.py b/lazy_loader/__init__.py index b1962af..08896b7 100644 --- a/lazy_loader/__init__.py +++ b/lazy_loader/__init__.py @@ -71,8 +71,18 @@ def __getattr__(name): if name in submodules: return importlib.import_module(f"{package_name}.{name}") elif name in attr_to_modules: - submod = importlib.import_module(f"{package_name}.{attr_to_modules[name]}") - return getattr(submod, name) + submod_path = f"{package_name}.{attr_to_modules[name]}" + submod = importlib.import_module(submod_path) + attr = getattr(submod, name) + + # If the attribute lives in a file (module) with the same + # name as the attribute, ensure that the attribute and *not* + # the module is accessible on the package. + if name == attr_to_modules[name]: + pkg = sys.modules[package_name] + pkg.__dict__[name] = attr + + return attr else: raise AttributeError(f"No {package_name} attribute {name}") diff --git a/tests/fake_pkg/__init__.py b/tests/fake_pkg/__init__.py new file mode 100644 index 0000000..540fd73 --- /dev/null +++ b/tests/fake_pkg/__init__.py @@ -0,0 +1,5 @@ +import lazy_loader as lazy + +__getattr__, __lazy_dir__, __all__ = lazy.attach( + __name__, submod_attrs={"some_func": ["some_func"]} +) diff --git a/tests/fake_pkg/some_func.py b/tests/fake_pkg/some_func.py new file mode 100644 index 0000000..10e99ed --- /dev/null +++ b/tests/fake_pkg/some_func.py @@ -0,0 +1,3 @@ +def some_func(): + """Function with same name as submodule.""" + pass diff --git a/tests/test_lazy_loader.py b/tests/test_lazy_loader.py index f74009f..bd8bab8 100644 --- a/tests/test_lazy_loader.py +++ b/tests/test_lazy_loader.py @@ -94,3 +94,12 @@ def test_lazy_attach(): for k, v in expected.items(): if v is not None: assert locls[k] == v + + +def test_attach_same_module_and_attr_name(): + import fake_pkg + + # Grab attribute twice, to ensure that importing it does not + # override function by module + assert isinstance(fake_pkg.some_func, types.FunctionType) + assert isinstance(fake_pkg.some_func, types.FunctionType) From b3d4f87875f14c8d4b3ac0f6c0643f4d6fbce182 Mon Sep 17 00:00:00 2001 From: Stefan van der Walt Date: Thu, 19 May 2022 10:24:10 -0700 Subject: [PATCH 2/2] Ensure that function import directly from submodule still works --- tests/test_lazy_loader.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_lazy_loader.py b/tests/test_lazy_loader.py index bd8bab8..d1ee06c 100644 --- a/tests/test_lazy_loader.py +++ b/tests/test_lazy_loader.py @@ -103,3 +103,8 @@ def test_attach_same_module_and_attr_name(): # override function by module assert isinstance(fake_pkg.some_func, types.FunctionType) assert isinstance(fake_pkg.some_func, types.FunctionType) + + # Ensure imports from submodule still work + from fake_pkg.some_func import some_func + + assert isinstance(some_func, types.FunctionType)