Skip to content

[feature-request] Require first-party imports to target highest exporting non-ancestor module #1107

@smackesey

Description

@smackesey

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-decisionAwaiting a decision from a maintainerruleImplementing or modifying a lint rule

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions