Skip to main content

A Python linter that finds unnecessarily long import paths and suggests shorter alternatives

Project description

PyPI version Python versions CI License: MIT

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:

  1. Harder to read: Internal paths obscure the public interface
  2. Fragile: Internal paths break when package maintainers refactor internals (re-exports are the stable API)
  3. 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:

  1. Decompose the module path: [X, X.Y, X.Y.Z]
  2. For each candidate path (shortest first), check if Name is re-exported there
  3. Re-export detection via AST analysis of __init__.py:
    • from .submodule import Name — explicit re-export
    • from .submodule import Name as Name — PEP 484 explicit form
    • from .submodule import * — wildcard (recursively resolved, respects __all__)
    • __all__ = ["Name", ...] — public API declaration
  4. 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
  • --fix writes in place: Always verify changes with git diff before 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 (not import 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

MIT

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

minport-0.1.0.tar.gz (93.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

minport-0.1.0-py3-none-any.whl (20.4 kB view details)

Uploaded Python 3

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

Hashes for minport-0.1.0.tar.gz
Algorithm Hash digest
SHA256 265b4c3914717571ed0455cfb9bd35e06db34cd3afc383ca28a2bc1945d34661
MD5 46cda8fcd57627b723a543b88636eb0e
BLAKE2b-256 28ab265caa47d6de63af00bc32dda9ae0e425548fff57f264ad44f43320111fa

See more details on using hashes here.

Provenance

The following attestation bundles were made for minport-0.1.0.tar.gz:

Publisher: release.yml on KinjiKawaguchi/minport-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for minport-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 31361565498a4957599deb1de18e8f0755828cbabf5dd5ba54481c3e6e743ce2
MD5 ae83a7310457e1f0f4ac69506bfadb78
BLAKE2b-256 a6f7b80df61c0b6587b0af87eb70a033194003b55c56a2a11c28c80a849848d5

See more details on using hashes here.

Provenance

The following attestation bundles were made for minport-0.1.0-py3-none-any.whl:

Publisher: release.yml on KinjiKawaguchi/minport-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page