Name | cribo JSON |
Version |
0.5.11
JSON |
| download |
home_page | None |
Summary | Python source bundler that produces a single .py file from multi-module projects |
upload_time | 2025-08-09 14:27:16 |
maintainer | None |
docs_url | None |
author | None |
requires_python | >=3.8 |
license | None |
keywords |
bundler
python
deployment
pyspark
lambda
|
VCS |
 |
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# Cribo: Python Source Bundler
[](https://pypi.org/project/cribo/)
[](https://www.npmjs.com/package/cribo)
[](https://codecov.io/gh/ophidiarium/cribo)
[](https://sonarcloud.io/summary/new_code?id=ophidiarium_cribo)
[](https://opensource.org/licenses/MIT)
**Cribo** is a Rust-based CLI tool that, via fast, heuristically proven bundling, consolidates a scattered Python codebaseβfrom a single entry point or monorepoβinto one idiomatic `.py` file. This not only streamlines deployment in environments like PySpark, AWS Lambda, and notebooks but also makes ingesting Python codebases into AI models easier and more cost-effective while preserving full functional insights.
## What is "Cribo"?
*Cribo* is named after the [Mussurana snake](https://a-z-animals.com/animals/mussurana-snake/) (*Clelia clelia*), nicknamed "Cribo" in Latin America. Just like the real Cribo specializes in hunting and neutralizing venomous snakes (with a diet that's 70-80% other snakes!), our tool wrangles Python dependencies and circular imports with ease. Brazilian farmers even keep Cribos around for natural pest controlβthink of this as the Python ecosystem's answer to dependency chaos. In short:*Cribo eats tricky imports for breakfast, so your code doesn't have to*!
## Features
- π¦ **Rust-based CLI** based on Ruff's Python AST parser
- π Can be installed via `pip install cribo` or `npm install cribo`
- π Contemporary minds can also use `uvx cribo` or `bunx cribo`
- π² **Tree-shaking** (enabled by default) to inline only the modules that are actually used
- π **Circular dependency resolution** using Tarjan's strongly connected components (SCC) analysis and function-level lazy import transformations, with detailed diagnostics
- π§Ή **Unused import trimming** to clean up Python files standalone
- π¦ **Requirements generation** with optional `requirements.txt` output
- π§ **Configurable** import classification and source directories
- π **Fast** and memory-efficient
- π **Performance tracking** with built-in benchmarking
## Installation
> **π Supply Chain Security**: All npm and pypi packages include provenance attestations for enhanced security and verification.
### From PyPI (Python Package)
```bash
pip install cribo
```
### From npm (Node.js CLI)
```bash
# Global installation
npm install -g cribo
# One-time use
bunx cribo --help
```
### Binary Downloads
Download pre-built binaries for your platform from the [latest release](https://github.com/ophidiarium/cribo/releases/latest):
- **Linux x86_64**: `cribo_<version>_linux_x86_64.tar.gz`
- **Linux ARM64**: `cribo_<version>_linux_arm64.tar.gz`
- **macOS x86_64**: `cribo_<version>_darwin_x86_64.tar.gz`
- **macOS ARM64**: `cribo_<version>_darwin_arm64.tar.gz`
- **Windows x86_64**: `cribo_<version>_windows_x86_64.zip`
- **Windows ARM64**: `cribo_<version>_windows_arm64.zip`
Each binary includes a SHA256 checksum file for verification.
### Package Manager Installation
#### Aqua
If you use [Aqua](https://aquaproj.github.io/), add to your `aqua.yaml`:
```yaml
registries:
- type: standard
ref: latest
packages:
- name: ophidiarium/cribo@latest
```
Then run:
```bash
aqua install
```
#### UBI (Universal Binary Installer)
Using [UBI](https://github.com/houseabsolute/ubi):
```bash
# Install latest version
ubi --project ophidiarium/cribo
# Install specific version
ubi --project ophidiarium/cribo --tag v0.4.1
# Install to specific directory
ubi --project ophidiarium/cribo --in /usr/local/bin
```
### From Source
```bash
git clone https://github.com/ophidiarium/cribo.git
cd cribo
cargo build --release
```
## Quick Start
### Command Line Usage
```bash
# Basic bundling
cribo --entry src/main.py --output bundle.py
# Bundle a package directory (looks for __main__.py or __init__.py)
cribo --entry mypackage/ --output bundle.py
# Generate requirements.txt
cribo --entry src/main.py --output bundle.py --emit-requirements
# Verbose output (can be repeated for more detail: -v, -vv, -vvv)
cribo --entry src/main.py --output bundle.py -v
cribo --entry src/main.py --output bundle.py -vv # debug level
cribo --entry src/main.py --output bundle.py -vvv # trace level
# Custom config file
cribo --entry src/main.py --output bundle.py --config my-cribo.toml
```
### CLI Options
- `-e, --entry <PATH>`: Entry point Python script or package directory (required). When pointing to a directory, Cribo will look for `__main__.py` first, then `__init__.py`
- `-o, --output <PATH>`: Output bundled Python file (required)
- `-v, --verbose...`: Increase verbosity level. Can be repeated for more detail:
- No flag: warnings and errors only
- `-v`: informational messages
- `-vv`: debug messages
- `-vvv` or more: trace messages
- `-c, --config <PATH>`: Custom configuration file path
- `--emit-requirements`: Generate requirements.txt with third-party dependencies
- `--no-tree-shake`: Disable tree-shaking optimization (tree-shaking is enabled by default)
- `--target-version <VERSION>`: Target Python version (e.g., py38, py39, py310, py311, py312, py313)
- `-h, --help`: Print help information
- `-V, --version`: Print version information
The verbose flag is particularly useful for debugging bundling issues. Each level provides progressively more detail:
```bash
# Default: only warnings and errors
cribo --entry main.py --output bundle.py
# Info level: shows progress messages
cribo --entry main.py --output bundle.py -v
# Debug level: shows detailed processing steps
cribo --entry main.py --output bundle.py -vv
# Trace level: shows all internal operations
cribo --entry main.py --output bundle.py -vvv
```
The verbose levels map directly to Rust's log levels and can also be controlled via the `RUST_LOG` environment variable for more fine-grained control:
```bash
# Equivalent to -vv
RUST_LOG=debug cribo --entry main.py --output bundle.py
# Module-specific logging
RUST_LOG=cribo::bundler=trace,cribo::resolver=debug cribo --entry main.py --output bundle.py
```
### Tree-Shaking
Tree-shaking is enabled by default to reduce bundle size by removing unused code:
```bash
# Bundle with tree-shaking (default behavior)
cribo --entry main.py --output bundle.py
# Disable tree-shaking to include all code
cribo --entry main.py --output bundle.py --no-tree-shake
```
**How it works:**
- Analyzes your code starting from the entry point
- Tracks which functions, classes, and variables are actually used
- Removes unused symbols while preserving functionality
- Respects `__all__` declarations and module side effects
- Preserves all symbols from directly imported modules (`import module`)
**When to disable tree-shaking:**
- If you encounter undefined symbol errors with complex circular dependencies
- When you need to preserve all code for dynamic imports or reflection
- For debugging purposes to see the complete bundled output
## Configuration
Cribo supports hierarchical configuration with the following precedence (highest to lowest):
1. **CLI-provided config** (`--config` flag)
2. **Environment variables** (with `CRIBO_` prefix)
3. **Project config** (`cribo.toml` in current directory)
4. **User config** (`~/.config/cribo/cribo.toml`)
5. **System config** (`/etc/cribo/cribo.toml` on Unix, `%SYSTEMDRIVE%\ProgramData\cribo\cribo.toml` on Windows)
6. **Default values**
### Configuration File Format
Create a `cribo.toml` file:
```toml
# Source directories to scan for first-party modules
src = ["src", ".", "lib"]
# Known first-party module names
known_first_party = [
"my_internal_package",
]
# Known third-party module names
known_third_party = [
"requests",
"numpy",
"pandas",
]
# Whether to preserve comments in the bundled output
preserve_comments = true
# Whether to preserve type hints in the bundled output
preserve_type_hints = true
# Target Python version for standard library checks
# Supported: "py38", "py39", "py310", "py311", "py312", "py313"
target-version = "py310"
```
### Environment Variables
All configuration options can be overridden using environment variables with the `CRIBO_` prefix:
```bash
# Comma-separated lists
export CRIBO_SRC="src,lib,custom_dir"
export CRIBO_KNOWN_FIRST_PARTY="mypackage,myotherpackage"
export CRIBO_KNOWN_THIRD_PARTY="requests,numpy"
# Boolean values (true/false, 1/0, yes/no, on/off)
export CRIBO_PRESERVE_COMMENTS="false"
export CRIBO_PRESERVE_TYPE_HINTS="true"
# String values
export CRIBO_TARGET_VERSION="py312"
```
### Configuration Locations
- **Project**: `./cribo.toml`
- **User**:
- Linux/macOS: `~/.config/cribo/cribo.toml`
- Windows: `%APPDATA%\cribo\cribo.toml`
- **System**:
- Linux/macOS: `/etc/cribo/cribo.toml` or `/etc/xdg/cribo/cribo.toml`
- Windows: `%SYSTEMDRIVE%\ProgramData\cribo\cribo.toml`
## How It Works
1. **Module Discovery**: Scans configured source directories to discover first-party Python modules
2. **Import Classification**: Classifies imports as first-party, third-party, or standard library
3. **Dependency Graph**: Builds a dependency graph and performs topological sorting
4. **Circular Dependency Resolution**: Detects and intelligently resolves function-level circular imports
5. **Tree Shaking**: Removes unused code by analyzing which symbols are actually used (enabled by default)
6. **Code Generation**: Generates a single Python file with proper module separation
7. **Requirements**: Optionally generates `requirements.txt` with third-party dependencies
## Output Structure
The bundled output follows this structure:
```python
#!/usr/bin/env python3
# Generated by Cribo - Python Source Bundler
# Preserved imports (stdlib and third-party)
import os
import sys
import requests
# β Module: utils/helpers.py β
def greet(name: str) -> str:
return f"Hello, {name}!"
# β Module: models/user.py β
class User:
def **init**(self, name: str):
self.name = name
# β Entry Module: main.py β
from utils.helpers import greet
from models.user import User
def main():
user = User("Alice")
print(greet(user.name))
if **name** == "**main**":
main()
```
## Use Cases
### PySpark Jobs
Deploy complex PySpark applications as a single file:
```bash
cribo --entry spark_job.py --output dist/spark_job_bundle.py --emit-requirements
spark-submit dist/spark_job_bundle.py
```
### AWS Lambda
Package Python Lambda functions with all dependencies:
```bash
cribo --entry lambda_handler.py --output deployment/handler.py
# Upload handler.py + requirements.txt to Lambda
```
## Special Considerations
### Pydantic Compatibility
Cribo preserves class identity and module structure to ensure Pydantic models work correctly:
```python
# Original: models/user.py
class User(BaseModel):
name: str
# Bundled output preserves **module** and class structure
```
### Pandera Decorators
Function and class decorators are preserved with their original module context:
```python
# Original: validators/schemas.py
@pa.check_types
def validate_dataframe(df: DataFrame[UserSchema]) -> DataFrame[UserSchema]:
return df
# Bundled output maintains decorator functionality
```
### Circular Dependencies
Cribo intelligently handles circular dependencies with advanced detection and resolution:
#### Resolvable Cycles (Function-Level)
Function-level circular imports are automatically resolved and bundled successfully:
```python
# module_a.py
from module_b import process_b
def process_a(): return process_b() + "->A"
# module_b.py
from module_a import get_value_a
def process_b(): return f"B(using_{get_value_a()})"
```
**Result**: β
Bundles successfully with warning log
#### Unresolvable Cycles (Module Constants)
Temporal paradox patterns are detected and reported with detailed diagnostics:
```python
# constants_a.py
from constants_b import B_VALUE
A_VALUE = B_VALUE + 1 # β Unresolvable
# constants_b.py
from constants_a import A_VALUE
B_VALUE = A_VALUE * 2 # β Temporal paradox
```
**Result**: β Fails with detailed error message and resolution suggestions:
```bash
Unresolvable circular dependencies detected:
Cycle 1: constants_b β constants_a
Type: ModuleConstants
Reason: Module-level constant dependencies create temporal paradox - cannot be resolved through bundling
```
## Comparison with Other Tools
| Tool | Language | Tree Shaking | Import Cleanup | Circular Deps | PySpark Ready | Type Hints |
| ----------- | -------- | ------------ | -------------- | ------------------- | ------------- | ---------- |
| Cribo | Rust | β
Default | β
| β
Smart Resolution | β
| β
|
| PyInstaller | Python | β | β | β Fails | β | β
|
| Nuitka | Python | β | β | β Fails | β | β
|
| Pex | Python | β | β | β Fails | β | β
|
## Development
### Building from Source
```bash
git clone https://github.com/ophidiarium/cribo.git
cd cribo
# Build Rust CLI
cargo build --release
# Build Python package
pip install maturin
maturin develop
# Run tests
cargo test
```
### Performance Benchmarking
Cribo uses [Bencher.dev](https://bencher.dev) for comprehensive performance tracking with statistical analysis and regression detection:
```bash
# Run all benchmarks
cargo bench
# Save a performance baseline
./scripts/bench.sh --save-baseline main
# Compare against baseline
./scripts/bench.sh --baseline main
# View detailed HTML report
./scripts/bench.sh --open
```
**Key benchmarks:**
- **End-to-end bundling**: Full project bundling performance (Criterion.rs)
- **AST parsing**: Python code parsing speed (Criterion.rs)
- **Module resolution**: Import resolution efficiency (Criterion.rs)
- **CLI performance**: Command-line interface speed (Hyperfine)
**CI Integration:**
- Automated PR comments with performance comparisons and visual charts
- Historical performance tracking with trend analysis
- Statistical significance testing to prevent false positives
- Dashboard available at [bencher.dev/perf/cribo](https://bencher.dev/perf/cribo)
See [docs/benchmarking.md](docs/benchmarking.md) for detailed benchmarking guide.
### Project Structure
```text
cribo/
βββ src/ # Rust source code
β βββ main.rs # CLI entry point
β βββ orchestrator.rs # Bundle orchestration and coordination
β βββ code_generator.rs # Python code generation (sys.modules approach)
β βββ resolver.rs # Import resolution and classification
β βββ cribo_graph.rs # Advanced dependency graph with item-level tracking
β βββ graph_builder.rs # AST to dependency graph bridge
β βββ tree_shaking.rs # Dead code elimination (enabled by default)
β βββ semantic_analysis.rs # Enhanced import and symbol analysis
β βββ ast_indexer.rs # Deterministic AST node indexing
β βββ unused_imports.rs # Legacy import cleanup
β βββ visitors/ # AST visitors for various analyses
β β βββ import_discovery.rs
β β βββ side_effect_detector.rs
β β βββ no_ops_removal.rs
β βββ ...
βββ python/cribo/ # Python package
βββ tests/ # Test suites
β βββ fixtures/ # Test projects
βββ docs/ # Documentation
βββ Cargo.toml # Rust dependencies
```
## Contributing
### Development Setup
```bash
# Clone the repository
git clone https://github.com/ophidiarium/cribo.git
cd cribo
# Install Rust toolchain and components
rustup component add llvm-tools-preview
cargo install cargo-llvm-cov
# Build Rust CLI
cargo build --release
# Build Python package
pip install maturin
maturin develop
# Run tests
cargo test
```
### Code Coverage
The project uses `cargo-llvm-cov` for code coverage analysis:
```bash
# Generate text coverage report (Istanbul-style)
cargo coverage-text
# Generate HTML coverage report and open in browser
cargo coverage
# Generate LCOV format for CI
cargo coverage-lcov
# Clean coverage data
cargo coverage-clean
```
**Branch Coverage (Experimental)**:
```bash
# Requires nightly Rust for branch coverage
cargo +nightly coverage-branch
```
Coverage reports are automatically generated in CI and uploaded to Codecov. See [`docs/coverage.md`](docs/coverage.md) for detailed coverage documentation.
**Note**: If you see zeros in the "Branch Coverage" column in HTML reports, this is expected with stable Rust. Branch coverage requires nightly Rust and is experimental.
### Contributing Guidelines
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Add tests
5. Submit a pull request
## License
This project uses a dual licensing approach:
- **Source Code**: Licensed under the [MIT License](LICENSE)
- **Documentation**: Licensed under the [Creative Commons Attribution 4.0 International License (CC BY 4.0)](docs/LICENSE)
### What this means
- **For the source code**: You can freely use, modify, and distribute the code for any purpose with minimal restrictions under the MIT license.
- **For the documentation**: You can share, adapt, and use the documentation for any purpose (including commercially) as long as you provide appropriate attribution under CC BY 4.0.
See the [LICENSE](LICENSE) file for the MIT license text and [docs/LICENSE](docs/LICENSE) for the CC BY 4.0 license text.
## Acknowledgments
- **Ruff**: Python AST parsing and import resolution logic inspiration
- **Maturin**: Python-Rust integration
---
For more examples and detailed documentation, visit our [documentation site](https://github.com/ophidiarium/cribo#readme).
For detailed documentation on the unused import trimmer, see [`docs/unused_import_trimmer.md`](docs/unused_import_trimmer.md).
Raw data
{
"_id": null,
"home_page": null,
"name": "cribo",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "bundler, python, deployment, pyspark, lambda",
"author": null,
"author_email": "Konstantin Vyatkin <tino@vtkn.io>",
"download_url": "https://files.pythonhosted.org/packages/b1/df/361c6da1c2124ef5d991dafa729238f3eb4536cd27e5f307b8dc28a00b73/cribo-0.5.11.tar.gz",
"platform": null,
"description": "# Cribo: Python Source Bundler\n\n[](https://pypi.org/project/cribo/)\n[](https://www.npmjs.com/package/cribo)\n[](https://codecov.io/gh/ophidiarium/cribo)\n[](https://sonarcloud.io/summary/new_code?id=ophidiarium_cribo)\n[](https://opensource.org/licenses/MIT)\n\n**Cribo** is a Rust-based CLI tool that, via fast, heuristically proven bundling, consolidates a scattered Python codebase\u2014from a single entry point or monorepo\u2014into one idiomatic `.py` file. This not only streamlines deployment in environments like PySpark, AWS Lambda, and notebooks but also makes ingesting Python codebases into AI models easier and more cost-effective while preserving full functional insights.\n\n## What is \"Cribo\"?\n\n*Cribo* is named after the [Mussurana snake](https://a-z-animals.com/animals/mussurana-snake/) (*Clelia clelia*), nicknamed \"Cribo\" in Latin America. Just like the real Cribo specializes in hunting and neutralizing venomous snakes (with a diet that's 70-80% other snakes!), our tool wrangles Python dependencies and circular imports with ease. Brazilian farmers even keep Cribos around for natural pest control\u2014think of this as the Python ecosystem's answer to dependency chaos. In short:*Cribo eats tricky imports for breakfast, so your code doesn't have to*!\n\n## Features\n\n- \ud83e\udd80 **Rust-based CLI** based on Ruff's Python AST parser\n- \ud83d\udc0d Can be installed via `pip install cribo` or `npm install cribo`\n- \ud83d\ude0e Contemporary minds can also use `uvx cribo` or `bunx cribo`\n- \ud83c\udf32 **Tree-shaking** (enabled by default) to inline only the modules that are actually used\n- \ud83d\udd04 **Circular dependency resolution** using Tarjan's strongly connected components (SCC) analysis and function-level lazy import transformations, with detailed diagnostics\n- \ud83e\uddf9 **Unused import trimming** to clean up Python files standalone\n- \ud83d\udce6 **Requirements generation** with optional `requirements.txt` output\n- \ud83d\udd27 **Configurable** import classification and source directories\n- \ud83d\ude80 **Fast** and memory-efficient\n- \ud83d\udcca **Performance tracking** with built-in benchmarking\n\n## Installation\n\n> **\ud83d\udd10 Supply Chain Security**: All npm and pypi packages include provenance attestations for enhanced security and verification.\n\n### From PyPI (Python Package)\n\n```bash\npip install cribo\n```\n\n### From npm (Node.js CLI)\n\n```bash\n# Global installation\nnpm install -g cribo\n\n# One-time use\nbunx cribo --help\n```\n\n### Binary Downloads\n\nDownload pre-built binaries for your platform from the [latest release](https://github.com/ophidiarium/cribo/releases/latest):\n\n- **Linux x86_64**: `cribo_<version>_linux_x86_64.tar.gz`\n- **Linux ARM64**: `cribo_<version>_linux_arm64.tar.gz`\n- **macOS x86_64**: `cribo_<version>_darwin_x86_64.tar.gz`\n- **macOS ARM64**: `cribo_<version>_darwin_arm64.tar.gz`\n- **Windows x86_64**: `cribo_<version>_windows_x86_64.zip`\n- **Windows ARM64**: `cribo_<version>_windows_arm64.zip`\n\nEach binary includes a SHA256 checksum file for verification.\n\n### Package Manager Installation\n\n#### Aqua\n\nIf you use [Aqua](https://aquaproj.github.io/), add to your `aqua.yaml`:\n\n```yaml\nregistries:\n - type: standard\n ref: latest\npackages:\n - name: ophidiarium/cribo@latest\n```\n\nThen run:\n\n```bash\naqua install\n```\n\n#### UBI (Universal Binary Installer)\n\nUsing [UBI](https://github.com/houseabsolute/ubi):\n\n```bash\n# Install latest version\nubi --project ophidiarium/cribo\n\n# Install specific version\nubi --project ophidiarium/cribo --tag v0.4.1\n\n# Install to specific directory\nubi --project ophidiarium/cribo --in /usr/local/bin\n```\n\n### From Source\n\n```bash\ngit clone https://github.com/ophidiarium/cribo.git\ncd cribo\ncargo build --release\n```\n\n## Quick Start\n\n### Command Line Usage\n\n```bash\n# Basic bundling\ncribo --entry src/main.py --output bundle.py\n\n# Bundle a package directory (looks for __main__.py or __init__.py)\ncribo --entry mypackage/ --output bundle.py\n\n# Generate requirements.txt\ncribo --entry src/main.py --output bundle.py --emit-requirements\n\n# Verbose output (can be repeated for more detail: -v, -vv, -vvv)\ncribo --entry src/main.py --output bundle.py -v\ncribo --entry src/main.py --output bundle.py -vv # debug level\ncribo --entry src/main.py --output bundle.py -vvv # trace level\n\n# Custom config file\ncribo --entry src/main.py --output bundle.py --config my-cribo.toml\n```\n\n### CLI Options\n\n- `-e, --entry <PATH>`: Entry point Python script or package directory (required). When pointing to a directory, Cribo will look for `__main__.py` first, then `__init__.py`\n- `-o, --output <PATH>`: Output bundled Python file (required)\n- `-v, --verbose...`: Increase verbosity level. Can be repeated for more detail:\n - No flag: warnings and errors only\n - `-v`: informational messages\n - `-vv`: debug messages\n - `-vvv` or more: trace messages\n- `-c, --config <PATH>`: Custom configuration file path\n- `--emit-requirements`: Generate requirements.txt with third-party dependencies\n- `--no-tree-shake`: Disable tree-shaking optimization (tree-shaking is enabled by default)\n- `--target-version <VERSION>`: Target Python version (e.g., py38, py39, py310, py311, py312, py313)\n- `-h, --help`: Print help information\n- `-V, --version`: Print version information\n\nThe verbose flag is particularly useful for debugging bundling issues. Each level provides progressively more detail:\n\n```bash\n# Default: only warnings and errors\ncribo --entry main.py --output bundle.py\n\n# Info level: shows progress messages\ncribo --entry main.py --output bundle.py -v\n\n# Debug level: shows detailed processing steps\ncribo --entry main.py --output bundle.py -vv\n\n# Trace level: shows all internal operations\ncribo --entry main.py --output bundle.py -vvv\n```\n\nThe verbose levels map directly to Rust's log levels and can also be controlled via the `RUST_LOG` environment variable for more fine-grained control:\n\n```bash\n# Equivalent to -vv\nRUST_LOG=debug cribo --entry main.py --output bundle.py\n\n# Module-specific logging\nRUST_LOG=cribo::bundler=trace,cribo::resolver=debug cribo --entry main.py --output bundle.py\n```\n\n### Tree-Shaking\n\nTree-shaking is enabled by default to reduce bundle size by removing unused code:\n\n```bash\n# Bundle with tree-shaking (default behavior)\ncribo --entry main.py --output bundle.py\n\n# Disable tree-shaking to include all code\ncribo --entry main.py --output bundle.py --no-tree-shake\n```\n\n**How it works:**\n\n- Analyzes your code starting from the entry point\n- Tracks which functions, classes, and variables are actually used\n- Removes unused symbols while preserving functionality\n- Respects `__all__` declarations and module side effects\n- Preserves all symbols from directly imported modules (`import module`)\n\n**When to disable tree-shaking:**\n\n- If you encounter undefined symbol errors with complex circular dependencies\n- When you need to preserve all code for dynamic imports or reflection\n- For debugging purposes to see the complete bundled output\n\n## Configuration\n\nCribo supports hierarchical configuration with the following precedence (highest to lowest):\n\n1. **CLI-provided config** (`--config` flag)\n2. **Environment variables** (with `CRIBO_` prefix)\n3. **Project config** (`cribo.toml` in current directory)\n4. **User config** (`~/.config/cribo/cribo.toml`)\n5. **System config** (`/etc/cribo/cribo.toml` on Unix, `%SYSTEMDRIVE%\\ProgramData\\cribo\\cribo.toml` on Windows)\n6. **Default values**\n\n### Configuration File Format\n\nCreate a `cribo.toml` file:\n\n```toml\n# Source directories to scan for first-party modules\nsrc = [\"src\", \".\", \"lib\"]\n\n# Known first-party module names\nknown_first_party = [\n \"my_internal_package\",\n]\n\n# Known third-party module names\nknown_third_party = [\n \"requests\",\n \"numpy\",\n \"pandas\",\n]\n\n# Whether to preserve comments in the bundled output\npreserve_comments = true\n\n# Whether to preserve type hints in the bundled output\npreserve_type_hints = true\n\n# Target Python version for standard library checks\n# Supported: \"py38\", \"py39\", \"py310\", \"py311\", \"py312\", \"py313\"\ntarget-version = \"py310\"\n```\n\n### Environment Variables\n\nAll configuration options can be overridden using environment variables with the `CRIBO_` prefix:\n\n```bash\n# Comma-separated lists\nexport CRIBO_SRC=\"src,lib,custom_dir\"\nexport CRIBO_KNOWN_FIRST_PARTY=\"mypackage,myotherpackage\"\nexport CRIBO_KNOWN_THIRD_PARTY=\"requests,numpy\"\n\n# Boolean values (true/false, 1/0, yes/no, on/off)\nexport CRIBO_PRESERVE_COMMENTS=\"false\"\nexport CRIBO_PRESERVE_TYPE_HINTS=\"true\"\n\n# String values\nexport CRIBO_TARGET_VERSION=\"py312\"\n```\n\n### Configuration Locations\n\n- **Project**: `./cribo.toml`\n- **User**:\n - Linux/macOS: `~/.config/cribo/cribo.toml`\n - Windows: `%APPDATA%\\cribo\\cribo.toml`\n- **System**:\n - Linux/macOS: `/etc/cribo/cribo.toml` or `/etc/xdg/cribo/cribo.toml`\n - Windows: `%SYSTEMDRIVE%\\ProgramData\\cribo\\cribo.toml`\n\n## How It Works\n\n1. **Module Discovery**: Scans configured source directories to discover first-party Python modules\n2. **Import Classification**: Classifies imports as first-party, third-party, or standard library\n3. **Dependency Graph**: Builds a dependency graph and performs topological sorting\n4. **Circular Dependency Resolution**: Detects and intelligently resolves function-level circular imports\n5. **Tree Shaking**: Removes unused code by analyzing which symbols are actually used (enabled by default)\n6. **Code Generation**: Generates a single Python file with proper module separation\n7. **Requirements**: Optionally generates `requirements.txt` with third-party dependencies\n\n## Output Structure\n\nThe bundled output follows this structure:\n\n```python\n#!/usr/bin/env python3\n# Generated by Cribo - Python Source Bundler\n\n# Preserved imports (stdlib and third-party)\nimport os\nimport sys\nimport requests\n\n# \u2500 Module: utils/helpers.py \u2500\ndef greet(name: str) -> str:\n return f\"Hello, {name}!\"\n\n# \u2500 Module: models/user.py \u2500\nclass User:\n def **init**(self, name: str):\n self.name = name\n\n# \u2500 Entry Module: main.py \u2500\nfrom utils.helpers import greet\nfrom models.user import User\n\ndef main():\n user = User(\"Alice\")\n print(greet(user.name))\n\nif **name** == \"**main**\":\n main()\n```\n\n## Use Cases\n\n### PySpark Jobs\n\nDeploy complex PySpark applications as a single file:\n\n```bash\ncribo --entry spark_job.py --output dist/spark_job_bundle.py --emit-requirements\nspark-submit dist/spark_job_bundle.py\n```\n\n### AWS Lambda\n\nPackage Python Lambda functions with all dependencies:\n\n```bash\ncribo --entry lambda_handler.py --output deployment/handler.py\n# Upload handler.py + requirements.txt to Lambda\n```\n\n## Special Considerations\n\n### Pydantic Compatibility\n\nCribo preserves class identity and module structure to ensure Pydantic models work correctly:\n\n```python\n# Original: models/user.py\nclass User(BaseModel):\n name: str\n\n# Bundled output preserves **module** and class structure\n```\n\n### Pandera Decorators\n\nFunction and class decorators are preserved with their original module context:\n\n```python\n# Original: validators/schemas.py\n@pa.check_types\ndef validate_dataframe(df: DataFrame[UserSchema]) -> DataFrame[UserSchema]:\n return df\n\n# Bundled output maintains decorator functionality\n```\n\n### Circular Dependencies\n\nCribo intelligently handles circular dependencies with advanced detection and resolution:\n\n#### Resolvable Cycles (Function-Level)\n\nFunction-level circular imports are automatically resolved and bundled successfully:\n\n```python\n# module_a.py\nfrom module_b import process_b\ndef process_a(): return process_b() + \"->A\"\n\n# module_b.py \nfrom module_a import get_value_a\ndef process_b(): return f\"B(using_{get_value_a()})\"\n```\n\n**Result**: \u2705 Bundles successfully with warning log\n\n#### Unresolvable Cycles (Module Constants)\n\nTemporal paradox patterns are detected and reported with detailed diagnostics:\n\n```python\n# constants_a.py\nfrom constants_b import B_VALUE\nA_VALUE = B_VALUE + 1 # \u274c Unresolvable\n\n# constants_b.py\nfrom constants_a import A_VALUE \nB_VALUE = A_VALUE * 2 # \u274c Temporal paradox\n```\n\n**Result**: \u274c Fails with detailed error message and resolution suggestions:\n\n```bash\nUnresolvable circular dependencies detected:\n\nCycle 1: constants_b \u2192 constants_a\n Type: ModuleConstants\n Reason: Module-level constant dependencies create temporal paradox - cannot be resolved through bundling\n```\n\n## Comparison with Other Tools\n\n| Tool | Language | Tree Shaking | Import Cleanup | Circular Deps | PySpark Ready | Type Hints |\n| ----------- | -------- | ------------ | -------------- | ------------------- | ------------- | ---------- |\n| Cribo | Rust | \u2705 Default | \u2705 | \u2705 Smart Resolution | \u2705 | \u2705 |\n| PyInstaller | Python | \u274c | \u274c | \u274c Fails | \u274c | \u2705 |\n| Nuitka | Python | \u274c | \u274c | \u274c Fails | \u274c | \u2705 |\n| Pex | Python | \u274c | \u274c | \u274c Fails | \u274c | \u2705 |\n\n## Development\n\n### Building from Source\n\n```bash\ngit clone https://github.com/ophidiarium/cribo.git\ncd cribo\n\n# Build Rust CLI\ncargo build --release\n\n# Build Python package\npip install maturin\nmaturin develop\n\n# Run tests\ncargo test\n```\n\n### Performance Benchmarking\n\nCribo uses [Bencher.dev](https://bencher.dev) for comprehensive performance tracking with statistical analysis and regression detection:\n\n```bash\n# Run all benchmarks\ncargo bench\n\n# Save a performance baseline\n./scripts/bench.sh --save-baseline main\n\n# Compare against baseline\n./scripts/bench.sh --baseline main\n\n# View detailed HTML report\n./scripts/bench.sh --open\n```\n\n**Key benchmarks:**\n\n- **End-to-end bundling**: Full project bundling performance (Criterion.rs)\n- **AST parsing**: Python code parsing speed (Criterion.rs)\n- **Module resolution**: Import resolution efficiency (Criterion.rs)\n- **CLI performance**: Command-line interface speed (Hyperfine)\n\n**CI Integration:**\n\n- Automated PR comments with performance comparisons and visual charts\n- Historical performance tracking with trend analysis\n- Statistical significance testing to prevent false positives\n- Dashboard available at [bencher.dev/perf/cribo](https://bencher.dev/perf/cribo)\n\nSee [docs/benchmarking.md](docs/benchmarking.md) for detailed benchmarking guide.\n\n### Project Structure\n\n```text\ncribo/\n\u251c\u2500\u2500 src/ # Rust source code\n\u2502 \u251c\u2500\u2500 main.rs # CLI entry point\n\u2502 \u251c\u2500\u2500 orchestrator.rs # Bundle orchestration and coordination\n\u2502 \u251c\u2500\u2500 code_generator.rs # Python code generation (sys.modules approach)\n\u2502 \u251c\u2500\u2500 resolver.rs # Import resolution and classification\n\u2502 \u251c\u2500\u2500 cribo_graph.rs # Advanced dependency graph with item-level tracking\n\u2502 \u251c\u2500\u2500 graph_builder.rs # AST to dependency graph bridge\n\u2502 \u251c\u2500\u2500 tree_shaking.rs # Dead code elimination (enabled by default)\n\u2502 \u251c\u2500\u2500 semantic_analysis.rs # Enhanced import and symbol analysis\n\u2502 \u251c\u2500\u2500 ast_indexer.rs # Deterministic AST node indexing\n\u2502 \u251c\u2500\u2500 unused_imports.rs # Legacy import cleanup\n\u2502 \u251c\u2500\u2500 visitors/ # AST visitors for various analyses\n\u2502 \u2502 \u251c\u2500\u2500 import_discovery.rs\n\u2502 \u2502 \u251c\u2500\u2500 side_effect_detector.rs\n\u2502 \u2502 \u2514\u2500\u2500 no_ops_removal.rs\n\u2502 \u2514\u2500\u2500 ...\n\u251c\u2500\u2500 python/cribo/ # Python package\n\u251c\u2500\u2500 tests/ # Test suites\n\u2502 \u2514\u2500\u2500 fixtures/ # Test projects\n\u251c\u2500\u2500 docs/ # Documentation\n\u2514\u2500\u2500 Cargo.toml # Rust dependencies\n```\n\n## Contributing\n\n### Development Setup\n\n```bash\n# Clone the repository\ngit clone https://github.com/ophidiarium/cribo.git\ncd cribo\n\n# Install Rust toolchain and components\nrustup component add llvm-tools-preview\ncargo install cargo-llvm-cov\n\n# Build Rust CLI\ncargo build --release\n\n# Build Python package\npip install maturin\nmaturin develop\n\n# Run tests\ncargo test\n```\n\n### Code Coverage\n\nThe project uses `cargo-llvm-cov` for code coverage analysis:\n\n```bash\n# Generate text coverage report (Istanbul-style)\ncargo coverage-text\n\n# Generate HTML coverage report and open in browser\ncargo coverage\n\n# Generate LCOV format for CI\ncargo coverage-lcov\n\n# Clean coverage data\ncargo coverage-clean\n```\n\n**Branch Coverage (Experimental)**:\n\n```bash\n# Requires nightly Rust for branch coverage\ncargo +nightly coverage-branch\n```\n\nCoverage reports are automatically generated in CI and uploaded to Codecov. See [`docs/coverage.md`](docs/coverage.md) for detailed coverage documentation.\n\n**Note**: If you see zeros in the \"Branch Coverage\" column in HTML reports, this is expected with stable Rust. Branch coverage requires nightly Rust and is experimental.\n\n### Contributing Guidelines\n\n1. Fork the repository\n2. Create a feature branch\n3. Make your changes\n4. Add tests\n5. Submit a pull request\n\n## License\n\nThis project uses a dual licensing approach:\n\n- **Source Code**: Licensed under the [MIT License](LICENSE)\n- **Documentation**: Licensed under the [Creative Commons Attribution 4.0 International License (CC BY 4.0)](docs/LICENSE)\n\n### What this means\n\n- **For the source code**: You can freely use, modify, and distribute the code for any purpose with minimal restrictions under the MIT license.\n- **For the documentation**: You can share, adapt, and use the documentation for any purpose (including commercially) as long as you provide appropriate attribution under CC BY 4.0.\n\nSee the [LICENSE](LICENSE) file for the MIT license text and [docs/LICENSE](docs/LICENSE) for the CC BY 4.0 license text.\n\n## Acknowledgments\n\n- **Ruff**: Python AST parsing and import resolution logic inspiration\n- **Maturin**: Python-Rust integration\n\n---\n\nFor more examples and detailed documentation, visit our [documentation site](https://github.com/ophidiarium/cribo#readme).\n\nFor detailed documentation on the unused import trimmer, see [`docs/unused_import_trimmer.md`](docs/unused_import_trimmer.md).\n\n",
"bugtrack_url": null,
"license": null,
"summary": "Python source bundler that produces a single .py file from multi-module projects",
"version": "0.5.11",
"project_urls": {
"Documentation": "https://github.com/ophidiarium/cribo#readme",
"Homepage": "https://github.com/ophidiarium/cribo",
"Issues": "https://github.com/ophidiarium/cribo/issues",
"Repository": "https://github.com/ophidiarium/cribo"
},
"split_keywords": [
"bundler",
" python",
" deployment",
" pyspark",
" lambda"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "79d724f7c2ed95acac6739cb69e8d39c04af3265b4632ae5ed79c6966564fe53",
"md5": "d7aa896a1035735c28081f7a39943184",
"sha256": "047efb23269a7772a6f575acc04971086d76f6dcb3cbd4ce2b04d98f9735d1a7"
},
"downloads": -1,
"filename": "cribo-0.5.11-py3-none-macosx_10_12_x86_64.whl",
"has_sig": false,
"md5_digest": "d7aa896a1035735c28081f7a39943184",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 2791082,
"upload_time": "2025-08-09T14:27:01",
"upload_time_iso_8601": "2025-08-09T14:27:01.816913Z",
"url": "https://files.pythonhosted.org/packages/79/d7/24f7c2ed95acac6739cb69e8d39c04af3265b4632ae5ed79c6966564fe53/cribo-0.5.11-py3-none-macosx_10_12_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "8ffd4f099312fdeb12b3638b65f40185fea7c7ee572bf380d3c3322f379d12a1",
"md5": "547c0f88e8bb27901185f58653e7b245",
"sha256": "6da95efcfce5665e6576e9ad4fe105af1f88f6ec017dbaf3dbc1c9c17afcc0fb"
},
"downloads": -1,
"filename": "cribo-0.5.11-py3-none-macosx_11_0_arm64.whl",
"has_sig": false,
"md5_digest": "547c0f88e8bb27901185f58653e7b245",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 2591221,
"upload_time": "2025-08-09T14:27:04",
"upload_time_iso_8601": "2025-08-09T14:27:04.170882Z",
"url": "https://files.pythonhosted.org/packages/8f/fd/4f099312fdeb12b3638b65f40185fea7c7ee572bf380d3c3322f379d12a1/cribo-0.5.11-py3-none-macosx_11_0_arm64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "9e0ddcd32b994e301c8c7dfba881825759c8c0e8302c248684b95d1e0346fc64",
"md5": "bb5ce96525f810cc8c0bc09f547dba3e",
"sha256": "7e3c64ff9bd2769e9761b541df4dd47fa44e221b144ed9043c00446b49834614"
},
"downloads": -1,
"filename": "cribo-0.5.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
"has_sig": false,
"md5_digest": "bb5ce96525f810cc8c0bc09f547dba3e",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 2747273,
"upload_time": "2025-08-09T14:27:06",
"upload_time_iso_8601": "2025-08-09T14:27:06.421254Z",
"url": "https://files.pythonhosted.org/packages/9e/0d/dcd32b994e301c8c7dfba881825759c8c0e8302c248684b95d1e0346fc64/cribo-0.5.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "f2fb34228e084826dcf121f507e22609359193d8460141e126932277984ab9e1",
"md5": "f08200939f1354f223f8673be8064020",
"sha256": "9097d41a4f415ee190635c64a3dde00abec2b0b25e74989f1e873b010683343f"
},
"downloads": -1,
"filename": "cribo-0.5.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"has_sig": false,
"md5_digest": "f08200939f1354f223f8673be8064020",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 2906150,
"upload_time": "2025-08-09T14:27:08",
"upload_time_iso_8601": "2025-08-09T14:27:08.641706Z",
"url": "https://files.pythonhosted.org/packages/f2/fb/34228e084826dcf121f507e22609359193d8460141e126932277984ab9e1/cribo-0.5.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "c7f48ac22e63b5655b7e87b34fcd5b49f1a5cbe0568593fc1a9bd70be569edd0",
"md5": "e03c4f2c3ca431a7509d2b387a2551c4",
"sha256": "82a0d2aadcc4afbf88c9b7afcfe76335ac2088f57b46f450ae99338e8737d99e"
},
"downloads": -1,
"filename": "cribo-0.5.11-py3-none-manylinux_2_5_x86_64.manylinux1_x86_64.whl",
"has_sig": false,
"md5_digest": "e03c4f2c3ca431a7509d2b387a2551c4",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 2959089,
"upload_time": "2025-08-09T14:27:10",
"upload_time_iso_8601": "2025-08-09T14:27:10.756755Z",
"url": "https://files.pythonhosted.org/packages/c7/f4/8ac22e63b5655b7e87b34fcd5b49f1a5cbe0568593fc1a9bd70be569edd0/cribo-0.5.11-py3-none-manylinux_2_5_x86_64.manylinux1_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "706338fc00b63ade373f34f1dc19605e4d76fe617fc22b2dae6faa624df903df",
"md5": "d03aeb94c3de2e8e1454b7b355bc5c30",
"sha256": "fa6399bd3481e65ae48a79e24813eb49ff066a2d0af0aec1264c98bb56ad79cd"
},
"downloads": -1,
"filename": "cribo-0.5.11-py3-none-win_amd64.whl",
"has_sig": false,
"md5_digest": "d03aeb94c3de2e8e1454b7b355bc5c30",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 2743653,
"upload_time": "2025-08-09T14:27:12",
"upload_time_iso_8601": "2025-08-09T14:27:12.886569Z",
"url": "https://files.pythonhosted.org/packages/70/63/38fc00b63ade373f34f1dc19605e4d76fe617fc22b2dae6faa624df903df/cribo-0.5.11-py3-none-win_amd64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "563d3e714c85b445552f00a07334ecedd45a6f82ee8361f26263714c18c78c59",
"md5": "7a80ef2bd8c9c3c89f5d725e9d49246f",
"sha256": "98ef2dc053ecb6ca91998956666de5b7f33d43d16dbab8fdce4324a027531e57"
},
"downloads": -1,
"filename": "cribo-0.5.11-py3-none-win_arm64.whl",
"has_sig": false,
"md5_digest": "7a80ef2bd8c9c3c89f5d725e9d49246f",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 2554658,
"upload_time": "2025-08-09T14:27:14",
"upload_time_iso_8601": "2025-08-09T14:27:14.971657Z",
"url": "https://files.pythonhosted.org/packages/56/3d/3e714c85b445552f00a07334ecedd45a6f82ee8361f26263714c18c78c59/cribo-0.5.11-py3-none-win_arm64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "b1df361c6da1c2124ef5d991dafa729238f3eb4536cd27e5f307b8dc28a00b73",
"md5": "a198a8d5ebcb89b9d766d23d4cd8da3f",
"sha256": "2628f0724c4d81f65d73818bdf0c1d7f2f6a830118796cb40566486041c0d9a4"
},
"downloads": -1,
"filename": "cribo-0.5.11.tar.gz",
"has_sig": false,
"md5_digest": "a198a8d5ebcb89b9d766d23d4cd8da3f",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 414483,
"upload_time": "2025-08-09T14:27:16",
"upload_time_iso_8601": "2025-08-09T14:27:16.493102Z",
"url": "https://files.pythonhosted.org/packages/b1/df/361c6da1c2124ef5d991dafa729238f3eb4536cd27e5f307b8dc28a00b73/cribo-0.5.11.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-09 14:27:16",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "ophidiarium",
"github_project": "cribo#readme",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "cribo"
}