Skip to content

Commit e15a8e9

Browse files
committed
[feat] Bare import with path.to.module
1 parent 8d83281 commit e15a8e9

File tree

9 files changed

+54
-17
lines changed

9 files changed

+54
-17
lines changed

examples/aliases/package/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
am: lazimp.ModuleType
44

55
__getattr__ = lazimp.lazy_import(
6-
sub_import={'alias_module': 'package'},
7-
aliases={'am': 'alias_module'}
6+
aliases={'am': 'alias_module'},
7+
alias_module='package',
88
)

examples/bare_sub_import/main.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import package
2+
3+
print('Before access to package_2')
4+
print('load of bare_sub_module and bare_sub_module_2: ', package.package_2)
5+
print('After access to package_2')
6+
print('package_2.bare_sub_module: ', package.package_2.bare_sub_module)
7+
print('package_2.bare_sub_module_2: ',
8+
package.package_2.bare_sub_module_2)
9+
print(
10+
'package_2.bare_sub_module_3 access success because package_2 expose it: ',
11+
package.package_2.bare_sub_module_3)
12+
print(
13+
'package_2.bare_sub_module_4 access failed because package_2 does not '
14+
'expose it: ', package.package_2.bare_sub_module_4)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import lazimp
2+
3+
sub_package: lazimp.ModuleType
4+
5+
__getattr__ = lazimp.lazy_import('package_2.bare_sub_module',
6+
'package_2.bare_sub_module_2')

examples/bare_sub_import/package_2/__init__.py

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
print('bare_sub_module loaded')
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
print('bare_sub_module_2 loaded')
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
print('bare_sub_module_3 loaded')

examples/basic/package/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
math: lazimp.ModuleType
44
heavy_module: lazimp.ModuleType
55

6-
# __getattr__ = lazimp.lazy_import('math', 'package.heavy_module')
76
__getattr__ = lazimp.lazy_import(
87
'math',
98
heavy_module='package',

src/lazimp/__init__.py

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55

66
def lazy_import(
7-
*imports: str,
7+
*bare_imports: str,
88
aliases: collections.abc.Mapping[str, str] | None = None,
99
sub_import: collections.abc.Mapping[str, str] | None = None,
1010
**kw_sub_import: str,
@@ -30,18 +30,25 @@ def lazy_import(
3030
sub_import={'submodule2':'package'}
3131
)
3232
33-
:param imports: Any number of module names to import.
33+
:param bare_imports: Any number of module names to import.
3434
:param aliases: A mapping of alias names to module names.
3535
:param sub_import: A mapping of module names to package names.
3636
:param kw_sub_import: Keyword arguments of module names to package names.
3737
:return: A function that returns the imported modules.
3838
"""
39-
imports = set(imports)
4039
if aliases is None:
4140
aliases = {}
4241
if sub_import is None:
4342
sub_import = {}
44-
sub_import = sub_import | kw_sub_import
43+
44+
imports = {name for name in bare_imports if len(name.split('.')) == 1}
45+
top_level_import = collections.defaultdict(list)
46+
for import_path in bare_imports:
47+
root, *path = (import_path.split('.'))
48+
if path:
49+
top_level_import[root].append(import_path)
50+
51+
sub_import.update(kw_sub_import)
4552

4653
import functools
4754
import importlib
@@ -50,11 +57,6 @@ def lazy_import(
5057
def getattr_(name: str) -> ModuleType:
5158
name = aliases.get(name, name)
5259

53-
if name not in imports and name not in sub_import:
54-
raise AttributeError(
55-
f'Module has no attribute {name!r}'
56-
) from None
57-
5860
if name in imports:
5961
try:
6062
return importlib.import_module(name)
@@ -63,10 +65,23 @@ def getattr_(name: str) -> ModuleType:
6365
f'Module has no attribute {name!r}'
6466
) from e
6567

66-
try:
67-
return importlib.import_module(f'.{name}',
68-
sub_import[name])
69-
except ModuleNotFoundError as e:
70-
raise AttributeError(f'Module has no attribute {name!r}') from e
68+
if name in sub_import:
69+
try:
70+
return importlib.import_module(f'.{name}', sub_import[name])
71+
except ModuleNotFoundError as e:
72+
raise AttributeError(
73+
f'Module has no attribute {name!r}') from e
74+
75+
if name in top_level_import:
76+
try:
77+
module = importlib.__import__(top_level_import[name].pop())
78+
for import_path_ in top_level_import[name]:
79+
module = importlib.__import__(import_path_)
80+
return module
81+
except ModuleNotFoundError as e:
82+
raise AttributeError(
83+
f'Module has no attribute {name!r}') from e
84+
85+
raise AttributeError(f'Module has no attribute {name!r}')
7186

7287
return getattr_

0 commit comments

Comments
 (0)