A Python linter that finds unnecessarily long import paths and suggests shorter alternatives
Project description
minport
A Python linter that finds unnecessarily long import paths and suggests shorter alternatives by tracing re-export chains.
The Problem
Python packages expose public APIs through re-exports, but developers often import from deep internal paths instead of the canonical public interface:
# Unnecessarily long — these reach into internal modules
from pydantic.fields import FieldInfo
from sqlalchemy.orm.session import Session
from myproject.domain.user.models import User
# Shorter — these use the public re-export path
from pydantic import FieldInfo
from sqlalchemy.orm import Session
from myproject.domain import User
Long import paths are:
- Harder to read: Internal paths obscure the public interface
- Fragile: Internal paths break when package maintainers refactor internals (re-exports are the stable API)
- Inconsistent: Same name imported from different paths across the codebase
Installation
pip install minport
# or with uv
uv add --dev minport
# or with pipx (for global CLI usage)
pipx install minport
Requirements: Python 3.11+ / No runtime dependencies.
Quick Start
minport check src/
# src/models.py:3:1: MP001 `from pydantic.fields import FieldInfo` can be shortened to `from pydantic import FieldInfo`
# Found 1 error (checked 12 files, 1 fixable with `minport check --fix`).
minport check src/ --fix
# Found 1 error (checked 12 files, fixed 1 in 1 file).
Usage
minport check src/ # Check a directory
minport check src/models.py # Check a single file
minport check src/ --src src/ # Set import resolution root
minport check src/ --exclude "tests/*" # Exclude patterns (overrides defaults)
minport check src/ --extend-exclude "generated/*" # Add to default excludes
minport check src/ --config path/to/pyproject.toml # Custom config path
minport check src/ --fix # Auto-fix in place
minport check src/ --quiet # Suppress the summary line
Exit codes: 0 = no violations, 1 = violations found, 2 = error (e.g. path not found).
Configuration
Configure minport in pyproject.toml:
[tool.minport]
src = ["src"] # Import resolution root(s)
exclude = ["tests/*", "migrations/*"] # Exclude patterns (overrides defaults)
extend-exclude = ["generated/*"] # Add patterns without overriding defaults
CLI arguments override pyproject.toml settings.
Default Excludes
minport automatically skips common non-source directories (.venv, __pycache__, .git, node_modules, dist, site-packages, etc.). Use --exclude to override these defaults entirely, or --extend-exclude to add patterns on top of them.
Rules
| Code | Name | Description | Fixable |
|---|---|---|---|
| MP001 | shorter-import-available | A shorter import path is available via re-exports | Yes |
Inline Suppression
Add # minport: ignore to suppress violations on specific imports.
Single-line import:
from pydantic.fields import FieldInfo # minport: ignore
Multi-line import — suppress all names:
from pydantic.fields import ( # minport: ignore
FieldInfo,
Field,
)
Multi-line import — suppress a specific name:
from pydantic.fields import (
FieldInfo, # minport: ignore ← only FieldInfo is suppressed
Field, # ← this can still be flagged
)
How It Works
For each from X.Y.Z import Name:
- Decompose the module path:
[X, X.Y, X.Y.Z] - For each candidate path (shortest first), check if
Nameis re-exported there - Re-export detection via AST analysis of
__init__.py:from .submodule import Name— explicit re-exportfrom .submodule import Name as Name— PEP 484 explicit formfrom .submodule import *— wildcard (recursively resolved, respects__all__)__all__ = ["Name", ...]— public API declaration
- Return the shortest match (if shorter than the original)
Works with both project packages (analyzes __init__.py files) and third-party packages (uses importlib.util.find_spec + AST analysis, no runtime imports).
Safety
- No modifications without
--fix: Read-only by default - Collision detection: If a name exists in multiple candidate paths, the import is not flagged
- Syntax error tolerance: Malformed Python files are skipped with a warning
--fixwrites in place: Always verify changes withgit diffbefore committing
Integrations
pre-commit
# .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: minport
name: minport
entry: minport check
language: python
types: [python]
additional_dependencies: ["minport"]
GitHub Actions
# .github/workflows/lint.yml
jobs:
minport:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- run: pip install minport
- run: minport check src/
Limitations
- Only rewrites
from X.Y.Z import Name(notimport X.Y.Z) - Does not analyze dynamic or runtime imports
- No IDE integration yet
Contributing
git clone https://github.com/KinjiKawaguchi/minport.git
cd minport
uv sync
uv run pytest --cov=minport
uv run ruff check src/ tests/
- DeepWiki — AI-generated codebase documentation
License
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file minport-0.1.0.tar.gz.
File metadata
- Download URL: minport-0.1.0.tar.gz
- Upload date:
- Size: 93.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
265b4c3914717571ed0455cfb9bd35e06db34cd3afc383ca28a2bc1945d34661
|
|
| MD5 |
46cda8fcd57627b723a543b88636eb0e
|
|
| BLAKE2b-256 |
28ab265caa47d6de63af00bc32dda9ae0e425548fff57f264ad44f43320111fa
|
Provenance
The following attestation bundles were made for minport-0.1.0.tar.gz:
Publisher:
release.yml on KinjiKawaguchi/minport-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
minport-0.1.0.tar.gz -
Subject digest:
265b4c3914717571ed0455cfb9bd35e06db34cd3afc383ca28a2bc1945d34661 - Sigstore transparency entry: 1280581476
- Sigstore integration time:
-
Permalink:
KinjiKawaguchi/minport-py@b7e895823b3e42717e448e2b02d44a200d8d1b40 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/KinjiKawaguchi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@b7e895823b3e42717e448e2b02d44a200d8d1b40 -
Trigger Event:
push
-
Statement type:
File details
Details for the file minport-0.1.0-py3-none-any.whl.
File metadata
- Download URL: minport-0.1.0-py3-none-any.whl
- Upload date:
- Size: 20.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
31361565498a4957599deb1de18e8f0755828cbabf5dd5ba54481c3e6e743ce2
|
|
| MD5 |
ae83a7310457e1f0f4ac69506bfadb78
|
|
| BLAKE2b-256 |
a6f7b80df61c0b6587b0af87eb70a033194003b55c56a2a11c28c80a849848d5
|
Provenance
The following attestation bundles were made for minport-0.1.0-py3-none-any.whl:
Publisher:
release.yml on KinjiKawaguchi/minport-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
minport-0.1.0-py3-none-any.whl -
Subject digest:
31361565498a4957599deb1de18e8f0755828cbabf5dd5ba54481c3e6e743ce2 - Sigstore transparency entry: 1280581478
- Sigstore integration time:
-
Permalink:
KinjiKawaguchi/minport-py@b7e895823b3e42717e448e2b02d44a200d8d1b40 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/KinjiKawaguchi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@b7e895823b3e42717e448e2b02d44a200d8d1b40 -
Trigger Event:
push
-
Statement type: