Add absolute named import support to lazy_export#5078
Add absolute named import support to lazy_export#5078ooctipus wants to merge 1 commit intoisaac-sim:developfrom
Conversation
Extend _parse_stub and lazy_export to handle absolute named imports (e.g. `from isaaclab.envs.mdp import foo, bar`) in .pyi stubs. These are eagerly resolved and re-exported alongside the existing relative and wildcard import paths. Add unit tests for _parse_stub's new absolute_named return value covering single/multiple packages, accumulation across lines, mutual exclusion with wildcards and relative imports, and filtered stub integrity.
Greptile SummaryThis PR extends Key points:
Confidence Score: 4/5
Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["lazy_export() called"] --> B{has .pyi stub?}
B -- No --> C["__all__ = []"]
B -- Yes --> D["_parse_stub(stub_file)"]
D --> E["AST walk over stub body"]
E --> F{node is ImportFrom?}
F -- No --> G["add to filtered_body"]
F -- Yes --> H{classify import}
H -- "level==1, not star" --> I["add to filtered_body\n(relative named → lazy_loader)"]
H -- "level==0, star" --> J["fallback_packages.append(module)"]
H -- "level==1, star" --> K["relative_wildcards.append(module)"]
H -- "level==0, not star" --> L["absolute_named[module].extend(names)"]
H -- "level>1 or no module" --> M["silently dropped, needs_filter=True"]
I --> N["write filtered .pyi\nfor lazy_loader"]
J --> N
K --> N
L --> N
N --> O["lazy.attach_stub → stub_getattr, stub_dir, __all__"]
C --> P["mod = sys.modules[package_name]"]
O --> P
P --> Q["Resolve absolute_named\n(from pkg import a,b)"]
Q --> R["importlib.import_module(abs_pkg)\ngetattr → mod.__dict__[name]\n__all__.append(name)"]
R --> S["Resolve relative_wildcards\n(from .X import *)"]
S --> T["importlib.import_module(fq_name)\nfor each public name → mod.__dict__"]
T --> U{fallback_packages?}
U -- Yes --> V["Build lazy _pkg_getattr\n__getattr__ chains stub → pkg fallback"]
U -- No --> W["__getattr__ = stub_getattr"]
V --> X["setattr mod: __getattr__, __dir__, __all__"]
W --> X
Last reviewed commit: "Add absolute named i..." |
| names = [alias.name for alias in node.names] | ||
| absolute_named.setdefault(node.module, []).extend(names) |
There was a problem hiding this comment.
Import aliases silently dropped
alias.asname is never consulted, so from pkg import foo as bar in a stub will eagerly register the attribute under the original name foo (not the alias bar). The correct exported key should be alias.asname if alias.asname else alias.name, and the value should always be fetched as alias.name from the source module.
In practice, stubs in this project may not currently use as-aliasing for absolute named imports, but the silent misbehaviour is a latent bug: the wrong name ends up in mod.__dict__ and __all__, and the intended alias is never exported.
| names = [alias.name for alias in node.names] | |
| absolute_named.setdefault(node.module, []).extend(names) | |
| names = [(alias.name, alias.asname or alias.name) for alias in node.names] | |
| for src_name, exported_name in names: | |
| absolute_named.setdefault(node.module, []).append((src_name, exported_name)) |
And correspondingly in lazy_export:
for abs_pkg, name_pairs in absolute_named.items():
pkg_mod = importlib.import_module(abs_pkg)
for src_name, exported_name in name_pairs:
mod.__dict__[exported_name] = getattr(pkg_mod, src_name)
if exported_name not in __all__:
__all__.append(exported_name)| for abs_pkg, names in absolute_named.items(): | ||
| pkg_mod = importlib.import_module(abs_pkg) | ||
| for name in names: | ||
| mod.__dict__[name] = getattr(pkg_mod, name) | ||
| if name not in __all__: | ||
| __all__.append(name) |
There was a problem hiding this comment.
No error context on failed absolute named import
Both importlib.import_module(abs_pkg) and getattr(pkg_mod, name) can raise without any pointer back to the stub that declared the import. Compare with the analogous fallback_packages block (lines 176–184) which wraps import_module in a try/except and re-raises an ImportError with a descriptive message.
Consider wrapping this block similarly:
for abs_pkg, names in absolute_named.items():
try:
pkg_mod = importlib.import_module(abs_pkg)
except ImportError as e:
raise ImportError(
f"lazy_export() in {package_name!r}: .pyi stub declares "
f"'from {abs_pkg} import ...' but the package is not installed."
) from e
for name in names:
try:
mod.__dict__[name] = getattr(pkg_mod, name)
except AttributeError as e:
raise AttributeError(
f"lazy_export() in {package_name!r}: .pyi stub declares "
f"'from {abs_pkg} import {name}' but the attribute does not exist."
) from e
if name not in __all__:
__all__.append(name)
Description
Extend
_parse_stubandlazy_exportto handle absolute named imports(e.g.
from isaaclab.envs.mdp import foo, bar) in.pyistubs. Previously onlyrelative imports and absolute wildcard fallbacks were supported. Explicit names from
absolute packages are now eagerly resolved and re-exported at
lazy_export()time.Type of change
Checklist
pre-commitchecks with./isaaclab.sh --formatconfig/extension.tomlfileCONTRIBUTORS.mdor my name already exists there