Metadata-Version: 2.4
Name: exportify
Version: 0.2.7
Summary: Export, __all__, and lazy import management for Python projects.
Project-URL: Homepage, https://github.com/knitli/exportify
Project-URL: Issues, https://github.com/knitli/exportify/issues
Project-URL: Repository, https://github.com/knitli/exportify
Author: Knitli Inc.
Author-email: Adam Poulemanos <adam@knit.li>
License-File: LICENSE-Apache-2.0
License-File: LICENSE-MIT
License-File: LICENSES/Apache-2.0.txt
License-File: LICENSES/MIT.txt
Keywords: __all__,__init__,code-generation,exports,lazy-imports,package-management
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Code Generators
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: cyclopts>=4.8.0
Requires-Dist: lateimport>=0.1.0
Requires-Dist: pyyaml>=6.0.3
Requires-Dist: rich>=14.3.0
Requires-Dist: textcase>=0.4.5
Description-Content-Type: text/markdown

<!--
SPDX-FileCopyrightText: 2026 Knitli Inc.

SPDX-License-Identifier: MIT OR Apache-2.0
-->
[![codecov](https://codecov.io/gh/knitli/exportify/graph/badge.svg?token=TV7RP2F9FG)](https://codecov.io/gh/knitli/exportify)
# exportify

**exportify** is a CLI tool and library for managing Python package exports: generating `__init__.py` files with lazy imports (using [`lateimport`](https://github.com/knitli/lateimport)), managing `__all__` declarations, and validating import consistency.

Exportify was previously developed as an internal dev tool to assist with our [CodeWeaver](https://github.com/knitli/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__.py` generation** — generates `__init__.py` files using a lazy `__getattr__` pattern powered by [`lateimport`](https://pypi.org/project/lateimport/), 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`](https://docs.astral.sh/uv/getting-started/installation/):

```bash
uv tool install exportify
```

or `pipx`:

```bash
pipx install exportify
```

Python 3.12+ required.

## Quick Start

```bash
# 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](docs/getting-started.md) | Step-by-step tutorial for new projects |

### Reference

| Document | Description |
|----------|-------------|
| [CLI Reference](docs/cli-reference.md) | Complete command reference with all flags |
| [Rule Engine](src/exportify/rules/README.md) | Rule syntax, priorities, match criteria, provenance |
| [Configuration](docs/init.md) | Initializing and configuring exportify |

### Guides

| Document | Description |
|----------|-------------|
| [Troubleshooting & FAQ](docs/troubleshooting.md) | Common issues and answers |
| [Contributing](docs/contributing.md) | Development setup and how to contribute |

### Internals (for contributors)

| Document | Description |
|----------|-------------|
| [Caching](docs/caching.md) | Cache implementation and API |
| [Overload Handling](docs/overload-handling.md) | `@overload` decorator support |
| [Provenance Support](docs/provenance.md) | Symbol provenance in rules |
| [Schema Versioning](docs/schema-versioning.md) | 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:

1. `EXPORTIFY_CONFIG` environment variable (any path)
2. `.exportify/config.yaml`
3. `.exportify/config.yml`
4. `.exportify.yaml` in the current working directory
5. `.exportify.yml`
6. `exportify.yaml`
7. `exportify.yml`

```yaml
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](src/exportify/rules/README.md) for the full rule syntax including logical combinations, match criteria, and advanced propagation options.

### Propagation Levels

- `none` — export only in the defining module
- `parent` — export in the defining module and its direct parent
- `root` — export all the way to the package root
- `custom` — specify explicit target module

## Generated Output

Exportify generates `__init__.py` files using the lazy `__getattr__` pattern from `lateimport`:

```python
# 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 add `lateimport` as a runtime dependency.

## Code Preservation

Add a `# === MANAGED EXPORTS ===` sentinel to an existing `__init__.py` to protect manually written code above it:

```python
"""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](docs/cli-reference.md) for all flags and options.

## Requirements

- Python 3.12+
- [`lateimport`](https://pypi.org/project/lateimport/) (installed automatically)

## License

Dual-licensed under MIT and Apache 2.0. See [LICENSE-MIT](LICENSE-MIT) and [LICENSE-Apache-2.0](LICENSE-Apache-2.0).
