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.3.tar.gz (98.5 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.3-py3-none-any.whl (22.0 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: minport-0.1.3.tar.gz
  • Upload date:
  • Size: 98.5 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.3.tar.gz
Algorithm Hash digest
SHA256 b64e54a3abe492f2eb00076d39a7a9268c41652a24becb83e15d20f215cb7505
MD5 0fa4223b0581b3af87137199c21e978d
BLAKE2b-256 bf51e624b90bc5436a669d290bbef14a96b1b844c3592979b7c20632e477d750

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: minport-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 22.0 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.3-py3-none-any.whl
Algorithm Hash digest
SHA256 48cf366aeaea1697cbf92cc3f9b6f2726f64e32c2cba076b07f1f8dffe738b4e
MD5 bfb8cc1a141f4382458e0e9aff0af012
BLAKE2b-256 f3d46e65c85ddcc6270c8b5414dba82730415b84cec582af5c3af708e01e08a9

See more details on using hashes here.

Provenance

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