Skip to main content

warmpy

Importing a big library takes seconds. Python pays that cost once per process, and a CLI starts a new process for every command, so a CLI pays it on every command. warmpy pays it once. The wrapped function runs in a background process that has already done its imports. The command you type becomes a small program that starts fast, sends its arguments to the background process, and shows the output.

A warmpy-decorated function creates a background process that starts on first use, serves later calls, and exits when idle. PersistentPerl used this trick for Perl CLIs twenty years ago, under the name pperl. warmpy is the same idea for Python

Install

pip install warmpy

warmpy needs unix sockets that can pass file descriptors, which macOS and Linux provide. Where they are absent, every call runs in the calling process, slowly, with the same results.

Use

from warmpy import warm_parse

@warm_parse
def main(
    path:str=None,  # File to process; stdin if omitted
):
    "Process a file"
    from .core import process   # the slow import goes inside the body
    ...

warm_parse takes the place of fastcore.script.call_parse. The function signature and its docments define the command line. Parsing happens in the calling process, so --help and argument errors never touch the background process. warm_parse(idle=1800, workers=4) sets how many seconds of disuse end the background process and how many worker processes may exist at once. pos names parameters that stay positional even with defaults, as in call_parse.

The module that holds the wrapped function must import fast. Heavy imports go inside the function body. A slow module makes every call slow, and warmpy cannot fix that.

The rule

Running through the background process changes nothing except speed. The function gets the same arguments, reads the same stdin, writes to the same terminal, sees the same working directory and the same environment variables, and produces the same exit code. Ctrl-C still stops it. The command hands its own stdin, stdout, and stderr to the background process, and sends its current directory and environment on every call. If the background process is missing or broken, the command imports the library and runs the function itself.

Processes

The first call starts a supervisor and one worker. The supervisor owns the socket and never imports your code. Each worker imports your code once, then serves one request at a time. When a request arrives and every worker is busy, the supervisor starts another worker, up to workers of them. After idle seconds without requests, the workers and the supervisor exit. Nobody manages these processes by hand.

Failures

Every failure has a fixed recovery, and none of them reaches the user. If the socket file exists but nothing answers for a second, the command deletes the file and starts fresh. If the background process was built from an older version of the package, the command tells it to exit and starts fresh; every connection begins with a version check. If two commands race to start the background process, one wins and the other connects to the winner. If a worker dies mid-request, that command falls back to running the function itself. The worst outcome warmpy permits is a slow one.

Stale code

The version check compares package versions, so an editable install that changed on disk is not detected. The background process keeps serving the code it imported. yourcommand --warmpy-stop ends it, and the next call starts fresh with the current code. yourcommand --warmpy-once (or WARMPY=0 yourcommand) runs one call in the current process and never starts or contacts the background process, which also makes it the right form for tests.

The function's obligations

The function must not depend on module-level variables it changed during earlier calls. In a new process those changes are gone. In the background process they persist. warmpy cannot see the difference.

Files

The socket and a log of background-process errors live under $XDG_RUNTIME_DIR, or ~/.cache/warmpy when that is unset. Each combination of function, Python executable, and package version gets its own socket.

Metadata

Release files for warmpy 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for warmpy 0.1.1
File Size Uploaded
warmpy-0.1.1.tar.gz 15.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for warmpy 0.1.1
File Interpreter ABI Platform
warmpy-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 26.7 kB

Release files / warmpy-0.1.1.tar.gz

Download URL warmpy-0.1.1.tar.gz
Size 15.2 kB
Tags Source
SHA-256 checksum
How to use checksums
f6932592d13f71062fde672360e4def87483d074ba2ad9b752b38707d5fec1db
BLAKE2b-256 checksum
How to use checksums
0b4bbde44d26735bf5e777c00f6929ce200248b6472d26c898fb8bd0ea521871
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.15

Release files / warmpy-0.1.1-py3-none-any.whl

Download URL warmpy-0.1.1-py3-none-any.whl
Size 11.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
049cd81dda50ee9408006e7972e3d646df72b60ec71d320327c9ea8ef56ea94f
BLAKE2b-256 checksum
How to use checksums
70c826ca84f14dadc4d00841d79e90cf2dd08b8148f37288968f07e2493a7ff1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.15

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page