Pythonator is a fast desktop supervisor for long-running processes: web servers, Discord/Telegram bots, workers, scrapers, schedulers. It runs Python, Node.js, Bun, Deno, Go, Java, .NET, Ruby, PHP, shell and PowerShell scripts, or any program, on Windows, macOS or Linux directly, with no Docker containers and no WSL. Each service gets its own environment, a live colour console, crash recovery and a built-in code editor in the same window.
Supervision
-
Run any number of services at once, mixing runtimes. Each one runs a script, its runtime's named entry point, or any command:
Runtime Script Entry point Install dependencies Python python -u bot.pypython -m uvicorn …pip installinto the venvNode.js node bot.js(.mjs,.ts)npm run start(pnpm / yarn / bun by lockfile)npm install/addBun bun run server.tsbun run devbun install/bun addDeno deno run -A main.tsdeno task startdeno install/deno addGo go run main.gogo run ./cmd/botgo mod tidy/go getJava java -jar server.jar(or one.javafile)java -cp . com.example.Bot(Maven / Gradle) .NET dotnet run --project Bot.csproj,Bot.dll,app.csdotnet restore/add packageRuby ruby bot.rb(unbuffered)bundle exec pumabundle install/gem installPHP php worker.phpphp -S localhost:8000composer install/requireShell bash run.sh,.bat/.cmdon WindowsPowerShell pwsh -File task.ps1Program any executable: caddy,redis-server,.exe, AppImage, macOS.appRuntimes are found on PATH and in their usual install folders (
~/.bun/bin,/usr/local/go/bin,JAVA_HOME, Git Bash on Windows, Homebrew andjava_homeon macOS, ...), or you set the interpreter per service. Runtime options go to the interpreter itself, e.g.-Xmx4Gfor Java or--max-old-space-size=4096for Node. Adding a language is one entry incore/runtimes.py. -
Restart policies (never / on failure / always) with exponential back-off (1s → 60s) for crash loops
-
Clean shutdown of the whole process tree. Every service runs in its own process group: Stop sends SIGTERM (CTRL+BREAK on Windows), waits a configurable grace period, then kills everything it spawned. Workers left behind after a crash are cleaned up too, so ports are free for the next start.
-
Schedules for jobs that run and finish:
every 15m, times of day (09:00, 18:30) or cron (0 3 * * mon-fri,@daily). A run is skipped if the previous one is still going. -
Autostart services when Pythonator opens, and keep them running from the system tray (the menu bar on macOS) when the window is closed
-
Start Pythonator at login (Settings), minimized to the tray, so your bots and servers come back after a reboot: a LaunchAgent on macOS, an XDG autostart entry on Linux, the Run key on Windows
-
Live CPU / RAM / uptime / pid per service tree, sampled on a background thread
Environment
- Python: per-service virtual environment, auto-detected (
.venv/venv) in the working directory; create it andpip installwith one click - Node.js:
node_modules/.bin(of the project and its parents) is on PATH; Install dependencies runsnpm install(or pnpm / yarn / bun), or adds the packages you list; pick a specificnodebinary per service - 📦 Dependencies button for every language: Create a starter
requirements.txt,package.json,deno.json,go.mod,Gemfileorcomposer.jsonif the project has none, Edit it in the built-in editor, then Save and install dependencies (unsaved edits are saved first) - Version fixes: when an install fails because a pinned version doesn't exist for your Python (say
numpy==1.26.0on 3.13), the console names the closest available version and asks y/n before changing the file and installing again. If a pinned package fails to build, it looks up versions with ready-made wheels first. For npm / pnpm / yarn / bun it offerspackage@latest. - Per-service environment variables (
KEY=value,$VARexpansion), working directory and interpreter .envfiles: point a service at.env(picked automatically when one sits next to the script) to keep tokens out of the config; variables set on the service override it$ shellconsole mode: runpip list,alembic upgrade head,npx prisma migrate deploy, ... inside the service's environment, with output streamed into the console. Stop ends a long-running command too (python -m http.server,npm run dev), and pressing it again kills one that won't quit.- On Windows commands run in PowerShell by default, so
mv,ls,rm,cpandcatfrom README instructions work. Pick cmd or Git Bash in Settings → Console $ shell (bash or PowerShell elsewhere). - A pasted block of several lines runs as one script and stops at the first line that fails.
- When a command doesn't exist, the console says why: Docker not installed,
apt/sudobeing Linux commands on Windows, or a tool installed after Pythonator started (restart it to pick up the new PATH).
- On Windows commands run in PowerShell by default, so
- stdin mode: type into a running process (
input()prompts, REPL-style bot commands)
Console & logs
- Fast ANSI console: 16/256/true colour, bold, italic, underline. Progress bars using
\rcollapse correctly. - Output is read and split into lines on background threads; the GUI only renders what's new, about 12 times a second, so a process printing 100k lines/s can't freeze the window
- Find in console, follow mode, timestamps, wrap
- Every line is persisted to rotating log files (size and count configurable). History search covers all of them off the GUI thread.
- Double-click a stack-trace line (Python, Node, Go, Java, .NET, Ruby, PHP, shell) to open the file at that line in the editor
- Clickable links: URLs in the output open in your browser, and when a web server prints its address
(
Running on http://127.0.0.1:5000,Local: http://localhost:5173/) an Open button appears in the header
Files & editor
- Folder tree of the selected service's project: open files, create files and folders, rename (F2), move to
trash, copy paths, run a script as a new service. Cache folders like
__pycache__and.gitare hidden. - Tabs, highlighting for Python, JavaScript/TypeScript, Go, Java, C#, Ruby, PHP, Rust, shell, batch and PowerShell, line numbers, smart indent,
Ctrl+/comment toggle, find & replace, go to line - Python completion via jedi (optional), using the service's own venv
- Atomic saves, reloads files changed on disk, and saves your edits automatically on Start/Restart (F5)
Reliability
- A bug in Pythonator can't take your services down with it: unexpected errors are written to
logs/pythonator.logand shown in a dialog, and the app keeps running - A service that can't start (missing interpreter, deleted folder, bad permissions) says why in its console instead of failing silently; supervision loops and stats sampling recover from any error
- The config is written atomically with a
.bakcopy. A damaged config is restored from the backup, and one that can't be read is never overwritten. - Very long output lines are split instead of eating memory; a full disk stops log files, never the service
- The editor refuses binary, non-UTF-8 and huge files rather than corrupting them
Requires Python 3.11+ on Windows, macOS (11 or later, Apple silicon or Intel) or Linux. For other languages, install their runtime as usual (Node.js, Go, a JDK, the .NET SDK, Ruby, PHP, ...); Pythonator finds it on PATH or in its standard install folder.
git clone https://github.com/cfunkz/Pythonator.git
cd Pythonator
pip install -e . # or: pip install -r requirements.txt
pythonator # or: python -m pythonator / python app.pySmart Python completion uses jedi. pip install -r requirements.txt
includes it; with pip install -e . add it with pip install -e ".[completion]" (or pip install jedi), in the
Python that runs Pythonator, then restart. Your services' venvs don't need it: jedi reads the packages installed
in each service's own venv. Suggestions pop up after . or three letters, or press Ctrl+Space. Without jedi the
editor completes words from the current file, and says so once when you open a Python file.
- New service (or drop a
.py,.js,.go,.rb,.php,.sh, ... file onto the window), pick the runtime and a script, entry point or command - Use the 📦 Dependencies button in the header (also in the ⋯ menu). For Python, pick Create venv, then
Install dependencies (your list, or
requirements.txt). Other runtimes only need Install dependencies (your list, orpackage.json,go.mod,Gemfile, ...). Edit package.json (or whichever file the project uses) opens it in the editor, and Create ... makes a starter file if there is none. Add packages, then Save and install dependencies. - Start (F5). Watch the console, edit the code above it, and press F5 to save and restart.
- Set Restart: on failure and Start automatically for anything that should always be up, or a Schedule (Configure → Supervision) for jobs like backups and reports
- Close the window and choose Keep running in the system tray (menu bar on macOS). To have everything come back after a reboot, turn on Settings → Start Pythonator when I log in.
| Shortcut | Action |
|---|---|
| Windows / Linux | macOS |
| --- | --- |
F5 / Shift+F5 |
⌘R / ⌘. (or F5 / ⇧F5) |
Ctrl+Shift+N / Ctrl+E |
⇧⌘N / ⌘E |
Ctrl+S, Ctrl+Shift+S, Ctrl+O, Ctrl+W |
⌘S, ⇧⌘S, ⌘O, ⌘W |
Ctrl+F, Ctrl+H, Ctrl+G |
⌘F, ⌥⌘F, ⌘L |
Ctrl+Space, Ctrl+/, Ctrl+D |
⌃Space, ⌘/, ⌘D |
Ctrl+1 / Ctrl+2 / Ctrl+3 |
⌘1 / ⌘2 / ⌘3 |
⌘, |
|
Ctrl+Q |
⌘Q |
↑ / ↓ in the console input |
same |
Tooltips show each shortcut the way your system writes it.
| Location | |
|---|---|
| Windows | %APPDATA%\Pythonator\ |
| macOS | ~/Library/Application Support/Pythonator/ |
| Linux | ~/.config/pythonator/ |
| Portable | next to the app, if a pythonator.json (or a 1.x bots.json) is already there |
| Override | PYTHONATOR_HOME=/some/dir or pythonator --home /some/dir |
It contains pythonator.json (config, written atomically), logs/ (one rotating log per service, plus pythonator.log for the app itself) and
requirements/. Upgrading from 1.x: your bots.json is migrated automatically on first start and left
untouched as a backup.
On Linux and macOS the config, its backup and the logs/ folder are readable by your account only, because
service environment variables and output often contain tokens. Prefer a .env file in the project (kept out
of git) over typing secrets into the service settings.
Pythonator runs your services with your own account's rights: it is a supervisor, not a sandbox, so only run code you trust. On top of that it:
- Asks before risky things: starting as root / Administrator, the first “Install dependencies” of a project
(package installs run their own setup code),
$ shellcommands that look destructive (rm -rf /,format c:,curl … | sh,shutdown, …), deleting files from the folder tree (to the trash first; permanent delete is asked separately), and autostarting services when the settings folder could have been changed by another user. - Never runs anything through a shell by accident: services and installs are started from an argument list.
On Windows,
.bat/.cmdfiles (includingnpm.cmd) get every argument quoted for cmd.exe, so an argument likea&calcstays an argument. - Keeps secrets out of logs: what you type at a
Password:/Token:prompt is logged as••••••••, and env vars are never printed. - Opens only web links (http/https) from console output, and file-tree names can't reach outside the folder.
- Cleans up: stopping a service stops its whole process tree, and quitting Pythonator stops everything it
started (unless you keep it running in the tray). On macOS that includes
⌘Q, Dock → Quit and logging out.
pythonator/
├── core/ Qt-free and unit-tested
│ ├── models.py typed dataclasses for services and settings (tolerant JSON parsing)
│ ├── store.py atomic config persistence + 1.x migration
│ ├── launch.py command / venv / node / package-manager / environment resolution
│ ├── runtimes.py registry of languages: how each one runs a script, an entry point, a command, installs
│ ├── shells.py the console $ shell (PowerShell / cmd / bash) and "command not found" hints
│ ├── starters.py starter package.json / go.mod / Gemfile / ... for projects without one
│ ├── fixes.py "that version doesn't exist" install failures and the closest version that does
│ ├── locations.py file:line in stack traces of every supported language
│ ├── errors.py LaunchError, the one error a service start can fail with
│ ├── runner.py one child process: reader, stdin writer and waiter threads, tree kill
│ ├── logs.py line assembly, ring buffer, rotating log files, history search
│ ├── ansi.py ANSI SGR parser
│ ├── policy.py restart back-off decisions
│ ├── schedule.py "every 15m", daily times and cron expressions
│ ├── urls.py spotting a web server's local address in its output
│ ├── userenv.py a terminal's PATH for apps opened from the macOS Finder / Dock; open-files limit
│ ├── login.py start at login: LaunchAgent (macOS), XDG autostart (Linux), Run key (Windows)
│ ├── macos.py App Nap and .app bundles, without PyObjC
│ └── stats.py background psutil sampler
├── supervisor.py QObject that owns services, applies policies, emits signals
└── ui/ PyQt6 widgets: main window, service list, folder tree, console, editor, dialogs, theme
├── keys.py shortcuts per platform (⌘R / ⌘. on macOS) and their labels
├── languages.py syntax highlighting for Go, Java, C#, Ruby, PHP, Rust, shell, batch, PowerShell
└── errors.py last-resort error handler: logs, shows a dialog, never lets the app die
GUI thread ─ renders new console lines every 80 ms, list/stats every 1 s, never blocks on I/O
▲ queued Qt signal (exit) ▲ drain()
per process: reader thread ─► LogStore (lines + file write) writer thread ◄─ stdin queue
waiter thread ─► ExitInfo ─► Supervisor ─► restart policy / QTimer back-off
stats thread ─► psutil, one sample per second per process tree
Platform notes. On Linux and macOS, services run in their own session, so Stop signals the whole process group. On Windows they get their own process group. If Pythonator was started from a console, Stop sends CTRL+BREAK first. In the windowed build there is no console to deliver it, so the tree is terminated directly.
On macOS:
- An app opened from the Finder, Dock or a login item only gets
/usr/bin:/bin:/usr/sbin:/sbinas its PATH. Pythonator asks your login shell ($SHELL -ilc) for the PATH you have in Terminal once at start, so Homebrew, nvm, pyenv, asdf, ... tools are found. SetPYTHONATOR_KEEP_PATH=1to skip this. - App Nap is turned off while Pythonator runs, so a window hidden in the menu bar still restarts crashed services and starts scheduled jobs on time. The Mac can still sleep as usual.
- The open-files limit is raised from macOS's default of 256, for Pythonator and the services it starts.
/usr/bin/javaand/usr/bin/python3are skipped when they are only placeholders that offer to install Java or Apple's command line tools; Homebrew's keg-only OpenJDK and Ruby are found too.
pip install pyinstaller pillow
pyinstaller --noconfirm packaging/pythonator.spec| Output | |
|---|---|
| Windows | dist\Pythonator\Pythonator.exe (copy the whole Pythonator folder) |
| macOS | dist/Pythonator.app (drag it to Applications) |
| Linux | dist/Pythonator/Pythonator |
Build on the system you're building for; a Mac build runs on the architecture of the Python that built it (use a universal2 Python from python.org for one app that runs on both Apple silicon and Intel). CI builds and starts the app on all three systems for every pull request and keeps the archives as downloadable artifacts.
The macOS app isn't signed with a Developer ID, so the first time, right-click it and choose Open (or run
xattr -dr com.apple.quarantine /Applications/Pythonator.app).
The app bundles Pythonator only. Your services still need their own runtime (Python, Node.js, ...), or an interpreter path set in their settings.
pip install -e ".[dev]"
ruff check .
mypy # strict
pytest # core, process supervision and headless UI tests (QT_QPA_PLATFORM=offscreen)
# CI runs them on Windows, macOS (Apple silicon and Intel) and LinuxPyQt6 6.12.0 ships a stub file mypy can't parse; type-check with any other PyQt6 version.




