Fast Windows process execution library.
Project description
fastrun
A Windows-only, quote-safe, string-based, feature-rich alternative to
Python's subprocess module, implemented as a native C extension around
CreateProcessW.
import fastrun
r = fastrun.run("python --version")
print(r.stdout) # 'Python 3.12.3\n'
print(r.returncode) # 0
Feature overview
| Feature | How |
|---|---|
| String-based commands, quote-safe | run("echo \"hello world\"") |
| No shell by default (safe) | direct CreateProcessW, no cmd.exe |
| Optional real shell mode | run(cmd, shell=True) -> cmd.exe /c ... |
| Timeouts | run(cmd, timeout=5.0) -> raises TimeoutExpired |
| Kill entire process tree on timeout | kill_tree=True (default), via Job Objects |
| Send data to child's stdin | run(cmd, input="some text") |
| Full environment replacement | run(cmd, env={...}) |
| Environment overlay (merge, don't replace) | run(cmd, extra_env={"FOO": "bar"}) |
| Custom working directory | run(cmd, cwd="C:\\path") |
| Redirect output straight to a file (no memory buffering) | run(cmd, stdout_file="out.log") |
Merge stderr into stdout (2>&1) |
run(cmd, merge_stderr=True) |
| Raise on non-zero exit | run(cmd, check=True) -> CalledProcessError |
| Text or raw bytes output | run(cmd, text=False) |
| Custom output encoding | run(cmd, encoding="cp1252") |
| Process priority | run(cmd, priority="below_normal") |
| Hide/show console window | run(cmd, hide_window=False) |
| PID of launched process | r.pid |
| Standalone quote-aware tokenizer | fastrun.split(cmdline) |
| Standalone Windows argv quoter | fastrun.quote(arg) |
| PATH resolution | fastrun.which("python") |
How it works
- Tokenizer (
fastrun.split) parses the command string into argv-like tokens:'single quotes',"double quotes with \" and \\ escapes", and bare backslashes treated as literal (soC:\Users\me\tool.exeneeds no escaping). - Re-quoter (
fastrun.quote) re-serializes tokens using the official Microsoft argv-quoting algorithm (the oneCommandLineToArgvWexpects -- the same one CPython's ownsubprocess.list2cmdlineimplements). - Launcher calls
CreateProcessWdirectly with redirected stdout/stderr pipes (or files), an optional stdin pipe, an optional merged/replaced environment block, an optional Job Object so timeouts kill the whole descendant tree (not just the immediate child), and optional priority-class / window-visibility control.
Full API
fastrun.run(cmdline, **options) -> CompletedProcess
fastrun.run(
cmdline, # str: the command line, e.g. "python --version"
cwd=None, # str | None: working directory
env=None, # dict[str,str] | None: full replacement environment
extra_env=None, # dict[str,str] | None: overlay on top of inherited env
timeout=None, # float | None: seconds before kill + TimeoutExpired
text=True, # bool: decode output to str (vs bytes)
encoding="utf-8", # str: decode/encode encoding
check=False, # bool: raise CalledProcessError on non-zero exit
shell=False, # bool: run via cmd.exe /c instead of our own parser
input=None, # str | bytes | None: data piped to child's stdin
kill_tree=True, # bool: timeout kills whole process tree via Job Object
stdout_file=None, # str | None: write stdout directly to this file path
stderr_file=None, # str | None: write stderr directly to this file path
merge_stderr=False, # bool: combine stderr into stdout (like 2>&1)
hide_window=True, # bool: suppress the child's console window
priority=None, # str | None: "idle" | "below_normal" | "normal"
# | "above_normal" | "high" | "realtime"
)
Returns a fastrun.CompletedProcess with:
.args-- the original command-line string.argv-- the parsed argument list.returncode-- process exit code.stdout/.stderr-- captured output (Noneif redirected to a file).pid-- the process ID that was assigned.timed_out--Trueif the timeout fired.check_returncode()-- raiseCalledProcessErrorif non-zero
fastrun.split(cmdline) -> list[str]
Standalone tokenizer access (like shlex.split, but Windows path-aware).
fastrun.quote(arg) -> str
Standalone Windows argv quoting for one argument.
fastrun.which(name) -> str | None
Resolve an executable name to a full path using Windows's own search
order (app dir -> cwd -> System32 -> Windows dir -> PATH), trying
.exe/.cmd/.bat/.com if no extension was given.
Exceptions
fastrun.CalledProcessError(returncode, cmd, output=None, stderr=None)fastrun.TimeoutExpired(cmd, output=None)
How to compile
You need Windows (this cannot be built or run on Linux/macOS -- it's a hard dependency on the Win32 API) plus:
- Python (3.8+), matching architecture (x64 recommended) -- the regular python.org installer or the Microsoft Store one both work, as long as they include development headers (they do by default).
- Microsoft Visual C++ Build Tools -- the compiler CPython extensions
require on Windows. Two ways to get it:
- Install Visual Studio Community (free) and select the "Desktop development with C++" workload during setup, or
- Install just the smaller "Build Tools for Visual Studio" package from Microsoft and select the C++ build tools workload.
- setuptools for your Python:
pip install setuptools wheel
Build steps
Open a terminal where the MSVC compiler is on PATH. The easiest way is
to use the Start Menu shortcut Visual Studio installs, called something
like "x64 Native Tools Command Prompt for VS 2022" (this pre-loads
all the right environment variables -- cl.exe, INCLUDE, LIB, etc.).
Using a plain cmd.exe/PowerShell without that shortcut usually fails
with error: Microsoft Visual C++ 14.0 or greater is required.
cd path\to\fastrun
python -m pip install --upgrade setuptools wheel
python setup.py build_ext --inplace
If it succeeds, you'll see a new file appear in the project root:
fastrun.cp312-win_amd64.pyd (exact name depends on your Python version)
That .pyd file is the compiled module -- Python can import it
directly like any .py file, as long as it's on sys.path (the project
root, or wherever you copy it, or your site-packages folder).
Verifying the build
python test_fastrun.py
This runs a small smoke-test suite covering the tokenizer, quoting,
return codes, check=True, and timeouts. All lines should print [PASS].
Building a redistributable wheel (optional)
If you want to pip install this on other Windows machines without them
needing MSVC installed:
python setup.py bdist_wheel
This produces a .whl file under dist\ that bundles the compiled
.pyd, installable via pip install dist\fastrun-1.0.0-*.whl on any
matching Windows/Python-version/architecture combination -- no compiler
needed on the target machine.
Common build errors
| Error | Fix |
|---|---|
Microsoft Visual C++ 14.0 or greater is required |
Install VS Build Tools with the C++ workload, and build from the "x64 Native Tools Command Prompt" |
fatal error C1083: Cannot open include file: 'Python.h' |
Your Python install is missing dev headers, or you're mixing a 32-bit Python with a 64-bit compiler prompt (or vice versa) -- match architectures |
LINK : fatal error LNK1181: cannot open input file 'python3xx.lib' |
Same architecture mismatch as above; reinstall Python for the matching architecture |
Import succeeds but crashes / ImportError: DLL load failed |
You built with a different Python minor version than you're importing into -- rebuild with the interpreter you intend to use |
How to use it
Basic run
import fastrun
r = fastrun.run("python --version")
print(r.returncode) # 0
print(r.stdout) # "Python 3.12.3\n"
Quotes just work
r = fastrun.run('echo "hello world"')
r = fastrun.run("tool.exe --path 'C:\\Program Files\\thing' --flag")
Timeouts that actually kill the whole tree
try:
fastrun.run("build.bat", timeout=30)
except fastrun.TimeoutExpired:
print("build took too long, and everything it spawned was killed too")
Piping input to a process
r = fastrun.run('python -c "import sys; print(sys.stdin.read().upper())"',
input="hello from python\n")
print(r.stdout) # "HELLO FROM PYTHON\n"
Environment control
# Full replacement -- child sees ONLY these variables
r = fastrun.run("set", shell=True, env={"PATH": r"C:\Windows\System32"})
# Overlay -- child inherits everything, plus these overrides
r = fastrun.run("python script.py", extra_env={"PYTHONUNBUFFERED": "1"})
Checking for failure
try:
fastrun.run('python -c "import sys; sys.exit(1)"', check=True)
except fastrun.CalledProcessError as e:
print(f"failed with code {e.args[0]}")
(CalledProcessError mirrors subprocess.CalledProcessError's
constructor order: returncode, cmd, output, stderr.)
Large output straight to a file
# Avoids buffering gigabytes of build output in memory
r = fastrun.run("dotnet build MySolution.sln", stdout_file="build.log",
stderr_file="build.err.log")
print(r.returncode)
# r.stdout / r.stderr are None here -- check the files instead
Merging stderr into stdout
r = fastrun.run('python -c "import sys; sys.stderr.write(\'oops\\n\')"',
merge_stderr=True)
print(r.stdout) # "oops\n"
Real shell semantics (wildcards, &&, env expansion)
# Only do this with trusted input -- shell=True reintroduces the
# injection risk that the default mode avoids.
r = fastrun.run("dir *.txt && echo done", shell=True)
Priority and window visibility
# Run a background batch job at low priority, no visible window
r = fastrun.run("cleanup.bat", priority="below_normal", hide_window=True)
# Launch something the user should actually see
r = fastrun.run("notepad.exe", hide_window=False)
PATH resolution
path = fastrun.which("python")
print(path) # "C:\\...\\Python312\\python.exe"
Using the tokenizer/quoter standalone
tokens = fastrun.split('tool --flag="a b" \'c d\' plain')
# ['tool', '--flag=a b', 'c d', 'plain']
quoted = fastrun.quote("has space")
# '"has space"'
Design notes / limitations
- No shell semantics by default.
&&,|,>, wildcards, and%VAR%expansion are not performed unless you passshell=True. - argv[0] resolution (non-shell mode) goes through normal Windows
executable search behavior (app dir, current dir, System32, Windows
dir,
PATH). - Output capture uses a polling read loop (
PeekNamedPipe+Sleep(5)when idle) rather than overlapped I/O or dedicated reader threads -- small, auditable, at the cost of a few ms latency on very chatty long-running processes. Usestdout_file/stderr_filefor very large output instead of in-memory capture. kill_treedepends on Job Objects; ifCreateJobObjectWorAssignProcessToJobObjectfails (e.g. restrictive sandbox, or the process is already in a job on some older systems), it silently falls back to killing just the direct child.- Max 512 arguments per command by default (
FASTRUN_MAX_ARGSinfastrun.c) -- bump the constant and rebuild if you need more. envandextra_envare mutually exclusive; if both are given,envwins andextra_envis ignored.
License
Do whatever you want with it. No warranty.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fastrun_win-1.0.0.tar.gz.
File metadata
- Download URL: fastrun_win-1.0.0.tar.gz
- Upload date:
- Size: 29.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
29aaba76c924d45b996bd4dc3c3b0104e17c7f0e0cfcb21253855a273b961ceb
|
|
| MD5 |
3698e5dd0d4eea6e05e7ee03c71cb092
|
|
| BLAKE2b-256 |
90a7edbea87263bbc357dc039877f326d9213ec7aeaf9435bf5780717194478e
|
File details
Details for the file fastrun_win-1.0.0-cp314-cp314-win_amd64.whl.
File metadata
- Download URL: fastrun_win-1.0.0-cp314-cp314-win_amd64.whl
- Upload date:
- Size: 26.4 kB
- Tags: CPython 3.14, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0c91564abb71e92ef12ec1b3c7ab14e543ea607ed8ee4504494805c90c73ef5b
|
|
| MD5 |
0c15051c0024dba9f8deb25fb2427b0a
|
|
| BLAKE2b-256 |
10201f9114467ee6cba110595f9bc194416fdcfeb8c51e810556b4584abae70b
|