Skip to content

Latest commit

 

History

History
235 lines (169 loc) · 8.84 KB

File metadata and controls

235 lines (169 loc) · 8.84 KB

Python By Example

Python By Example is a Go By Example-inspired learning site for Python 3.13. It presents small literate examples with prose, source fragments, expected output, official Python documentation links, and an editable runner.

Production: https://www.pythonbyexample.dev (workers.dev is disabled so the custom-domain WAF and rate-limit posture cannot be bypassed).

Features

  • 109 curated Python 3.13 examples in learning order
  • Literate source/output cells for each example walkthrough
  • Editable complete examples powered by CodeMirror
  • Read-only syntax highlighting powered by Shiki
  • Example execution in isolated Cloudflare Dynamic Python Workers
  • Official Python 3.13 documentation links per example
  • Workers Assets for static files
  • Fingerprinted CSS/JS assets with immutable cache headers
  • Versioned Worker Cache API keys for rendered HTML
  • SEO metadata, canonical URLs, JSON-LD structured data, and a sitemap for home and example pages
  • Client-side example search on the home page (press / to focus)
  • Dark mode via prefers-color-scheme, including dual-theme code highlighting
  • Per-example social-card images composed from the marginalia figure set
  • Learner-behavior reporting from Worker wide events (docs/learner-analytics.md)
  • A quality-gate suite for registries, rubric scores, coverage, and teaching structure that fails builds when content regresses
  • /about page describing the verification pipeline, runner sandbox, figure grammar, and live design tokens
  • Copy buttons on read-only source cells
  • Keyboard prev/next navigation (/) on example pages
  • "Copy link" sharing of the runner's current code via #code= URLs

Attribution

Python By Example is inspired by:

  • Go by Example for the concise example-per-page teaching format.
  • Rust By Example for practical runnable examples and expected output.
  • Literate programming, especially the idea that prose and source should explain each other rather than live as separate artifacts.
  • Notebook-style tools such as marimo for the source/output adjacency used in the walkthrough cells.

The project uses:

  • Cloudflare Workers and Python Workers for deployment and runtime.
  • FastAPI for application routing.
  • Shiki for read-only syntax highlighting.
  • CodeMirror for the editable example editor.
  • Lucide for the copy/check/x glyphs on the code-cell copy button.

Python documentation links point to the official Python documentation.

Architecture

  • src/main.py is the Cloudflare Worker entrypoint.
  • FastAPI handles request routing through the Cloudflare ASGI bridge.
  • src/app.py renders pages from simple placeholder templates in src/templates/.
  • src/example_sources/*.md contains the human-editable examples.
  • src/example_loader.py parses Markdown examples into the runtime catalog.
  • src/examples.py is a compatibility shim for the loaded catalog.
  • src/example_sources_data.py is generated embedded source data for Cloudflare Workers.
  • public/ contains static assets served by Workers Assets.
  • python_modules/ is generated by the Workers tooling and intentionally ignored by Git.

Example runs are executed by Dynamic Workers. The parent Worker creates a Dynamic Worker module from the submitted code, disables outbound network access, and keys Worker Loader reuse by Python version, example slug, and code hash.

Development

This project is developed with red-green-refactor TDD:

  1. Write or update a failing test.
  2. Implement the smallest change that makes it pass.
  3. Refactor while keeping tests green.

Install the exact committed Python and Node dependency sets, then run:

uv sync --locked --all-groups
npm ci --ignore-scripts
make test

The test suite (and the example loader it imports) requires Python 3.13; make test runs it through uv run --python 3.13 so a system python3 on another version still works.

After cloning, install the local git hooks once so merges and rebases regenerate src/asset_manifest.py instead of producing conflicts:

./scripts/install-git-hooks.sh

Run locally on Workers with the repository-pinned Wrangler:

make dev

Open:

http://localhost:9696

Verification

Run a local Worker before the full browser-backed verification:

make dev

Then run the main checks before deploying or pushing:

make verify
scripts/format_examples.py --check
make verify-python-version VERSION=3.13
git diff --check

make seo-cache-lint verifies that:

  • home and all example pages have SEO metadata
  • canonical and Open Graph URLs are correct
  • HTML references fingerprinted CSS/JS assets
  • generated asset hashes match current source assets
  • HTML_CACHE_VERSION is fresh
  • Worker Cache API keys include the HTML version
  • prototype layout pages are not cached

make browser-layout-test launches headless Chrome and checks the rendered Shiki code-block layout so generated line markup does not create visual blank rows.

Asset fingerprinting and cache busting

Source assets live at stable paths such as:

public/site.css
public/syntax-highlight.js
public/editor.js
public/runner.js

Before tests/deploy, regenerate embedded example data, fingerprinted asset copies, and Python manifests:

make build

This writes files such as:

public/site.<hash>.css
public/syntax-highlight.<hash>.js
public/editor.<hash>.js
public/runner.<hash>.js
src/asset_manifest.py

Rendered HTML uses only fingerprinted asset URLs. public/_headers gives these immutable cache headers:

Cache-Control: public, max-age=31536000, immutable

Rendered HTML pages are cached separately with a generated HTML_CACHE_VERSION in the Worker Cache API key. When templates, examples, app code, or asset fingerprints change, the generated HTML cache key changes too.

Prototype layout routes under /layout-options/* bypass the Worker Cache API and return Cache-Control: no-store.

Deploy

Run checks, then deploy:

make verify
scripts/format_examples.py --check
make deploy

make deploy first runs make check-generated, which rebuilds and rejects any generated output not committed to the branch. It then syncs the ignored Python Workers dependency bundle before Wrangler deploys.

Contributing

See CONTRIBUTING.md for pull request workflow, example editing rules, and verification commands.

Updating examples or Python version

Edit Markdown files under src/example_sources/.

Each example has:

  • TOML frontmatter with slug, title, section, summary, and doc_path
  • exactly one :::program block for the full editable source
  • one or more :::cell blocks for verified prose/source/output teaching cells
  • optional :::note blocks

After editing examples, run:

make build
make verify-examples
scripts/format_examples.py --check
make check-generated

After adding an example or journey (or changing card title, summary, figure, or card CSS), regenerate social cards. make check-generated verifies the committed card-input provenance, exact JPEG set, JPEG decodability, and 1200×630 dimensions without byte-comparing Chrome's platform-variable raster output:

make social-cards

This composes 1200x630 cards for home, examples, and journeys, records their deterministic HTML-input hashes in public/og/manifest.json, and rasterizes JPEGs under public/og/ with headless Chrome (set CHROME_PATH if needed).

make quality-checks runs the registry, confusable-pair, broad-tour, footgun, notes, program/cell, prose-duplication, inline-link, scoring, figure, journey, and example-graph gates.

src/example_sources_data.py is generated and committed so Cloudflare Workers can load examples in production. Do not edit it by hand.

For a Python version migration, update python_version and docs_base_url in src/example_sources/manifest.toml, then run:

make verify-python-version VERSION=3.13

Use the active Cloudflare-supported Python version.

Cloudflare notes

  • wrangler.jsonc enables python_workers and python_dedicated_snapshot.
  • The parent Worker uses a LOADER Worker Loader binding.
  • Dynamic Worker IDs include Python version, example slug, and submitted code hash.
  • Dynamic Workers run with globalOutbound: null and tight CPU/subrequest limits.
  • POST example runs carry Cache-Control: no-store and are never cached.
  • Wrangler is an exact dev dependency in package-lock.json; make dev and make deploy install/use that local version through Pywrangler.
  • Production still requires the account-level WAF/rate-limit rule documented in docs/turnstile-runner-protection-spec.md; repository settings alone do not prove that external control is active.