private-import-path (SMP310)¶
Flags private imports that reach beyond one direct relative module, and imports that bind a private module as an object. In tests, imports from private modules must match the test’s path and name.
Why is this bad?¶
Importing a module such as package._api exposes its implementation path to callers. Importing names directly lets a
package keep its implementation private while choosing what to expose through __all__. A parent-relative import such
as from .._api import API also crosses a package boundary.
Example¶
package/consumer.py
1import package._api as api
2
3value = api.make()
package/consumer.py
1from ._api import make
2
3value = make()
What it does¶
The rule checks imports in module, class and function bodies. It offers no fix for an import alone: choosing a public re-export or replacing uses of a module object may require changes elsewhere.
Active on¶
Private module-object imports, such as
import package._api as apiandfrom . import _api.Absolute private imports, such as
from package._api import API. Usefrom ._api import APIwithin the same package, or importAPIfrom a public re-export elsewhere.Parent-relative private imports, such as
from .._api import APIandfrom .. import _api.Paths with two private names, such as
from ._internal._helpers import APIorfrom ._internal import _helper. A direct relative import may cross one private name, not two.Private modules below a sibling, such as
from ._api.helpers import API.Unpaired test imports, such as
tests/package/test_api.pyimporting frompackage._other. A test of a private module may import from the matching module only; other behavior should be reached through public interfaces.
Exceptions¶
One private name in a direct relative import:
from ._api import APIlets the package use a private sibling to build an API outside tests.from .public import _helperalso stays within the same package level.A paired test and private module:
tests/package/test_api.pymay import names frompackage._api, and a colocatedpackage/test_api.pymay import names frompackage._apior._api. The package path and the stem aftertest_must match. A relative import from the mirrored test tree resolves inside that tree, so it is not paired with the source module. Importing the private module as an object remains disallowed.Dunder names:
__future__and__version__do not count as private names.Documented public underscore names:
os._exitis a public API despite its underscore.
Other tools¶
Ruff’s import-private-name (PLC2701) reports private imports across packages, but exempts relative imports and absolute imports within the same top-level package. This rule checks those paths and module-object imports too.