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
minport check src/ --output-format github  # output for github annotations

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/ --output-format github

The --output-format github flag emits GitHub Actions workflow commands, so violations appear as inline annotations on the PR file view.

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.2.0.tar.gz (93.7 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.2.0-py3-none-any.whl (22.4 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: minport-0.2.0.tar.gz
  • Upload date:
  • Size: 93.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for minport-0.2.0.tar.gz
Algorithm Hash digest
SHA256 161e5191d24f0257c9af1af78acbbdb9cb2b2c626094f1eaaf9788b9728c2ea0
MD5 428d80e24354386e10e983d2da5200fe
BLAKE2b-256 22d1266a7e7751c060df592d860999ed39ad9f2788932eef276e06cbfa62d39d

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: minport-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 22.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.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 651c32b7523af62fc9b43987c3cf39cf857880b66d1de7a11eb2455e8f51aa71
MD5 22d6d8739638fcc39b8f3a9edc1c42e6
BLAKE2b-256 aaf9afd8fdb5778570442c743d3995b224d0cfccaa8b3634ac4f27abc16c85a6

See more details on using hashes here.

Provenance

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