This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Pythonlings is "Rustlings for Python": a terminal TUI (Textual) where learners fix small broken exercises and checks rerun on save. Published on PyPI as pythonlings (formerly python-learnings; the unrelated pylings PyPI name belongs to a different project).
pip install -e ".[dev]" # local install with pytest deps
python -m pytest -q # full suite
python -m pytest tests/unit/test_runner.py -q # one file
python -m pytest tests/unit/test_state.py::test_name -q # one test
pythonlings --root tests/fixtures/passing_curriculum verify # smoke-test: all solutions pass their checks
python -m build # sdist + wheelManual testing of flows: pythonlings init --path ./learn-python (create a learner workspace), pythonlings (TUI), pythonlings run variables1, pythonlings dry-run variables1, pythonlings solution variables1, pythonlings hint, pythonlings list, pythonlings topics, pythonlings reset. --root points any command at an arbitrary workspace (used heavily by tests against tests/fixtures/).
Two distinct trees in this repo:
- The application —
pythonlings/(installable package) - The curriculum — repo-root
exercises/,checks/,solutions/, andinfo.toml
At build time, hatch force-include maps the curriculum into the wheel as pythonlings/curriculum/ (see pyproject.toml). pythonlings init copies that bundled curriculum into a self-contained learner workspace; progress lives in <workspace>/.pythonlings/state.json (written atomically, with .bak recovery on corruption).
Each exercise is a triple that must stay in sync, plus a manifest entry:
exercises/<topic>/<name>.py— the broken code the learner edits, containing the# I AM NOT DONEmarker (defined incore/exercise.py)checks/<topic>/<name>.py— hidden bareassertstatements (not pytest)solutions/<name>.py— reference answerinfo.toml— ordered[[exercises]]entries withname,path,hint,docsURL; this file is the source of truth for exercise order and topics
When changing curriculum, update all four, then run the relevant tests plus pythonlings --root tests/fixtures/passing_curriculum verify. Exercise names are topic + ordinal (variables1, collections10).
An exercise passes when a generated runner script exec()s the exercise source and then the check source in a shared namespace, in a fresh subprocess with a 5s timeout. The check sees the exercise's variables directly — that's why checks are bare asserts. Passing checks alone aren't enough: the learner must also remove # I AM NOT DONE to advance.
pythonlings/core/— all filesystem, manifest, state, reset, solutions, and runner logic. No UI imports. (Checks rerun on a debounce in the TUI editor, not a filesystem watcher.)pythonlings/screens/andpythonlings/widgets/— Textual UI only;pythonlings/app.pywires them up;pythonlings.tcssholds styles.pythonlings/cli.py— argparse subcommands; entry pointpythonlings = "pythonlings.cli:main".
Keep UI behavior in screens/widgets and behavior logic in core — tests depend on this split (tests/unit/ for core, tests/integration/ for CLI/workspace flows, tests/tui/ for Textual pilot tests, fixtures in tests/fixtures/).
requires-python = ">=3.9": guard newer-stdlib usage (e.g.tomllibfalls back totomliincore/manifest.py);from __future__ import annotationsat the top of modules.- Async tests run under
pytest-asyncioin auto mode (configured inpyproject.toml). - Tests are named
test_<behavior>.py/test_<expected_behavior>. - Commits use conventional prefixes (
feat:,fix:,docs:,chore:); work flows throughfeature/*→dev→main. AGENTS.mdholds the same contributor guidelines in long form.