A symbol can be defined in one module and re-exported from several others. When importing a symbol from an external typed library, the convention is to import from as close to the root as possible. This is how popular auto-import resolution tools work (see my very similar post in this pylance discussion:
If the same name is exported from multiple modules, the auto-import logic in pyright and pylance prefer the shortest module path. Only the shortest path is listed in the completion list, and the longer paths are de-duped.
In large projects, sometimes you have big submodules that you want to provide an "internal public" interface for-- e.g. if you have a large submodule project.foo, you might want project-internal code to import only from project.foo itself, rather than project.foo.* submodules.
I'd like to suggest that ruff offer a rule that enforces this by requiring internal imports resolve to the shortest module that exposes a symbol. So:
### project/foo/__init__.py
# redundant alias is necessary to expose `BAR` as public for `project.foo`
from .bar import BAR as BAR
### project/foo/bar.py
BAR = "BAR"
### project/baz.py
from project.foo.bar import BAR # ERROR: should import from project.foo instead
A symbol can be defined in one module and re-exported from several others. When importing a symbol from an external typed library, the convention is to import from as close to the root as possible. This is how popular auto-import resolution tools work (see my very similar post in this pylance discussion:
In large projects, sometimes you have big submodules that you want to provide an "internal public" interface for-- e.g. if you have a large submodule
project.foo, you might want project-internal code to import only fromproject.fooitself, rather thanproject.foo.*submodules.I'd like to suggest that
ruffoffer a rule that enforces this by requiring internal imports resolve to the shortest module that exposes a symbol. So: