Skip to main content

A Python-to-executable compiler that just works.

Project description

Coil

Python License Platform Version

A Python-to-executable compiler that just works.


Why Coil?

Existing tools for turning Python projects into executables — PyInstaller, cx_Freeze, Nuitka, py2exe — all share the same problem: they're complicated. Hidden imports, missing DLLs, spec files, hook scripts, cryptic errors. You spend more time fighting the tool than building your app.

Coil takes a different approach: directory in, executable out. Point it at your project folder, and it handles the rest. No spec files. No hook scripts. No per-file configuration.

  • Auto-detects entry points
  • Auto-detects dependencies (or reads your requirements.txt)
  • Bundles an embedded Python runtime — no Python installation needed on the target machine
  • Produces a single portable .exe or a clean bundled directory
  • Built-in decompiler to recover your own source if you need it

Quick Start

pip install coil-compiler
coil init ./myproject        # Generate coil.toml config
coil build ./myproject       # Build your executable

Or, if coil isn't on your PATH:

python -m coil init ./myproject
python -m coil build ./myproject

That's it. Your executable is in ./dist/.

Installation

pip install coil-compiler

Requirements:

  • Python 3.9 or later
  • Windows (macOS and Linux support planned)
  • pip (for dependency installation during builds)

Windows note: If coil isn't recognized after install, your pip Scripts directory may not be on PATH. You can either:

  • Use python -m coil instead (works the same way — all examples below apply)
  • Or add Python's Scripts directory to your PATH (python -m site --user-site will show you where it is)

Usage

Basic Build

# Auto-detect entry point, build portable exe
coil build ./myproject

Specify Entry Point

coil build ./myproject --entry app.py

Portable Mode (Default)

Single standalone .exe. Copy it anywhere and run it. No installation needed.

coil build ./myproject --mode portable

Bundled Mode

Directory containing compiled application files. Multiple scripts become multiple compiled files.

coil build ./myproject --mode bundled

GUI Application

Coil auto-detects GUI frameworks. If your project imports tkinter, PyQt5, PyQt6, PySide2, PySide6, wx, kivy, pygame, pyglet, dearpygui, customtkinter, flet, pystray, infi.systray, or plyer, the console window is hidden automatically.

# No --gui needed — Coil detects tkinter/PyQt5/etc. and hides the console
coil build ./myproject

# Force console window even with GUI imports
coil build ./myproject --console

# Explicitly set GUI mode (redundant if auto-detected, but works)
coil build ./myproject --gui

Specify Python Version

coil build ./myproject --python 3.12

Dependency Control

# Exclude packages
coil build ./myproject --exclude numpy,pandas

# Force-include packages
coil build ./myproject --include extra-lib

# Use a specific requirements file
coil build ./myproject --requirements ./reqs.txt

Multiple Entry Points

Each entry point produces its own executable. In bundled mode, all entry points share the same runtime:

coil build ./myproject --entry cli.py --entry gui.py --mode bundled

GUI detection is per-entry — if gui.py imports PyQt5 but cli.py doesn't, only gui.exe will have its console hidden.

Secure Build

Heavy obfuscation. Cannot be reversed by coil decompile:

coil build ./myproject --secure

Decompile

Recover source from a default (non-secure) Coil build:

coil decompile ./dist/MyApp.exe --output ./recovered

Secure builds cannot be decompiled.

Clean Build

Build in an isolated environment with only declared dependencies. Guarantees reproducible builds:

coil build ./myproject --clean

The clean environment is cached — subsequent builds reuse it if dependencies haven't changed.

Project Setup

Generate a coil.toml config file for your project:

coil init ./myproject

This asks a few questions (entry point, console/GUI, icon) and writes a coil.toml with sensible defaults. After that, coil build ./myproject uses the config automatically — no flags needed.

Build Profiles

Define named profiles in coil.toml for different build scenarios:

[profile.dev]
mode = "bundled"
secure = false
verbose = true

[profile.release]
mode = "portable"
secure = true

Switch between them with --profile:

coil build ./myproject --profile dev
coil build ./myproject --profile release

CLI flags always override profile settings.

Pre-Build Diagnostics

Check for problems before building:

coil doctor ./myproject

Checks Python version, runtime availability, write permissions, config validity, and known package issues.

Build Preview

See what Coil will include in a build without building:

coil inspect ./myproject

Shows entry point, dependencies (stdlib vs third-party), estimated output size, and config.

Dry Run

See what would be built without building:

coil build ./myproject --dry-run --verbose

Full Example

coil build ./myproject \
  --entry main.py \
  --mode portable \
  --os windows \
  --python 3.12 \
  --gui \
  --secure \
  --exclude numpy,pandas \
  --output ./dist \
  --name MyApp \
  --icon ./assets/icon.ico

How It Works

  1. Dependency Resolution — Coil checks for a requirements.txt or pyproject.toml. If neither exists, it scans every .py file using Python's ast module to find all imports, separates stdlib from third-party, and resolves import names to PyPI packages.

  2. Runtime Bundling — Downloads the official Windows embeddable Python distribution matching your target version. No C compiler needed.

  3. Compilation — All .py files are compiled to .pyc bytecode using the target Python version. If your build machine runs a different Python version than your target, Coil delegates compilation to the embedded runtime so .pyc magic numbers always match. No loose .py files in the output.

  4. Packaging — In portable mode, everything is packed into a single .exe file. In bundled mode, a clean directory with the runtime, compiled code, and dependencies.

Portable Mode Details

The portable .exe is a single file you can copy anywhere and run. Here's what happens under the hood:

  • You distribute one file. The .exe contains a lightweight native launcher (~23 KB) with the full application payload appended.
  • First launch extracts the runtime to a local cache (%LOCALAPPDATA%\coil\<app>\<build_hash>\). This is a one-time operation.
  • Subsequent launches detect the cache and start instantly — no extraction needed.
  • Each build gets a unique hash. Rebuilding your app creates a new cache entry. Old cache entries are automatically cleaned up (only the 3 most recent are kept).
  • Cache safety: Extraction uses file locking to prevent corruption if multiple instances launch simultaneously. A marker file ensures only fully-extracted caches are used — if extraction is interrupted, it will restart cleanly.

To manage the cache manually:

coil cache info     # Show cache location and size
coil cache clear    # Delete all cached runtimes

Tip: If you have a requirements.txt or pyproject.toml, Coil uses that instead of scanning imports. This is faster and more reliable.

Configuration (coil.toml)

Run coil init to generate a config file, or create one manually:

[project]
entry = "main.py"
name = "MyApp"

[build]
mode = "portable"
os = "windows"
console = true
python = "3.12"
clean = false

[build.dependencies]
# Coil resolves deps automatically from requirements.txt or AST scan
auto = true
exclude = []
include = []

[build.output]
dir = "./dist"
icon = ""

# Profiles override [build] settings
# [profile.dev]
# mode = "bundled"
# secure = false
# verbose = true

# [profile.release]
# mode = "portable"
# secure = true

Priority order: CLI flags > profile values > [build] values > defaults.

CLI Reference

Subcommands

Command Description
coil build <project> Build a Python project into an executable
coil init [project] Generate a coil.toml config file
coil doctor [project] Run pre-build diagnostics
coil inspect [project] Preview what Coil will include in a build
coil decompile <exe> Recover source from a default Coil build
coil cache info Show cache location and size
coil cache clear Delete all cached runtimes

Build Flags

Flag Default Description
--entry Auto-detect (__main__.py then main.py) Entry point script relative to project dir
--mode portable portable (single exe) or bundled (directory)
--os Current OS windows, macos, linux
--python Auto-detect Target Python version
--gui false Suppress console window
--console true Show console window (default)
--secure false Heavy obfuscation, not reversible
--clean false Build in clean environment with only declared deps
--profile None Build profile from coil.toml
--exclude None Comma-separated packages to exclude
--include None Comma-separated packages to force-include
--output ./dist Output directory
--name Project dir name Output executable name
--icon None Icon file path (.ico)
--requirements Auto-detect Path to requirements.txt or pyproject.toml
--verbose false Detailed build output
--dry-run false Preview build without executing

Obfuscation

Default mode: Source is compiled to bytecode and packaged with metadata that coil decompile can use to recover the original .py files. This is a safety net — not cryptographic security, but your source isn't casually visible.

Secure mode (--secure): Bytecode only, debug info stripped, no recovery metadata. coil decompile will refuse to process it. This is stronger but not impenetrable — no bytecode obfuscation is NSA-proof. It's meant to raise the bar, not guarantee secrecy.

FAQ

Q: Does Coil require Python on the target machine? No. Coil bundles an embedded Python runtime. The resulting executable is fully standalone.

Q: What about C extensions / native modules? Coil bundles .pyd / .dll files from installed packages. Most packages with C extensions work out of the box.

Q: How big are the executables? A minimal project produces an exe around 15-20 MB (mostly the Python runtime). Dependencies add to that. Coil strips unnecessary files to keep size reasonable.

Q: Can I cross-compile for other platforms? Not yet. Currently Coil only builds Windows executables on Windows. Cross-platform and cross-compilation are on the roadmap.

Q: What's the difference between portable and bundled? Portable = single .exe file you can copy anywhere. On first launch, it extracts the runtime to a local cache and runs from there. Later launches reuse the cache and start instantly. Bundled = directory with the executable and its supporting files. No extraction step — it runs directly from the directory.

Q: Where does the portable exe store its cache? %LOCALAPPDATA%\coil\<AppName>\<build_hash>\. Run coil cache info to see details, or coil cache clear to remove it. If %LOCALAPPDATA% isn't available, it falls back to %TEMP% or the exe's own directory.

Q: Is the portable exe really a single file? Yes. The build output is a single .exe. On first run, it extracts a cached copy of the runtime to a local directory. This is not visible to the user as a separate step — the app just starts. Subsequent runs skip extraction entirely.

Roadmap

  • macOS .app support
  • Linux ELF binary support
  • ARM64 bootloader for Windows on ARM
  • Cross-compilation
  • Optional runtime signing (helps with AV false positives)

Contributing

Contributions welcome. Open an issue or submit a pull request.

# Development setup
git clone https://github.com/nathannncurtis/coil.git
cd coil
pip install -e .
python -m pytest

License

Apache License 2.0. See LICENSE for the full text.

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

coil_compiler-0.1.2.tar.gz (86.7 kB view details)

Uploaded Source

Built Distribution

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

coil_compiler-0.1.2-py3-none-any.whl (78.1 kB view details)

Uploaded Python 3

File details

Details for the file coil_compiler-0.1.2.tar.gz.

File metadata

  • Download URL: coil_compiler-0.1.2.tar.gz
  • Upload date:
  • Size: 86.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for coil_compiler-0.1.2.tar.gz
Algorithm Hash digest
SHA256 47eeb665b240b9c022f900bda81e327c2de7c8a02aee8654bafd21ad1bde3e5a
MD5 3006c9c437e4b94fb69190bc8e251b84
BLAKE2b-256 bdea56a54c569de3898f92681fbd1ce3fed03592020466519eaa113c95b1abc8

See more details on using hashes here.

File details

Details for the file coil_compiler-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: coil_compiler-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 78.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for coil_compiler-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 a9c0ac1ab9d5e19bbbe8e055b5a085c6a490d40e0ec18ee17037b8a0881d7195
MD5 c3a438f8664d841b6ab5277ea3100ccf
BLAKE2b-256 63a2f27ad26c26cf9807c882756320ff1e75d4985b6f034341bd3d6723a80611

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