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.1.tar.gz (94.6 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.1-py3-none-any.whl (20.8 kB view details)

Uploaded Python 3

File details

Details for the file minport-0.1.1.tar.gz.

File metadata

  • Download URL: minport-0.1.1.tar.gz
  • Upload date:
  • Size: 94.6 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.1.tar.gz
Algorithm Hash digest
SHA256 52d67c26b40d1943f6717f790d528863b5a0efea7e93b1d2b0ce8db7d4ab59c9
MD5 aa06116a2812c5338127f37fd688dfb8
BLAKE2b-256 2e66f5f7e8c616eee3602cc48de3049041d79a46828c131effb6d29c34c5f21a

See more details on using hashes here.

Provenance

The following attestation bundles were made for minport-0.1.1.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.1-py3-none-any.whl.

File metadata

  • Download URL: minport-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 20.8 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 2790195ae15f2f04240e493b05145be2015cbf753ea0bc0bbae3dc4137f6f999
MD5 3f39f16194fbc265da50c0f3c46fda09
BLAKE2b-256 cae93ab92856eba71fd8c8514873ec08c5ea252f5918b38516076243cc933430

See more details on using hashes here.

Provenance

The following attestation bundles were made for minport-0.1.1-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