core: discovery_pure; allow multiple package roots (#152)
* core: discovery_pure; allow multiple package roots iterates over my.__path__._path if possible to discover additional paths to iterate over else defaults to the path relative to the current file
This commit is contained in:
parent
5ecd4b4810
commit
c31641b4eb
1 changed files with 34 additions and 3 deletions
|
@ -16,7 +16,7 @@ NOT_HPI_MODULE_VAR = '__NOT_HPI_MODULE__'
|
|||
###
|
||||
|
||||
import ast
|
||||
from typing import Optional, Sequence, NamedTuple, Iterable, cast, Any
|
||||
from typing import Optional, Sequence, List, NamedTuple, Iterable, cast, Any
|
||||
from pathlib import Path
|
||||
import re
|
||||
import logging
|
||||
|
@ -114,10 +114,37 @@ def _extract_requirements(a: ast.Module) -> Requires:
|
|||
|
||||
# todo should probably be more defensive..
|
||||
def all_modules() -> Iterable[HPIModule]:
|
||||
"""
|
||||
Return all importable modules under all items in the 'my' namespace package
|
||||
|
||||
Note: This returns all modules under all roots - if you have
|
||||
several overlays (multiple items in my.__path__ and you've overridden
|
||||
modules), this can return multiple HPIModule objects with the same
|
||||
name. It should respect import order, as we're traversing
|
||||
in my.__path__ order, so module_by_name should still work
|
||||
and return the correctly resolved module, but all_modules
|
||||
can have duplicates
|
||||
"""
|
||||
for my_root in _iter_my_roots():
|
||||
yield from _modules_under_root(my_root)
|
||||
|
||||
|
||||
def _iter_my_roots() -> Iterable[Path]:
|
||||
import my # doesn't import any code, because of namespace package
|
||||
|
||||
paths: List[str] = list(my.__path__) # type: ignore[attr-defined]
|
||||
if len(paths) == 0:
|
||||
# should probably never happen?, if this code is running, it was imported
|
||||
# because something was added to __path__ to match this name
|
||||
raise RuntimeError("my.__path__ was empty, try re-installing HPI?")
|
||||
else:
|
||||
yield from map(Path, paths)
|
||||
|
||||
|
||||
def _modules_under_root(my_root: Path) -> Iterable[HPIModule]:
|
||||
"""
|
||||
Experimental version, which isn't importing the modules, making it more robust and safe.
|
||||
"""
|
||||
my_root = Path(__file__).absolute().parent.parent
|
||||
for f in sorted(my_root.rglob('*.py')):
|
||||
if f.is_symlink():
|
||||
continue # meh
|
||||
|
@ -185,8 +212,12 @@ def test_pure() -> None:
|
|||
"""
|
||||
We want to keep this module clean of other HPI imports
|
||||
"""
|
||||
# this uses string concatenation here to prevent
|
||||
# these tests from testing against themselves
|
||||
src = Path(__file__).read_text()
|
||||
assert 'import ' + 'my' not in src
|
||||
# 'import my' is allowed, but
|
||||
# dont allow anything other HPI modules
|
||||
assert re.findall('import ' + r'my\.\S+', src, re.M) == []
|
||||
assert 'from ' + 'my' not in src
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue