project-packer
Pack an entire multi-file Python project into a single portable .py file — and unpack it anywhere.
Why?
Copying a whole project to share with someone (or an AI assistant) usually means
zipping folders, juggling .gitignore rules, and hoping nothing important is
missed. project-packer flattens the whole thing into one self-describing
.py file you can paste into a chat, email, or terminal.
- Single file out — no zip, no tar, no directory tree to reassemble.
- Manifest-aware — respects
MANIFEST.insyntax, so it understands what's actually part of your Python package. - Setuptools-aware — auto-detects project name and version from
setup.py. - Zero dependencies — stdlib only. Runs on any Python 3.8+.
- Round-trippable —
unpackreconstructs the exact tree, folders and all.
Installation
No install required. Just drop packer.py anywhere and run it.
curl -O https://raw.githubusercontent.com/blee-design/project-packer/main/packer.py
python packer.py --help
Or install as a console script:
pip install git+https://github.com/blee-design/project-packer.git
packer --help
Quickstart
Pack a project
cd my-project
python packer.py pack
Creates my-project-1.2.0.py containing every file the manifest declares.
Unpack it
On any machine with Python 3.8+:
python packer.py unpack my-project-1.2.0.py
Recreates my-project-1.2.0/ with the full original tree.
Or skip packer.py entirely
The packed file is a valid Python file. Just paste its contents and the recipient can unpack it with the same tool.
CLI reference
pack
python packer.py pack [root] [options]
| Arg | Default | Meaning |
|---|---|---|
root |
. |
Project root. Must contain setup.py or .git (unless --force). |
-o, --output FILE |
<name>-<version>.py |
Output filename. |
-v, --verbose |
off | Print every file decision with reasoning. |
-f, --force |
off | Pack even without setup.py / .git. |
--respect-gitignore |
off | Also apply .gitignore exclusions. |
--manifest FILE |
auto | Use a custom manifest file. |
unpack
python packer.py unpack <packed_file> [target_dir] [options]
| Arg | Default | Meaning |
|---|---|---|
packed_file |
required | The .py file produced by pack. |
target_dir |
<name>-<version>/ |
Directory to recreate. Created if missing. |
-v, --verbose |
off | Print every file as it's written. |
Manifest format
project-packer understands the standard MANIFEST.in directive set. It looks
for packer.manifest first, then falls back to MANIFEST.in. Override with
--manifest FILE.
Supported directives
| Directive | Example | Meaning |
|---|---|---|
include |
include README.md |
Add a specific file. |
exclude |
exclude .env |
Remove a specific file. |
recursive-include |
recursive-include templates *.html |
Add matching files under a directory. |
recursive-exclude |
recursive-exclude tests *.py |
Remove matching files under a directory. |
graft |
graft docs |
Add an entire directory. |
prune |
prune build |
Remove an entire directory. |
global-include |
global-include *.py |
Add matching files anywhere in the tree. |
global-exclude |
global-exclude *.log |
Remove matching files anywhere. |
Lines starting with # are comments. See MANIFEST_Sample.in
for a worked example.
When there is no manifest
If neither packer.manifest nor MANIFEST.in exists, packer includes every
file that isn't matched by its default ignore list, plus a
handful of metadata files (setup.py, README.md, LICENSE, etc.) even if
they would normally be ignored.
Default ignores
Some files are always skipped, even if a manifest says to include them:
__pycache__/ .git/ .venv/ venv/ .tox/ .eggs/
dist/ build/ *.egg-info/ *.egg/ *.cache/
.pytest_cache/ .mypy_cache/ .ruff_cache/
*.pyc *.pyo *.so *.dll *.exe *.log *.tmp
*.json *.csv *.txt.bak *.pickle
cookies.txt youtube_token.pickle client_secrets.json gallery-dl.conf
*.bak *.backup *~ .DS_Store Thumbs.db
*.mp4 *.mkv *.avi *.mov *.webm *.tif *.tiff
Rationale: keep secrets out, keep media out, keep build artifacts out.
To include a file that matches these patterns, rename it or remove the pattern
from DEFAULT_IGNORE in packer.py.
.gitignore integration
By default .gitignore is not consulted — the manifest is the source of
truth. Pass --respect-gitignore to apply .gitignore exclusions on top of
whatever the manifest already included. Negated patterns (!keep-me.txt) are
honoured.
Packed-file format
See docs/format.md for the full specification. Short version:
# Project: my-project
# Version: 1.2.0
# Packed on: 2026-10-09 10:30:00
# Total files: 12
#======================================================================
# This file contains the complete source code.
# To unpack, use: python packer.py unpack <this_file>
#======================================================================
#---- FILE: setup.py ----
...contents...
#---- END FILE: setup.py ----
#---- FILE: my_pkg/core.py ----
...contents...
#---- END FILE: my_pkg/core.py ----
A third party could write a compatible unpacker in ~30 lines of any language. See the spec for the exact rules.
Use cases
- Share code with an AI assistant — one paste gives full context.
- Ship a project to a machine with no git — a USB stick and a Python interpreter is enough.
- Code golf for file transfer — smaller than the equivalent zip for text-heavy projects.
- Self-contained releases — commit the packed file alongside source.
Limitations
- Text only. Binary files (
*.png,*.mp4,.so, …) are recorded as a placeholder ([Binary file: path - content not shown]). They are not round-trippable. - No compression. Files are concatenated as-is. For large projects, use
tar+xz. - No integrity check. A truncated packed file unpacks silently. If this matters, add your own hash.
setup.pyparsing is AST-based, not executed. Dynamicversion=values (e.g., read from a file) are not resolved.
License
MIT © Udaya Raj Joshi — see LICENSE.
Metadata
Release files for project-packer 2.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| project_packer-2.0.2.tar.gz | 16.1 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| project_packer-2.0.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 26.4 kB
Release files / project_packer-2.0.2.tar.gz
| Download URL | project_packer-2.0.2.tar.gz |
|---|---|
| Size | 16.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7f6405e9919b43e46ca241e4c06c5e6afc479eb7963665a72e05c7fcd19da052
|
|
BLAKE2b-256 checksum How to use checksums |
9e0c56a3a47b453eec79ee6842d5ca657cac6afc07bb01ebc856e21f9f989eea
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|
Release files / project_packer-2.0.2-py3-none-any.whl
| Download URL | project_packer-2.0.2-py3-none-any.whl |
|---|---|
| Size | 10.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
98708f9589e31a63f16278281aad5d10e6528c1086c499058d4b93e712e60799
|
|
BLAKE2b-256 checksum How to use checksums |
02460b263f6b3516f0456eadbe9429e28ea209b05e3f28a230d3c9d0f5310c44
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|
Security for project-packer 2.0.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.