This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Exportify is a CLI tool and library for managing Python package exports: generating __init__.py files with lazy imports, managing __all__ declarations, and validating import consistency. It was extracted from the CodeWeaver monorepo and is being established as a standalone package.
# Install dependencies
uv sync
# Run linter (auto-fixes enabled)
uv run ruff check src/ tests/
uv run ruff format src/ tests/
# Type checking (ty, not mypy)
uv run ty check
# Run all tests
uv run pytest
# Run the CLI
uv run exportify --help
uv run exportify init # Initialize project configuration
uv run exportify check # Validate exports and __all__ consistency
uv run exportify sync # Align project code with export rules
uv run exportify undo # Restore files modified by the last sync
uv run exportify doctor # Run system and configuration health checks
uv run exportify cache clear # Delete cached analysis results
uv run exportify cache stats # Show cache statisticsThe core workflow is a 5-stage pipeline (pipeline.py):
- File Discovery (
discovery/file_discovery.py) — Finds Python files in a source tree - AST Parsing (
analysis/ast_parser.py) — ExtractsDetectedSymbolinstances from each file, handling@overloadgrouping viaast_parser_overload.py - Rule Engine (
export_manager/rules.py) — Evaluates YAML-configured rules against each symbol, producingExportDecisionobjects withRuleAction(include/exclude/no_decision) andPropagationLevel(none/parent/root) - Propagation Graph (
export_manager/graph.py) — Builds the module hierarchy as a DAG and propagates export decisions upward, generatingExportManifestobjects per module - Code Generation (
export_manager/generator.py) — Converts manifests to__init__.pyfiles using lazy__getattr__pattern; preserves manually written code above the# === MANAGED EXPORTS ===sentinel
Validation (validator/) runs independently: LateImportValidator uses ImportResolver and ConsistencyChecker to verify that existing lazy import calls resolve correctly and that __all__ declarations match _dynamic_imports.
Cache (common/cache.py) — JSONAnalysisCache stores AnalysisResult objects keyed by file path + SHA-256 hash, stored at .codeweaver/cache/analysis_cache.json.
The type system follows the pipeline phases:
DetectedSymbol— raw AST-extracted symbol withMemberTypeandSymbolProvenanceExportDecision— rule evaluation result for one symbolExportManifest— all exports for a single module after propagationLazyExport— a single entry in a generated__init__.py
Enums: MemberType (class/function/constant/variable/type_alias/imported), SymbolProvenance (defined_here/imported/alias_imported), RuleAction (include/exclude/no_decision), PropagationLevel (none/parent/root/custom).
Rules live in YAML files with schema_version: "1.0". The rule engine evaluates rules in priority order (0–1000, highest first); ties break alphabetically by rule name. First matching rule wins.
Rule priority bands:
- 1000: Absolute exclusions (private, dunders)
- 900–800: Infrastructure/framework exclusions
- 700: Primary export rules (defined classes, functions, constants)
- 600–500: Import handling
- 300–400: Special cases
- 0–200: Defaults/fallbacks
Manual overrides via RuleEngine.set_overrides() bypass all rules — use sparingly.
The generator uses a sentinel-based section system: code above # === MANAGED EXPORTS === is "preserved" and kept on regeneration. Code below is fully managed. SectionParser (export_manager/section_parser.py) uses AST analysis to identify managed vs. preserved sections in existing files.
Built with Cyclopts. Commands: validate, generate, analyze, doctor, migrate, status, clear-cache. The generate command loads rules from .codeweaver/lateimport_rules.yaml by default (falling back to built-in defaults if absent).
- Formatter/linter: ruff (line length 100, Google-style docstrings, Python 3.12+ target)
- Type checker:
ty(Astral) — configured inpyproject.tomlunder[tool.ty] - Imports:
from __future__ import annotationsat top of all modules;TYPE_CHECKINGblocks for type-only imports - License headers: SPDX headers required on all source files (
MIT OR Apache-2.0) - Test files have relaxed ruff rules (see
ruff.toml[lint.per-file-ignores])