Skip to main content

Pack a directory of text files into one self-extracting Python script, and unpack it back.

Project description

utfbundle

Packs a directory of text files into a single Python script, and unpacks it back into an identical directory tree.

Useful for uploading a folder to GitHub Gist, ChatGPT, Claude Projects, or any other tool that only accepts flat files, single files, or text blocks. These interfaces typically flatten directory structure on upload, so anything nested under subdirectories loses its path once it's in context.

Reaching for an archiver might be an option in some of these cases, unless text files inside the archive need to be read by a bot or AI integration.

utfbundle writes a directory's contents into one .py file, with each original file's path and content stored inside it as plain text where possible, or base64 for anything that isn't valid UTF-8. Running that file writes every file back out, unchanged, to a directory of your choosing. Packing and unpacking are inverses. Files come back out byte-for-byte identical, whichever form they were stored in.

Not intended to be a general-purpose archiver. No compression, no encryption. tar or zip already cover those use cases. This is specifically for the case where the constraint is "one plain text file, exactly reversible" including directories that have a few binary files mixed in with the text.

Install

pip install utfbundle

This installs the utfbundle command (and pulls in pathspec as a dependency automatically).

Requirements

  • Python 3.8+
  • pathspec (installed automatically with the package above; pip install pathspec if running from source)

Quick start

Pack the current directory:

[python3] utfbundle

This writes <dirname>_bundle.py in the current directory.

Reconstruct the tree later by running the bundle:

python3 <dirname>_bundle.py

With no arguments, it writes back into .. Packing . and running the resulting bundle from the same location are complements. Pass a path to extract elsewhere instead:

python3 <dirname>_bundle.py /path/to/restore/into

You can also pack a specific directory rather than the current one:

[python3] utfbundle path/to/project -o project_bundle.py

Here, since a named directory was packed rather than ., the bundle's default destination is a subdirectory named after it. Running project_bundle.py with no arguments elsewhere recreates path/to/project/ as project/.

Choosing what's included: .bundlespec

By default, packing includes everything except common junk (.git/, node_modules/, target/, __pycache__/, .DS_Store, prior bundle files). To control this directly, add a .bundlespec file at the root of the directory you're packing.

The syntax is .gitignore's glob syntax, inverted. A .gitignore excludes by default, so a bare line excludes and ! re-includes. A .bundlespec includes by default, so a bare line includes, and ! excludes something an earlier line already matched:

# what to bundle
src/**
assets/**
Cargo.toml

# exceptions
!target/
!node_modules/
!*.log
!secrets.rs

Rules apply top to bottom; the last matching rule for a given path wins. List broad includes first, then narrow with !.

Command-line reference

[python3] utfbundle [src_dir] [-o bundle.py] [-s SPEC] [-p PATTERN ...] [-n NAME]
Flag Meaning
src_dir Directory to pack. Defaults to .
-o, --output Output bundle path. Defaults to <name>_bundle.py
-s, --spec Path to a patterns file. Defaults to <src_dir>/.bundlespec if present
-p, --pattern An extra pattern, may be repeated; applied after the spec file
-n, --project-name Name used for the bundle file and default extract destination

Limitations

  • Binary files are supported, but not free. Each file is read as UTF-8 text; if that fails, it's stored instead as base64 inside the bundle (and a note is printed to stderr) and decoded back to exact bytes on extract. Either way the round-trip is exact, but base64 content isn't meant to be read by eye the way the text entries are, and it inflates that file's size by roughly a third.
  • No secret-scanning. It bundles whatever the patterns match. Keep credentials and .env files out via .bundlespec, the same way you would for a commit.
  • No compression. Bundle size is roughly the sum of the included files' sizes (plus the base64 overhead on any binary files).
  • Non-UTF-8 text is treated as binary. A file in Latin-1, Windows-1252, UTF-16, etc. will fail the UTF-8 decode check and get stored as base64 rather than as a readable literal. It still round-trips exactly. This only affects whether it's human-readable inside the bundle file.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

utfbundle-0.1.1.tar.gz (8.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

utfbundle-0.1.1-py3-none-any.whl (8.7 kB view details)

Uploaded Python 3

File details

Details for the file utfbundle-0.1.1.tar.gz.

File metadata

  • Download URL: utfbundle-0.1.1.tar.gz
  • Upload date:
  • Size: 8.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.5

File hashes

Hashes for utfbundle-0.1.1.tar.gz
Algorithm Hash digest
SHA256 661b989fa7c2cca3180022e5505aadd4a296187ab3c224b37593049bfb1faea3
MD5 c36fdb62faed2ae2d4d0cf0a13f35f5d
BLAKE2b-256 e2ec2841683ec6b32c7cbce9b4997f3e2ad43b95a77c6f0f885dadb47945b5c0

See more details on using hashes here.

File details

Details for the file utfbundle-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: utfbundle-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 8.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.5

File hashes

Hashes for utfbundle-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 f6a5f9073e1dd8d32bc90819d46889ca8b58f5d1cd6f6d6ac1855743362d412f
MD5 5bc859412876a88c5dfc4784fb19c57b
BLAKE2b-256 b5b2aaddfb2f634d0a06a4f50d0958b7c7163b24c421b44a3c021267b2f2414b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page