Improve pylings interaction model#2
Closed
abhiksark wants to merge 96 commits into
Closed
Conversation
Captures the brainstormed decisions: single pylings command, Textual TUI, watch loop, strict linear gating via '# I AM NOT DONE', info.toml metadata, single-file exercises with bare asserts, subprocess-based verifier, pip-installable distribution.
- Add 're-broken content' migration step (existing exercises ship with correct answers; placeholders needed for real fix-to-pass UX) - Add .gitignore migration for /.pylings/ runtime dir - Define Manifest dataclass that was referenced but never spelled out - Add formal .pylings/state.json schema (JSON arrays, not Python sets) - Document --root global CLI flag used by tests - Clarify n binding: skips success animation, not a redundant 'advance' - Clarify h binding: hint section is collapsed by default in output panel - Make welcome_message / final_message optional with defaults - Expand reset state transitions for X==current, X<current, X>current
Critical: - Split 'pass' into TUI-pass (exit+marker) and verify-pass (exit only). Fixes the contradiction where CI was supposed to run verify green on a curriculum that is broken-by-design. - Pin .pylings/ to <root>/.pylings/ for test isolation under --root. Hardening: - Force UTF-8 encoding in subprocess so non-ASCII output doesn't mangle on misconfigured-locale systems. - Reject empty info.toml (zero exercises) instead of silently launching an empty TUI. - Document lazy-import rule for Textual: subcommands must stay sub-100ms cold-start; only watch/no-arg pay the TUI import cost. - Add --version flag (argparse-handled). - Note pyproject package-data inclusion for pylings.tcss. - Note solutions/ directory as a known follow-up. Updated success criterion #5 to match the new verify model, and added #7 for cold-start latency.
20-task plan with TDD discipline at every step. Each task has explicit file paths, complete code blocks, expected test output, and a commit template. Coverage maps 1:1 to the spec: core data model (Tasks 2-7), CLI subcommands (Tasks 8-12), cold-start latency guard (Task 13), async watcher (Task 14), Textual TUI (Tasks 15-17), curriculum migration (Task 18-19), and CI/docs (Task 20).
Adds pyproject.toml with hatchling backend, package directories, __main__ entry point, and a stub CLI that prints the version. Removes pylings.sh, pylings.py, and the old per-exercise test files (those will be folded into the exercises themselves in a later task).
These were committed before .gitignore covered them. .gitignore already excludes __pycache__/ in the global Python block, so they won't return.
Exercise is a frozen dataclass describing one curriculum item plus the done-marker check. RunResult is the value returned by the subprocess runner (added now alongside Exercise; the runner itself comes next).
Four fixture exercises covering the failure modes the runner has to distinguish: passing, AssertionError, SyntaxError, and 'tests pass but the I AM NOT DONE marker is still present'. Used by manifest, runner, and CLI integration tests.
Loads and validates the curriculum manifest: format_version must be 1, exercises array must be non-empty, paths must exist, names must be unique. Welcome and final messages default if omitted.
run() spawns python <exercise.py>, captures stdout/stderr with forced UTF-8 encoding, enforces a 5s timeout, and returns a RunResult. The 'passed' flag combines exit code 0 with the absence of the # I AM NOT DONE marker. run_verify() is a sibling that ignores the marker — used by 'pylings verify' for CI and curriculum-author validation.
State tracks completed exercises and the current pointer. Atomic writes via tmp-and-rename. Corruption recovery: bad JSON gets backed up to .bak and a fresh state is returned. Sets serialized as sorted arrays for deterministic diffs.
snapshot() copies a pristine exercise to .pylings/originals/ once; subsequent calls are no-ops. restore() overwrites the live file from the snapshot. Missing snapshot raises ResetError with a clear message.
argparse dispatch with --root and --version globals plus subcommand stubs. verify is wired end-to-end: loads the manifest, runs each exercise with run_verify (marker-agnostic), exits 1 on first failure or 2 on manifest errors. Textual import deferred behind the watch branch so subcommands stay fast.
Prints every exercise in curriculum order with a status marker: ✓ (completed), ● (current), 🔒 (locked / not yet reached). 'Current' is the saved current or, if none saved, the first pending exercise.
Looks up the exercise by name in info.toml and prints its hint. Unknown names exit non-zero with a clear error.
Runs a single exercise and prints its output. Distinct exit codes for script failure, timeout, and the 'tests pass but marker still present' case. Stderr/stdout are passed through so tracebacks remain real.
Snapshots every exercise on the first command that touches files (verify, list, run, reset, watch). reset restores from .pylings/ originals/ after a y/N prompt, or unconditionally with --yes.
Asserts that 'textual' is never imported during pylings hint / list / run / verify. Uses python -X importtime and greps the trace. Prevents accidental top-level Textual imports from regressing subcommand responsiveness.
watch() is an async iterator yielding once per debounced change to the target file. Caller decides what to do with each event (typically re-run the exercise).
ProgressBar renders a fixed-width unicode bar plus the X/Y count. Stylesheet sets up the three-region layout (top progress, middle main split into tree+output, slide-down hint section).
Ten exercises covering writing, reading, the with statement, line handling, append mode, iteration, writing line lists, transforming contents, word counting, and a copy-with-transform challenge, on an easy-to-hard ramp. Each exercise works on a tempfile and cleans up. Verified: every broken form fails and a correct solution passes.
Twelve exercises covering class definition, __init__, instance attributes, methods, self, default attributes, method composition, __str__/__repr__, class vs instance attributes, factory methods, and a complete-class challenge, on an easy-to-hard ramp. Verified: every broken form fails its check and a correct solution passes.
Ten exercises covering lambdas, map, filter, sorted with a key, higher-order functions, returning functions, functools.reduce, any/all, and a data-pipeline challenge, on an easy-to-hard ramp. Verified: every broken form fails its check and a correct solution passes.
Ten exercises covering higher-order functions, simple decorators, @ syntax, preserving return values, *args/**kwargs wrappers, functools.wraps, call counting, decorator factories, stacking, and a memoize challenge, on an easy-to-hard ramp. Verified: every broken form fails its check and a correct solution passes.
Ten exercises covering yield, generator consumption, for-iteration, generator expressions, next(), guarded infinite generators, the iterator protocol (__iter__/__next__), and a streaming-transform challenge, on an easy-to-hard ramp. Verified: every broken form fails its check and a correct solution passes.
Eight exercises covering the with statement, automatic cleanup, __enter__/__exit__, returning a value from __enter__, contextlib.contextmanager, exception suppression, and a managed-resource challenge, on an easy-to-hard ramp. Verified: every broken form fails its check and a correct solution passes.
Eight exercises covering @DataClass basics, typed fields, defaults, default_factory, the generated __init__/__repr__/__eq__, frozen dataclasses, and a dataclass-with-a-method challenge, on an easy-to-hard ramp. Verified: every broken form fails its check and a correct solution passes.
Eight exercises covering variable and function annotations, parameterized generics, Optional, tuple types, type aliases, Callable, and a fully-annotated-function challenge, on an easy-to-hard ramp. Verified: every broken form fails its check and a correct solution passes.
Ten exercises covering re.match, re.search, re.findall, character classes, quantifiers, anchors, capturing and named groups, re.sub, and a structured-data-extraction challenge, on an easy-to-hard ramp. Verified: every broken form fails its check and a correct solution passes.
Twelve exercises covering assertions, test functions, multi-case and parametrized tests, verifying raised exceptions, fixture-style setup, edge cases, testing a class, and a small-test-suite challenge, on an easy-to-hard ramp. Verified: every broken form fails its check and a correct solution passes.
Five testing checks inspected the test source for substrings like 'assert' and 'ValueError' — but those words also appear in the exercise's own comments, so an unsolved exercise (marker removed) passed falsely. The guards now strip comment lines before searching, and testing8 asserts its case list is genuinely populated.
Eight exercises covering base cases, factorial, recursive sums, counting, Fibonacci, string reversal, recursion over nested structures, and a recursive-search challenge, on an easy-to-hard ramp. Verified: every broken form fails its check and a correct solution passes.
Eight exercises covering import, from-import, import-as, the random and string modules, the __name__ variable, the __main__ guard, and a combined-imports challenge, on an easy-to-hard ramp. Verified: every broken form fails its check and a correct solution passes.
Ten exercises covering Counter, most_common, defaultdict for counting and grouping, namedtuple, deque as a stack and a queue, Counter arithmetic, and a tally-and-rank challenge, on an easy-to-hard ramp. Verified: every broken form fails its check and a correct solution passes.
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Test Plan