Export, __all__, and lazy import management for Python projects.
Project description
exportify
exportify is a CLI tool and library for managing Python package exports: generating __init__.py files with lazy imports (using lateimport), managing __all__ declarations, and validating import consistency.
Exportify was previously developed as an internal dev tool to assist with our CodeWeaver project; it slowly grew in function until it didn't make sense to keep it as part of that project. Here it is for anyone to use.
What it Does
Exportify solves the problem of managing consistency and updates in export patterns across a codebase. It ensures module and package-level __all__ exports are consistent, accurate, and complete. It can also validate
Exportify offers a simple rule-based system for enforcing __all__ and __init__ patterns across a codebase with optional per-file overrides. Comes with sane defaults.
Features
- Lazy
__init__.pygeneration — generates__init__.pyfiles using a lazy__getattr__pattern powered bylateimport, keeping package imports fast and circular-import-free - YAML-driven rule engine — declarative rules control which symbols are exported, with priority ordering and pattern matching
- Export propagation — symbols exported from submodules automatically propagate up the package hierarchy
- Code preservation — manually written code above the
# === MANAGED EXPORTS ===sentinel is preserved across regeneration - Validation — checks that lazy import calls are well-formed and that
__all__declarations are consistent - Cache — SHA-256-based analysis cache for fast incremental updates
Installation
Easiest to install with uv:
uv tool install exportify
or pipx:
pipx install exportify
Python 3.12+ required.
Quick Start
# Create a default config file (.exportify/config.yaml)
exportify init
# Check current project consistency — runs all checks by default
exportify check
# Sync exports and __all__ to match your rules
# Creates missing __init__.py files and updates existing ones
exportify sync
# Preview what sync would change without writing anything
exportify sync --dry-run
# Run health checks and show current status
exportify doctor
Documentation
For new users
| Document | Description |
|---|---|
| Getting Started | Step-by-step tutorial for new projects |
Reference
| Document | Description |
|---|---|
| CLI Reference | Complete command reference with all flags |
| Rule Engine | Rule syntax, priorities, match criteria, provenance |
| Configuration | Initializing and configuring exportify |
Guides
| Document | Description |
|---|---|
| Troubleshooting & FAQ | Common issues and answers |
| Contributing | Development setup and how to contribute |
Internals (for contributors)
| Document | Description |
|---|---|
| Caching | Cache implementation and API |
| Overload Handling | @overload decorator support |
| Provenance Support | Symbol provenance in rules |
| Schema Versioning | Config schema version management |
Configuration
Rules live in .exportify/config.yaml (created by exportify init or written manually). Exportify searches for the config file in this order:
EXPORTIFY_CONFIGenvironment variable (any path).exportify/config.yaml.exportify/config.yml.exportify.yamlin the current working directory.exportify.ymlexportify.yamlexportify.yml
schema_version: "1.0"
rules:
- name: "exclude-private"
priority: 1000
match:
name_pattern: "^_"
action: exclude
- name: "include-public-classes"
priority: 700
match:
name_pattern: "^[A-Z]"
member_types: [class]
action: include
propagate: root
- name: "include-public-functions"
priority: 700
match:
name_pattern: "^[a-z]"
member_types: [function]
action: include
propagate: parent
Rule Priority Bands
| Priority | Purpose |
|---|---|
| 1000 | Absolute exclusions (private, dunders) |
| 900–800 | Infrastructure/framework exclusions |
| 700 | Primary export rules (classes, functions) |
| 600–500 | Import handling |
| 300–400 | Special cases |
| 0–200 | Defaults/fallbacks |
See the Rule Engine docs for the full rule syntax including logical combinations, match criteria, and advanced propagation options.
Propagation Levels
none— export only in the defining moduleparent— export in the defining module and its direct parentroot— export all the way to the package rootcustom— specify explicit target module
Generated Output
Exportify generates __init__.py files using the lazy __getattr__ pattern from lateimport:
# SPDX-FileCopyrightText: 2026 Your Name
#
# SPDX-License-Identifier: MIT
# === MANAGED EXPORTS ===
# This section is automatically generated. Manual edits below this line will be overwritten.
from __future__ import annotations
from typing import TYPE_CHECKING
from types import MappingProxyType
from lateimport import create_late_getattr
if TYPE_CHECKING:
from mypackage.core import MyClass
from mypackage.utils import helper_function
_dynamic_imports: MappingProxyType[str, tuple[str, str]] = MappingProxyType({
"MyClass": (__spec__.parent, "core"),
"helper_function": (__spec__.parent, "utils"),
})
__getattr__ = create_late_getattr(_dynamic_imports, globals(), __name__)
__all__ = ("MyClass", "helper_function")
def __dir__() -> list[str]:
"""List available attributes for the package."""
return list(__all__)
[!IMPORTANT] To use exportify for lazy
__init__management, you must addlateimportas a runtime dependency.
Code Preservation
Add a # === MANAGED EXPORTS === sentinel to an existing __init__.py to protect manually written code above it:
"""My package."""
from .compat import legacy_function # kept across regeneration
# === MANAGED EXPORTS ===
# ... generated section below (managed by exportify)
Everything above the sentinel is left untouched on every sync run.
CLI Reference
| Command | Description |
|---|---|
exportify init |
Initialize project configuration |
exportify check |
Validate exports and __all__ consistency |
exportify sync |
Align project code with export rules |
exportify undo |
Restore files from the last sync run |
exportify doctor |
Run system health checks |
exportify cache clear |
Clear the analysis cache |
exportify cache stats |
Show cache statistics |
See the full CLI reference for all flags and options.
Requirements
- Python 3.12+
lateimport(installed automatically)
License
Dual-licensed under MIT and Apache 2.0. See LICENSE-MIT and LICENSE-Apache-2.0.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file exportify-0.2.2.tar.gz.
File metadata
- Download URL: exportify-0.2.2.tar.gz
- Upload date:
- Size: 96.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f88513884047b17979e3104c68027b1ca7bac004b031ce439d347a173d5742e4
|
|
| MD5 |
4836bb4bd73aa8035f246bda6d1b74e4
|
|
| BLAKE2b-256 |
a5ac34433077202fe3753c00c10d305f13d6b500a8db190ccbdecccf5f57e5bf
|
Provenance
The following attestation bundles were made for exportify-0.2.2.tar.gz:
Publisher:
publish.yml on knitli/exportify
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
exportify-0.2.2.tar.gz -
Subject digest:
f88513884047b17979e3104c68027b1ca7bac004b031ce439d347a173d5742e4 - Sigstore transparency entry: 1059823337
- Sigstore integration time:
-
Permalink:
knitli/exportify@05024a3880e57934aab3b83f91e64a94b3e1a2a3 -
Branch / Tag:
refs/tags/v0.2.2 - Owner: https://github.com/knitli
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@05024a3880e57934aab3b83f91e64a94b3e1a2a3 -
Trigger Event:
push
-
Statement type:
File details
Details for the file exportify-0.2.2-py3-none-any.whl.
File metadata
- Download URL: exportify-0.2.2-py3-none-any.whl
- Upload date:
- Size: 124.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1d598520cd1d3836620c4e9bcd94b6553a540e29a3262e9b0c2cc625888bf15f
|
|
| MD5 |
71df7d3133be81bc21e6967fafc9d863
|
|
| BLAKE2b-256 |
f3403856ca294fab95e5f5b9d53d0a75e6bb19bd57a3b1206bb0d56d9a0cf92a
|
Provenance
The following attestation bundles were made for exportify-0.2.2-py3-none-any.whl:
Publisher:
publish.yml on knitli/exportify
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
exportify-0.2.2-py3-none-any.whl -
Subject digest:
1d598520cd1d3836620c4e9bcd94b6553a540e29a3262e9b0c2cc625888bf15f - Sigstore transparency entry: 1059823339
- Sigstore integration time:
-
Permalink:
knitli/exportify@05024a3880e57934aab3b83f91e64a94b3e1a2a3 -
Branch / Tag:
refs/tags/v0.2.2 - Owner: https://github.com/knitli
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@05024a3880e57934aab3b83f91e64a94b3e1a2a3 -
Trigger Event:
push
-
Statement type: