Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid conflicts when function is implemented in same-named submodule #9

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ jobs:

- name: Test
run: |
pytest
PYTHONPATH=. pytest
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 12 additions & 2 deletions lazy_loader/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}")

Expand Down
5 changes: 5 additions & 0 deletions tests/fake_pkg/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import lazy_loader as lazy

__getattr__, __lazy_dir__, __all__ = lazy.attach(
__name__, submod_attrs={"some_func": ["some_func"]}
)
3 changes: 3 additions & 0 deletions tests/fake_pkg/some_func.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
def some_func():
"""Function with same name as submodule."""
pass
14 changes: 14 additions & 0 deletions tests/test_lazy_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,17 @@ 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)
Comment on lines +104 to +105
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we test that from fake_pkg.some_func import some_func also works?

In codebases where this happens, some people do from fake_pkg import some_func and some do from fake_pkg.some_func import some_func and both work.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I added a test for that.


# Ensure imports from submodule still work
from fake_pkg.some_func import some_func

assert isinstance(some_func, types.FunctionType)