A zero-install, browser-native Python IDE — edit, execute, and visualise data science workloads entirely client-side via WebAssembly, with server-side proxies for market data and compiled languages.
██████╗ ██╗ ██╗███╗ ██╗ ██████╗ ██╗
██╔══██╗██║ ██║████╗ ██║██╔═══██╗███║
██████╔╝██║ ██║██╔██╗ ██║██║ ██║╚██║
██╔══██╗██║ ██║██║╚██╗██║██║ ██║ ██║
██║ ██║╚██████╔╝██║ ╚████║╚██████╔╝ ██║
╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═════╝ ╚═╝
- Overview
- Architecture
- Feature Matrix
- Tech Stack
- Project Structure
- Data Flow
- Runtime Execution Model
- Proxy API Reference
- Package Support
- Service Worker & Caching
- Theme System
- Keyboard Shortcuts
- Local Development
- Vercel Deployment
- Environment Variables
- Security Considerations
- Performance Tuning
- Extending Run01
- Troubleshooting
- Roadmap
- Contributing
Run01 is a premium browser-based Python sandbox that merges a production-grade code editor with a full scientific Python stack — all executing inside your browser via WebAssembly. There is no backend compute, no container spin-up, and no per-execution billing. The Python runtime boots once per session and stays warm.
The server layer (Flask / Vercel) is intentionally thin: it serves static assets, proxies financial data from Yahoo Finance (bypassing browser CORS), and routes compiled-language jobs (C++, C#, Rust) to the Piston execution engine.
| Principle | Implementation |
|---|---|
| Zero cold-start | Pyodide + packages cached via Service Worker on first visit |
| Privacy-first | Code never leaves the browser; only yf_download() and Piston routes touch the network |
| Streaming output | stdout lines render immediately as they arrive — no waiting for the run to complete |
| Visual-first data science | Matplotlib PNGs and interactive Plotly charts render inline in the console |
| Monochrome glassmorphic | Single coherent design language across light and dark modes |
┌──────────────────────────────────────────────────────────────────────┐
│ BROWSER (CLIENT) │
│ │
│ ┌─────────────────────┐ ┌──────────────────────────────────────┐ │
│ │ Monaco Editor │ │ Output Console │ │
│ │ (AMD, CDN-loaded) │ │ ┌─────────┐ ┌────────┐ ┌────────┐ │ │
│ │ │ │ │ stdout │ │ PNG │ │Plotly │ │ │
│ │ Python / C++ / C# │ │ │ stream │ │ chart │ │ chart │ │ │
│ │ / Rust source │ │ └─────────┘ └────────┘ └────────┘ │ │
│ └──────────┬──────────┘ └──────────────────────────────────────┘ │
│ │ getValue() ▲ │
│ ▼ │ │
│ ┌──────────────────────────────────────────┐ │ render │
│ │ Pyodide (WebAssembly) │────┘ │
│ │ │ │
│ │ runPythonAsync(code) │ │
│ │ ├─ stdout → batched callback │ │
│ │ │ ├─ __RUN01_IMG__:<b64> → PNG │ │
│ │ │ └─ __RUN01_PLOTLY__:<b64> → JSON │ │
│ │ └─ stderr → batched callback │ │
│ │ │ │
│ │ Packages (WASM): │ │
│ │ numpy · pandas · scipy · scikit-learn │ │
│ │ matplotlib · statsmodels · seaborn │ │
│ │ plotly │ │
│ └────────────┬─────────────────────────────┘ │
│ │ pyfetch("/api/yf/...") │
│ │ (yfinance CORS bypass) │
└───────────────┼──────────────────────────────────────────────────────┘
│ HTTP
┌───────────────┼──────────────────────────────────────────────────────┐
│ ▼ FLASK / VERCEL │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ GET /api/yf/<ticker>?period=&interval= → yfinance (PyPI) │ │
│ │ POST /api/run {language, code} → Piston API │ │
│ │ GET /sw.js → Service Worker │ │
│ │ GET / → index.html │ │
│ └─────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
│
▼ external
┌───────────────────────┐ ┌──────────────────────────┐
│ Yahoo Finance API │ │ Piston API (emkc.org) │
│ finance.yahoo.com │ │ C++ · C# · Rust · more │
└───────────────────────┘ └──────────────────────────┘
| Feature | Status | Notes |
|---|---|---|
| Python (Pyodide WASM) | ✅ | No server round-trip for execution |
| NumPy / Pandas / SciPy | ✅ | Pre-loaded at startup |
| Scikit-Learn | ✅ | Pre-loaded |
| Statsmodels | ✅ | Pre-loaded |
| Matplotlib inline PNG | ✅ | plt.show() patched to emit base64 |
| Seaborn | ✅ | Installed via micropip |
| Plotly interactive charts | ✅ | fig.show() patched to emit JSON → rendered with Plotly.js |
| Yahoo Finance (live data) | ✅ | await yf_download(ticker) via server proxy |
| Streaming stdout | ✅ | Each print() renders immediately |
| C++ / C# / Rust | ✅ | Via /api/run → Piston |
| Dark / Light theme | ✅ | Persisted in localStorage |
| Service Worker caching | ✅ | Pyodide WASM, Monaco, Plotly cached after first load |
| Draggable split pane | ✅ | Mouse and keyboard (Arrow keys) |
Download output .txt |
✅ | Ctrl+D |
| Reset to starter code | ✅ | Ctrl+R |
| Cursor position in status bar | ✅ | ln N, col N |
stdin support |
🔜 | Roadmap Q3 |
| Multi-file editor | 🔜 | Roadmap Q4 |
| Share snippet via URL | 🔜 | Roadmap Q4 |
Layer Library / Service Version Where it runs
──────────────── ─────────────────────────── ───────────── ──────────────
Python runtime Pyodide 0.26.4 Browser (WASM)
Code editor Monaco Editor 0.52.0 Browser (CDN)
Charts (static) Matplotlib + Seaborn latest Browser (WASM)
Charts (interac) Plotly.js 2.35.2 Browser (CDN)
ML Scikit-Learn latest Browser (WASM)
Stats Statsmodels latest Browser (WASM)
Market data yfinance ≥1.4.1 Server (Flask)
Compiled langs Piston API (emkc.org) v2 External API
Web server Flask 3.0.3 Server
Deployment Vercel (@vercel/python) latest Edge / Lambda
Fonts Outfit, JetBrains Mono, variable Google Fonts CDN
Pixelify Sans
SW caching Service Worker (Cache API) — Browser
run01/
├── api/
│ └── index.py # Vercel WSGI entrypoint — imports pyrunner.app
│
├── pyrunner/
│ ├── __init__.py # Package marker
│ ├── app.py # Flask app: routes, yf proxy, Piston proxy
│ ├── requirements.txt # flask, yfinance
│ │
│ ├── static/
│ │ ├── app.js # Monaco + Pyodide bootstrap, run loop, output renderer
│ │ ├── style.css # Monochrome glassmorphic design system
│ │ └── sw.js # Service Worker: cache-first CDN strategy
│ │
│ └── templates/
│ └── index.html # Shell HTML: nav, split panes, init overlay
│
├── vercel.json # Build + routing config for Vercel
├── requirements.txt # Root-level (mirrors pyrunner/requirements.txt)
├── .gitignore
└── README.md ← you are here
Three distinct responsibilities on a single Flask app object:
- Static serving —
GET /rendersindex.html;GET /sw.jsserves the Service Worker with correctService-Worker-Allowed: /header (required for full-origin scope) - Yahoo Finance proxy —
GET /api/yf/<ticker>callsyfinance.Ticker.history()server-side and returns clean OHLCV JSON. Strips timezone info sostrftimeworks across yfinance versions. - Piston proxy —
POST /api/runforwards{language, code}tohttps://emkc.org/api/v2/piston/executeand relays the result. Timeout: 30 s.
The heaviest file. Key sections:
PYODIDE_SETUP— Python bootstrap string injected into the WASM runtime at startup. Patchesplt.show()andfig.show()to emit__RUN01_IMG__:and__RUN01_PLOTLY__:sentinel lines on stdout rather than attempting to open a GUI window.monacoReady— Promise that resolves when the AMD loader has finished and the editor is mounted. 30 s timeout (extended from the original 5 s to handle CDN cold-starts).pyodideReady— CallsloadPyodide(), then a singlepyodide.loadPackage([...])to batch-download all stdlib packages in parallel, thenmicropip.install(['seaborn', 'plotly']).processOutput()— Per-line stdout router: detects sentinels and delegates torenderImage()orrenderPlotly(); everything else goes toappendLine().getPlotly()— Pollswindow.Plotlywith exponential back-off, falling back to a dynamic<script>inject if the synchronous CDN load raced ahead.
Cache-first Service Worker with a versioned cache (run01-v2). Only caches GET responses from four CDN origins: cdn.jsdelivr.net, cdn.plot.ly, fonts.googleapis.com, fonts.gstatic.com. All other requests (including the Flask API routes) pass through uncached.
User clicks ▶ Run
│
▼
monacoEditor.getValue() → raw source string
│
▼
pyodide.setStdout({ batched: processOutput })
pyodide.setStderr({ batched: processOutput })
│
▼
pyodide.runPythonAsync(code) ← awaited; non-blocking
│
├─ print("hello")
│ └─→ processOutput("hello", false, block)
│ └─→ appendLine(...)
│
├─ plt.show()
│ └─→ _mpl_capture() in WASM
│ └─→ savefig → base64 PNG
│ └─→ print("__RUN01_IMG__:<b64>")
│ └─→ processOutput detects prefix
│ └─→ renderImage(b64, block)
│
├─ fig.show()
│ └─→ _plotly_capture() in WASM
│ └─→ pio.to_json(fig) → base64 UTF-8
│ └─→ print("__RUN01_PLOTLY__:<b64>")
│ └─→ processOutput detects prefix
│ └─→ renderPlotly(b64, block)
│
└─ return / raise
└─→ finishOutputBlock(block, success)
Python code: df = await yf_download("AAPL", period="3mo")
│
▼ (inside Pyodide)
pyodide.http.pyfetch("/api/yf/AAPL?period=3mo")
│
▼ HTTP GET (same origin — no CORS issue)
Flask /api/yf/AAPL
│
▼
yfinance.Ticker("AAPL").history(period="3mo", interval="1d")
│
▼
pd.DataFrame → JSON records → jsonify(records)
│
▼ HTTP 200 JSON
resp.json() inside pyfetch
│
▼
pd.DataFrame(data).set_index("Date") ← returned to caller
Monaco and Pyodide initialise in parallel via Promise.all([monacoReady, pyodideReady]). Neither blocks the other. The UI is unlocked only after both resolve.
t=0ms ──► loadPyodide() ─────────────────────────────────► ✓ (~4-8s cold)
t=0ms ──► require(['vs/editor.main']) ──────────────► ✓ (~1-2s cold)
│
Promise.all resolves
│
hideOverlay() + btnRun.enable()
# Single call — Pyodide resolves the full dependency graph internally
# and downloads all wheels in parallel. Much faster than sequential awaits.
await pyodide.loadPackage([
'numpy', 'pandas', 'scipy', 'scikit-learn',
'matplotlib', 'statsmodels', 'micropip',
])
# Pure-Python packages not in Pyodide's package index
micropip = pyodide.pyimport('micropip')
await micropip.install(['seaborn', 'plotly'], keep_going=True)keep_going=True means a single package failure (e.g. a transient CDN error) doesn't abort the entire install.
# Injected at startup — replaces Matplotlib's GUI backend
def _mpl_capture(*args, **kwargs):
buf = io.BytesIO()
plt.savefig(buf, format='png', dpi=150, bbox_inches='tight',
facecolor='#0a0a0a', edgecolor='none')
b64 = base64.b64encode(buf.getvalue()).decode('ascii')
buf.close()
plt.close('all')
print(f'__RUN01_IMG__:{b64}', flush=True) # ← sentinel on stdout
plt.show = _mpl_captureimport plotly.io as _pio
def _plotly_capture(fig, *args, **kwargs):
fig_json = _pio.to_json(fig) # full Plotly JSON spec
encoded = base64.b64encode(fig_json.encode()).decode()
print(f'__RUN01_PLOTLY__:{encoded}', flush=True) # ← sentinel on stdout
_pio.show = _plotly_capture
go.Figure.show = lambda self, *a, **kw: _plotly_capture(self, *a, **kw)On the JS side, renderPlotly() decodes the base64, parses the JSON, and calls Plotly.newPlot() with layout overrides to match the current theme.
Fetches OHLCV price history for a given ticker from Yahoo Finance.
Path parameters
| Parameter | Type | Description |
|---|---|---|
ticker |
string | Case-insensitive. Uppercased internally. E.g. AAPL, TSLA, BTC-USD |
Query parameters
| Parameter | Default | Valid values |
|---|---|---|
period |
1mo |
1d 5d 1mo 3mo 6mo 1y 2y 5y 10y ytd max |
interval |
1d |
1m 2m 5m 15m 30m 60m 90m 1h 1d 5d 1wk 1mo 3mo |
Response (200)
[
{
"Date": "2024-01-02",
"Open": 185.25,
"High": 186.10,
"Low": 183.92,
"Close": 185.64,
"Volume": 71628400
}
]Response (404) — symbol not found or delisted
{ "error": "No price data found for 'XYZ'. Symbol may be delisted or invalid." }Response (500) — upstream failure
{ "error": "<exception message>" }Usage in Python (inside Run01)
# Async — must be awaited at top level or inside an async function
df = await yf_download("AAPL", period="6mo", interval="1d")
# df is a pd.DataFrame with DatetimeIndex and columns:
# Open High Low Close Volume
print(df.tail())Compiles and executes code in a compiled language via the Piston API.
Request body (JSON)
{
"language": "cpp",
"code": "#include <iostream>\nint main() { std::cout << \"hello\"; }"
}| Field | Type | Valid values |
|---|---|---|
language |
string | cpp · csharp · rust |
Response — passes through the Piston response shape:
{
"run": {
"stdout": "hello",
"stderr": "",
"code": 0,
"signal": null,
"output": "hello"
},
"language": "c++",
"version": "10.2.0"
}| Package | Use case |
|---|---|
numpy |
Numerical arrays, linear algebra, FFT |
pandas |
DataFrames, time-series, CSV/JSON I/O |
scipy |
Stats, signal processing, optimization |
scikit-learn |
ML: regression, classification, clustering, preprocessing |
statsmodels |
OLS, GLM, ARIMA, VAR, hypothesis tests |
matplotlib |
Static charts (rendered as inline PNG) |
micropip |
Pure-Python package installer |
| Package | Use case |
|---|---|
seaborn |
Statistical visualisation built on Matplotlib |
plotly |
Interactive charts (rendered inline via Plotly.js) |
Any pure-Python package on PyPI that has no compiled C/Fortran extensions. Examples:
import micropip
await micropip.install('sympy') # symbolic mathematics
await micropip.install('networkx') # graph algorithms
await micropip.install('faker') # test data generationPackages with native extensions that haven't been compiled for Emscripten/WASM:
yfinance— useawait yf_download()insteadtensorflow,torch— too large / not WASM-compiledpsycopg2,pymysql— no socket access in browser
The Service Worker (/sw.js) implements a cache-first strategy for CDN assets. This means repeat visits load in under 500 ms regardless of network speed — Pyodide's ~12 MB WASM bundle is served from the browser cache.
| Origin | What's cached |
|---|---|
cdn.jsdelivr.net |
Pyodide WASM + wheels, Monaco editor |
cdn.plot.ly |
Plotly.js dist |
fonts.googleapis.com |
Font CSS |
fonts.gstatic.com |
Font files (woff2) |
All Flask routes (/, /api/*, /sw.js) bypass the cache intentionally.
The cache is keyed by CACHE_VERSION = 'run01-v2'. On SW activation, all caches with a different key are deleted. To bust the cache on a new release, increment this constant in sw.js.
const CACHE_VERSION = 'run01-v3'; // ← bump on CDN library upgradesThe SW is registered at the end of index.html:
navigator.serviceWorker.register('/sw.js')The /sw.js route sets Service-Worker-Allowed: / so the worker can control the full origin (not just /static/*).
Run01 ships with two complete themes driven by a single data-theme attribute on <html>.
| Variable | Dark | Light | Role |
|---|---|---|---|
--bg |
#050505 |
#F2F2F7 |
Page background |
--glass-bg |
rgba(255,255,255,0.02) |
rgba(255,255,255,0.75) |
Panel fill |
--glass-border |
rgba(255,255,255,0.07) |
rgba(0,0,0,0.08) |
Panel stroke |
--text |
#F5F5F7 |
#1D1D1F |
Primary text |
--text-muted |
#8E8E93 |
#6E6E73 |
Secondary text |
--text-dim |
#48484A |
#A1A1A6 |
Tertiary / disabled |
--white |
#FFFFFF |
#000000 |
Accent / CTA fill |
--black |
#000000 |
#FFFFFF |
Accent / CTA text |
| App theme | Monaco theme |
|---|---|
dark |
run01-dark (base: vs-dark, transparent background) |
light |
run01-light (base: vs, transparent background) |
Both themes use editor.background: #00000000 so the editor's glass panel shows through.
localStorage.setItem('run01-theme', 'dark' | 'light')The saved theme is applied before first paint to avoid flash-of-wrong-theme (FOWT).
| Shortcut | Action |
|---|---|
Ctrl/⌘ + Enter |
Run code |
Ctrl/⌘ + D |
Download output as .txt |
Ctrl/⌘ + R |
Reset editor to starter code |
Ctrl/⌘ + Z |
Undo (Monaco native) |
Ctrl/⌘ + Shift + Z |
Redo (Monaco native) |
Ctrl/⌘ + / |
Toggle line comment (Monaco native) |
Alt + ←/→ |
Navigate word (Monaco native) |
Tab on resize handle |
Focus the resize handle |
←/→ on resize handle |
Move divider 20 px |
Shift + ←/→ on handle |
Move divider 50 px |
| Tool | Minimum version |
|---|---|
| Python | 3.10 |
| pip | 23.x |
| A modern browser | Chrome 115+, Firefox 116+, Safari 16.4+ |
# 1. Clone the repository
git clone https://github.com/your-org/run01.git
cd run01
# 2. Create and activate a virtual environment
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
# 3. Install dependencies
pip install -r requirements.txt
# 4. Run the development server
python pyrunner/app.pyOpen http://localhost:5000. Flask runs in debug mode (debug=True) so the server reloads on file changes.
On first load, the browser fetches ~15 MB of CDN assets (Pyodide WASM, Monaco, Plotly). This takes 5–20 s depending on connection speed. All assets are cached by the Service Worker; subsequent loads take under 500 ms.
vercel.json routes all traffic to api/index.py, which imports the Flask app object from pyrunner.app. Vercel's @vercel/python builder wraps it as a serverless WSGI function.
{
"builds": [{ "src": "api/index.py", "use": "@vercel/python" }],
"routes": [{ "src": "/(.*)", "dest": "api/index.py" }]
}# api/index.py — Vercel detects the `app` name automatically
from pyrunner.app import app# Install Vercel CLI (one-time)
npm i -g vercel
# Link and deploy
vercel --prodOr push to GitHub and connect the repository in the Vercel dashboard — Vercel will auto-deploy on every push to main.
| Constraint | Value | Impact |
|---|---|---|
| Function timeout | 60 s (Pro) / 10 s (Hobby) | Piston proxy: fine; yf proxy: fine for most tickers |
| Function memory | 1024 MB | yfinance + pandas fit comfortably |
| Payload size | 4.5 MB request / 4.5 MB response | Chart JSON from Piston is well under limit |
| Cold-start | ~300 ms | Flask import is fast; no heavy ML imports server-side |
Run01 has no required environment variables for its core functionality. The following are optional:
| Variable | Default | Purpose |
|---|---|---|
FLASK_ENV |
production |
Set to development locally for debug mode |
FLASK_DEBUG |
0 |
Set to 1 to enable auto-reloader |
PISTON_URL |
https://emkc.org/api/v2/piston/execute |
Override Piston endpoint (e.g. self-hosted) |
Set these in a .env file locally (never commit) or as Vercel environment variables in the dashboard.
| Code | Where it runs | Isolation |
|---|---|---|
| Python (user code) | Browser WASM sandbox | Full browser sandbox; no file system, no raw sockets |
| C++ / C# / Rust | Piston API (emkc.org) | Piston uses isolated containers with CPU/memory limits |
| Flask (yf proxy) | Vercel serverless | Receives only a ticker string; no user code touches the server |
The yf proxy exists specifically because finance.yahoo.com blocks cross-origin requests from browsers. By routing through the same-origin Flask endpoint, Pyodide's pyfetch works without any CORS headers.
The yf proxy passes ticker through yfinance.Ticker(ticker.upper()) — yfinance validates the symbol upstream. Malformed tickers return a 404 with an error message; they never reach the shell.
The Piston proxy only forwards language (validated against an allowlist) and code (opaque string forwarded as-is). Piston handles sandboxing.
The SW only caches CDN GET responses. It never intercepts Flask API calls (/api/*) or the SW file itself (/sw.js), preventing stale proxy responses.
- Cache hit — After first load, the SW serves Pyodide from the browser cache. No action needed.
- Preload hints —
index.htmlhas<link rel="preload">for Pyodide, Monaco loader, and Plotly. The browser starts fetching them before the parser hits the<script>tags. - Parallel package load — The single
pyodide.loadPackage([...])call downloads all wheels in parallel. Avoid splitting this into multiple sequential calls. micropip.installlast — Pure-Python packages (seaborn, plotly) via micropip run after the heavier WASM packages, so they don't block the critical path.
- Matplotlib: use
dpi=100instead of150for faster PNG encoding at the cost of resolution. - Plotly: use
plotly.graph_objects(JSON-serialisable) rather thanplotly.expressfigures backed by large DataFrames —pio.to_jsonis faster on simpler traces.
Browsers cap the Cache Storage quota (typically 20–80% of available disk). If the quota is exceeded, the oldest caches are evicted. Pyodide's 12 MB WASM file is the largest single asset; on devices with very limited storage, the SW may fail to cache it. The SW handles this gracefully — failed cache writes are non-blocking.
- Look up the language slug at Piston runtimes.
- Add an entry to
PISTON_LANGSinapp.py:PISTON_LANGS = { "cpp": "c++", "csharp": "csharp", "rust": "rust", "go": "go", # ← new }
- Add a
<button class="lang-tab" data-lang="go">inindex.html. - Add a
goentry toSTARTER_CODESandLANG_METAinapp.js. - Wire the tab click handler in
app.js.
If it's in Pyodide's package index (compiled wheel available):
// In initPyodide() in app.js:
await pyodide.loadPackage(['numpy', 'pandas', ..., 'sympy']); // add hereIf it's pure Python (no C extensions):
// In initPyodide() in app.js:
await micropip.install(['seaborn', 'plotly', 'your-package']);If neither works — the package requires C extensions not in Pyodide's index. Consider a server-side proxy pattern (like the yf proxy) or a separate Piston route.
All design tokens live in :root and [data-theme="light"] in style.css. The entire system is driven by 7 CSS custom properties; changing them updates every component simultaneously.
:root {
--bg: #050505; /* page background */
--glass-bg: rgba(255,255,255,0.02);
--glass-border: rgba(255,255,255,0.07);
--text: #F5F5F7;
--text-muted: #8E8E93;
--white: #FFFFFF; /* primary accent */
--black: #000000; /* inverse accent */
}Symptoms: Overlay stays visible; status shows "Python init failed".
Causes & fixes:
- CDN blocked by a corporate firewall or ad-blocker. Whitelist
cdn.jsdelivr.net. - Browser too old. Pyodide requires
SharedArrayBuffer. CheckcrossOriginIsolatedin DevTools console. Iffalse, the server is missingCross-Origin-Opener-Policy/Cross-Origin-Embedder-Policyheaders (not required on Vercel / localhost, but required on some custom servers). - Low memory device. Pyodide + packages consume ~300 MB of heap. On devices with < 2 GB RAM, the WASM instantiation may fail.
Symptoms: ValueError: No price data found for 'XYZ'.
Causes: Invalid ticker, weekend/holiday (no 1d data), or intraday interval requested for a period > 60 days (Yahoo Finance limitation).
# Fine — 3 months of daily data
df = await yf_download("AAPL", period="3mo", interval="1d")
# Error — intraday data is only available for the last 60 days
df = await yf_download("AAPL", period="1y", interval="1h") # ← will failYou imported yfinance directly in your code. yfinance cannot run inside the browser WASM sandbox. Use the proxy helper instead:
# ✗ Wrong
import yfinance as yf
df = yf.Ticker("AAPL").history(period="3mo")
# ✓ Correct
df = await yf_download("AAPL", period="3mo")Symptom: Editor area is blank; no syntax highlighting.
Fix: The AMD loader (loader.js) must be available before app.js runs. The script tags are ordered: Monaco loader (sync) → Pyodide (defer) → app.js (defer). If the CDN is slow, the 30 s timeout in monacoReady will resolve gracefully without the editor, so Run01 still works (you can paste code into a textarea fallback).
Cause: PYODIDE_SETUP was not executed at startup (e.g. a previous error aborted initialisation).
Fix: Open DevTools → Console and look for errors from initPyodide(). Re-run the page.
Cause: The Piston API at emkc.org is a free community service with occasional downtime.
Fix: Self-host Piston: https://github.com/engineer-man/piston and set PISTON_URL to your instance.
-
stdinsupport via a modal prompt interceptor - Keyboard-accessible theme toggle
- Multi-file editor (virtual filesystem backed by
pyodide.FS) - Snippet sharing via URL (code encoded in fragment hash, no server storage)
-
pip installUI: type a package name, micropip installs it into the running session
- Collaborative editing (WebRTC or Liveblocks)
- Jupyter notebook import (.ipynb → Run01 cells)
- Export to Colab / Kaggle
Contributions are welcome. Please open an issue first for non-trivial changes.
| Branch | Purpose |
|---|---|
main |
Production; auto-deploys to Vercel |
dev |
Integration branch for feature PRs |
feat/<name> |
New features |
fix/<name> |
Bug fixes |
feat: add stdin modal interceptor
fix: extend Monaco AMD timeout from 5s to 30s
docs: add Piston proxy API reference
perf: batch all pyodide.loadPackage() calls into single await
# Python
pip install ruff
ruff check pyrunner/
# JavaScript (optional — no bundler, so ESLint runs standalone)
npx eslint pyrunner/static/app.jsMIT © Run01 Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.