Skip to main content

Enforces the presence and correctness of __all__ in Python modules

Project description

sheridan-iceberg

CI PyPI Coverage Mutation Score License: MIT Python 3.14+

The public API is the tip of the iceberg. iceberg guards the waterline.

sheridan-iceberg analyzes Python modules and enforces the presence and correctness of __all__. It uses Python's ast module for static analysis — no importing of user code.

Features

  • show — inspect and report the effective public API of any module or project; uses __all__ when present, falls back to AST inference; --use-ast forces AST-only regardless of __all__
  • check — enforce __all__ correctness; IB002 is one-directional (names that appear public in the AST but are absent from __all__), so deliberate re-exports are never flagged as phantom names
  • fix — auto-repair __all__ in place; uses full bidirectional comparison, removing phantom exports as well as adding missing ones
  • Walks a Python project's modules via AST (safe, no imports)
  • Uses __all__ as the authoritative public API surface when present; falls back to inferring non-underscore top-level names when absent
  • Machine-readable JSON output and human-readable text/tree output
  • Works as a pre-commit hook, CLI tool, or GitHub Action

Installation

pip install sheridan-iceberg

Usage

# Report the public API of a project
iceberg show src/

# Show as JSON (machine-readable)
iceberg show src/ --format json

# Ignore __all__ entirely — always use AST inference
iceberg show src/ --use-ast

# Check __all__ declarations against the AST
iceberg check src/

# Suppress IB001 (missing __all__) — only report IB002 and IB003
iceberg check src/ --ignore-missing

# Check with JSON output
iceberg check src/ --format json

# Auto-fix __all__ declarations (bidirectional — also removes phantom exports)
iceberg fix src/

# Preview what fix would change without writing
iceberg fix src/ --dry-run

Example output

iceberg show produces an indented tree by default. When __init__.py declares __all__, it is the source of truth for the whole package — only that module is shown:

# iceberg show src/mypackage/

mypackage/
  __init__
    Role
    User
    helper

Pass --use-ast to bypass __all__ and see every module's inferred names:

# iceberg show src/mypackage/ --use-ast

mypackage/
  __init__
    Role
    User
    helper
  core
    Alpha
    Beta
    Gamma
  utils
    helper
    parse

iceberg check reports violations:

src/mypackage/utils.py: IB001 missing __all__ (expected ['helper', 'parse'])
src/mypackage/models.py: IB002 names appear public but missing from __all__: ['Role']
src/mypackage/core.py: IB003 __all__ is not sorted (expected ['Alpha', 'Beta', 'Gamma'])

Exit codes:

  • show: 0 always (path existence aside)
  • check: 0 no issues, 1 issues found, 2 path not found
  • fix: 0 success, 2 path not found

JSON output

iceberg show --format json:

[
  {
    "module": "mypackage.utils",
    "path": "src/mypackage/utils.py",
    "source": "ast",
    "names": ["helper", "parse"]
  },
  {
    "module": "mypackage.models",
    "path": "src/mypackage/models.py",
    "source": "__all__",
    "names": ["Role", "User"]
  }
]

The source field is "__all__" when the module has an __all__ (and --use-ast is not set), "ast" otherwise.

iceberg check --format json:

[
  {
    "code": "IB001",
    "path": "src/mypackage/utils.py",
    "kind": "missing",
    "declared": null,
    "expected": ["helper", "parse"]
  }
]

Programmatic usage

from sheridan.iceberg import check_api, fix_api, get_public_api

# Get the public API surface — __init__.__all__ is the source of truth
api = get_public_api("src/")
# {"mypackage": ["Role", "User", "helper"]}

# Bypass __all__ and see every module's AST-inferred names
api = get_public_api("src/", use_ast=True)
# {"mypackage": [...], "mypackage.core": [...], "mypackage.utils": [...]}

# Check for __all__ issues
issues = check_api("src/")
# [{"code": "IB002", "path": "...", "kind": "incorrect", "declared": [...], "expected": [...]}]

# Suppress IB001 (missing __all__) — only surface IB002 and IB003
issues = check_api("src/", ignore_missing=True)

# Fix __all__ in place — returns paths of modified files
fixed = fix_api("src/")
# [PosixPath("src/mypackage/core.py")]

# Preview what would change without writing
would_fix = fix_api("src/", dry_run=True)

How inference works

For regular modules, iceberg infers the public API from top-level definitions — functions, classes, and assignments whose names do not start with _.

For __init__.py files, names re-exported via from x import y are also counted, since this is the standard Python pattern for building a package's public surface.

# foo/__init__.py
from foo.snap import Widget   # Widget is inferred as public
from foo._bar import _helper  # _helper is excluded (underscore)

Submodules are not automatically included. The existence of foo/snap.py on disk does not add snap to foo.__all__ — the __init__.py is the explicit gatekeeper. To expose a submodule, import it explicitly:

# foo/__init__.py
from foo import snap  # now snap is part of the inferred public API

Test files (test_*.py, *_test.py, conftest.py) are always skipped.

As a pre-commit hook

repos:
  - repo: https://github.com/sheridan/sheridan-iceberg
    rev: v0.1.0
    hooks:
      - id: iceberg

Development

# Install dependencies
task install

# Run all checks (lint, format, typecheck, test, iceberg)
task check

# Run individual checks
task lint:check   # ruff — read-only
task lint         # ruff — autofix
task format:check # formatter — read-only
task format       # formatter — write
task typecheck    # mypy --strict
task test         # pytest --cov
task iceberg:check # dogfood: run iceberg check on itself
task iceberg:show  # dogfood: show iceberg's own public API

# Run tests
task test

# Build docs
task docs-serve

CI pipeline (Dagger)

The full CI pipeline runs each gate in its own container via Dagger. Podman is the default runtime; Docker is supported via CONTAINER_RUNTIME=docker.

# First-time setup (generates ci/sdk/ — run once after clone)
podman machine start   # macOS only
task ci-init

# Run the full CI pipeline locally
task ci

# Use Docker instead
CONTAINER_RUNTIME=docker task ci

See CONTRIBUTING.md for details.

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

sheridan_iceberg-1.0.5.tar.gz (55.6 kB view details)

Uploaded Source

Built Distribution

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

sheridan_iceberg-1.0.5-py3-none-any.whl (17.7 kB view details)

Uploaded Python 3

File details

Details for the file sheridan_iceberg-1.0.5.tar.gz.

File metadata

  • Download URL: sheridan_iceberg-1.0.5.tar.gz
  • Upload date:
  • Size: 55.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for sheridan_iceberg-1.0.5.tar.gz
Algorithm Hash digest
SHA256 63c54ee5ce7ecf4ab21b2264b1e21971718c817f3e825d9c3adb3dd0acf45408
MD5 97d63de2494d2a8606aa9c14be33315b
BLAKE2b-256 2ee0be4cb9a15a3393ef91d868b905f7f08695edcdf56913250441bbf47081b9

See more details on using hashes here.

Provenance

The following attestation bundles were made for sheridan_iceberg-1.0.5.tar.gz:

Publisher: publish.yaml on andrewasheridan/iceberg

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

File details

Details for the file sheridan_iceberg-1.0.5-py3-none-any.whl.

File metadata

File hashes

Hashes for sheridan_iceberg-1.0.5-py3-none-any.whl
Algorithm Hash digest
SHA256 e2e357c18542ceefc7e7e169714ff11a626357515b4dfe972c795b5d278fd995
MD5 2d1fdee0855338a22351d2ed1e9182a1
BLAKE2b-256 9c78fedb10123c7494c2781735055725848cabff6023b4269092bb02920bc7f9

See more details on using hashes here.

Provenance

The following attestation bundles were made for sheridan_iceberg-1.0.5-py3-none-any.whl:

Publisher: publish.yaml on andrewasheridan/iceberg

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