docs
A small CLI that bootstraps and manages Python projects. One binary, no Python
prerequisite — pyr downloads a standalone CPython into ~/.pyr/python on first
use and creates a project-local .venv from it.
pyr init myapp # scaffold a project (downloads python on first run)
cd myapp
pyr run # python app/main.py
pyr add httpx # add a dep, resolve, lock, install
pyr remove httpx # the obvious
pyr sync # reconcile venv + lock with pyproject.toml
pyr upgrade # update pyr itself
pyr upgrade --python # update the managed cpythonInstall
macOS / Linux
curl -fsSL https://pyrun.dev/install.sh | shThe script drops pyr into ~/.pyr/bin/. Add that to your PATH — the
installer prints the exact line for your shell.
Windows
In PowerShell:
irm https://pyrun.dev/install.ps1 | iexThis installs pyr.exe into %USERPROFILE%\.pyr\bin\. Add that directory to
your user PATH if it isn't already — the installer prints the exact command.
Or, manually: download the latest pyr-windows-x86_64.zip from
releases, unzip it, and put
pyr.exe somewhere on your PATH.
PYR_HOME overrides where pyr keeps its state and binary; defaults to ~/.pyr
(or %USERPROFILE%\.pyr on Windows).
How dependencies work
pyproject.toml [project].dependencies is the source of truth. You write what
you want there — either by hand, or via pyr add/pyr remove.
requirements.txt is a generated lock file (flat, alpha-sorted, fully pinned,
with a # generated by pyr header), produced by pyr sync.
pyproject.toml requirements.txt
[project] # generated by pyr 0.1.0; do not edit
dependencies = [ anyio==4.3.0
"httpx[http2]", certifi==2024.2.2
] h11==0.14.0
h2==4.1.0
httpcore==1.0.5
httpx==0.27.0pyr add httpx and pyr remove httpx both edit pyproject.toml then call
pyr sync under the hood. pyr sync resolves the top-level deps via pip,
prunes anything no longer needed, and rewrites the lock. pyr run auto-syncs if
pyproject.toml is newer than the lock — so hand-edits to
[project].dependencies followed by pyr run Just Work.
Commands
| command | what it does |
|---|---|
pyr init [name] |
Scaffold a project in ./<name>/ (or cwd). Refuses to overwrite existing content. |
pyr run [-- args] |
Run app/main.py in the venv with PYTHONPATH=.. Args after -- go to python. |
pyr add <pkg>... |
Add packages to pyproject.toml [project].dependencies, then sync. |
pyr remove <pkg>... |
Remove packages from pyproject.toml, then sync. |
pyr sync |
Reconcile the venv and lock with pyproject.toml. Idempotent. |
pyr upgrade |
Update the pyr binary from the latest GitHub release. |
pyr upgrade --python |
Update the managed CPython in ~/.pyr/python. |
pyr help [cmd] |
Top-level usage, or per-command details. |
pyr <cmd> --help and pyr help <cmd> are equivalent. To pass a literal
--help (or any flag starting with -) to python via pyr run, separate with
--: pyr run -- --help.
Project layout
After pyr init myapp:
myapp/
pyproject.toml # [project] metadata; you edit dependencies here
requirements.txt # generated lock; pyr owns this file
.gitignore # python defaults
app/
__init__.py
config.py # ENV from os.getenv
main.py # the entrypoint pyr run executes
.venv/ # the project's python environmentHow upgrades propagate
pyr upgrade --python swaps out ~/.pyr/python with the latest CPython
release. The next time you run pyr run (or any command that touches the venv)
in any project, pyr notices the version stamp in .venv/.pyr-python no longer
matches and silently rebuilds the venv from the new managed python, reinstalling
everything from requirements.txt.
pyr upgrade (no flags) updates the pyr binary itself. The downloaded zip is
extracted to a temp dir, size-checked, exec'd to confirm it reports the expected
version, then renamed over the running binary.
Requirements
- macOS / Linux: any reasonably recent x86_64 or aarch64 system.
tarandunzipmust be on PATH (both are basically always present). - Windows 10 1803+: needed for the bundled
tar.exeinSystem32(used to extract release zips during self-upgrade).