pll-python 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +124 -1
  2. package/cli.cjs +1555 -1179
  3. package/package.json +2 -2
  4. package/worker.cjs +44 -781
package/README.md CHANGED
@@ -32,7 +32,7 @@ print(greeting)
32
32
  | Line in the file | What it does |
33
33
  | --- | --- |
34
34
  | `#level raw` | Nothing checked. Plain Python plus PLL's libraries. This is what you get with no line at all. |
35
- | `#level beginner` | Warns about reassigning a variable, hiding another name (including built-ins like `list`), and `global` / `nonlocal`. Type annotations are checked as the program runs. If it finds a problem, the file does **not** run. |
35
+ | `#level beginner` | Warns about reassigning a variable, hiding another name (a built-in like `list`, or one of PLL's own like `circle`), and `global` / `nonlocal`. Type annotations are checked as the program runs. If it finds a problem, the file does **not** run. |
36
36
  | `#level intermediate` | Same, but reassignment is allowed inside a function. |
37
37
  | `#level advanced` | No pre-run checks; annotations still checked, by Python's own rules. |
38
38
 
@@ -60,6 +60,129 @@ pll hw.py --no-tests # just the program
60
60
  -v, --version
61
61
  ```
62
62
 
63
+ ## Authoring Examplar bundles
64
+
65
+ Examplar assesses a *test suite* rather than an implementation: students
66
+ write tests first, and those tests are run against known-good
67
+ implementations (**wheats**), where they must all pass, and known-bad ones
68
+ (**chaffs**), where each must be caught by at least one failing test.
69
+
70
+ There is a complete worked example in the repository, with a student file,
71
+ two wheats, six chaffs and a staff suite:
72
+ [`samples/examplar_bundle/`](https://github.com/dbp/pll/tree/main/samples/examplar_bundle).
73
+
74
+ Lay the implementations out one file each:
75
+
76
+ ```
77
+ hw3/
78
+ wheats/reference.py known-good; every test must pass on these
79
+ wheats/alternative.py more than one stops tests over-fitting
80
+ chaffs/shout/1.py known-bad; each must be caught by some test
81
+ chaffs/shout/2.py
82
+ chaffs/total/1.py
83
+ ```
84
+
85
+ A chaff goes in a directory named after the function it breaks: the
86
+ student's report is one card per function, and that directory is how you say
87
+ which card it belongs on. Every function needs at least one chaff, or its
88
+ card could never say anything about how thorough their tests are.
89
+
90
+ Every wheat and chaff has to define the same functions, since a suite is run
91
+ against all of them interchangeably. Then:
92
+
93
+ ```bash
94
+ pll examplar build hw3/ -o hw3.json --verify staff_tests.py
95
+ ```
96
+
97
+ Only bytecode goes into the bundle, so your sources stay in your
98
+ repository. It is compiled by the Pyodide this package pins, which is why
99
+ bundles are built with this tool and not a local `python` — bytecode is tied
100
+ to the Python version, and this way it matches by construction.
101
+
102
+ `--verify` runs your own suite against the bundle and refuses to write it
103
+ unless the suite passes on every wheat and **fails on every chaff**. A chaff
104
+ no test can catch would silently never count for anyone:
105
+
106
+ ```
107
+ verifying with staff_tests.py:
108
+ ok wheat reference: all 2 pass
109
+ ok chaff shout/1: caught by test_shout_excites
110
+ BAD chaff shout/2: no test catches it - it would never count
111
+ BAD your own suite has no tests for total, so its chaffs are unproven
112
+ pll: 2 problem(s) with this bundle; not written.
113
+ ```
114
+
115
+ A function your own suite never touches fails too. Its chaffs are never run,
116
+ so nothing has shown they are catchable — the same hazard as a chaff no test
117
+ can catch, arrived at from the other direction.
118
+
119
+ It also catches a staff test that is itself wrong, since that shows up as a
120
+ wheat failure — with pytest's rewritten assertion, which is the whole point.
121
+ (Students never see this; their card names the failing test and stops there,
122
+ because the assertion states the correct answer. Here the reader is you, and
123
+ there is nothing to give away.)
124
+
125
+ ```
126
+ BAD wheat reference: your own tests do not all pass on it
127
+ test_shout: assert 'HI!' == 'hi!'
128
+ --- shout chaffs not checked: its tests have to pass first
129
+ ```
130
+
131
+ Those chaffs are not checked, here or for students: a suite that is wrong
132
+ fails on every chaff, so its coverage would measure the bug rather than the
133
+ suite. The same goes for a test that *errors* — nothing short of a pass
134
+ counts. It is decided per function, so `total` above is still scored.
135
+
136
+ A chaff's id is its filename, and it is the **only** thing a student is told
137
+ about a chaff they missed. `chaffs/shout/off-by-one.py` therefore hands over
138
+ the test they were meant to write; number them (`chaffs/shout/1.py`) unless
139
+ the name is genuinely uninformative. Numbering restarts in each directory,
140
+ since the card is already about one function. Wheat ids are harmless — those
141
+ are correct code.
142
+
143
+ Publish the bundle at a URL and point students at it from their file:
144
+
145
+ ```python
146
+ #level beginner
147
+ #examplar https://cs2000.example/hw3.json
148
+ ```
149
+
150
+ If any of your students use **vscode.dev** or a codespace, the extension host
151
+ there is a browser, so serve the bundle with CORS
152
+ (`samples/examplar_serve.mjs` in the repository is a dependency-free server
153
+ that gets these right):
154
+
155
+ ```
156
+ Access-Control-Allow-Origin: *
157
+ Access-Control-Expose-Headers: ETag
158
+ Access-Control-Allow-Headers: If-None-Match
159
+ ```
160
+
161
+ The last two matter more than they look. Without `Expose-Headers` the ETag is
162
+ invisible to script, so nothing is ever cached; without `Allow-Headers` the
163
+ conditional request fails its preflight, so students stay on whatever copy
164
+ they fetched first and never see a bundle you republish. Neither failure
165
+ reports itself.
166
+
167
+ Only their **definitions** run during the check — not their program — so a
168
+ `print` or an `input()` at the bottom of a student's file cannot disturb it,
169
+ and it is not run once per implementation. The workspace is unmounted while
170
+ it runs, so a test that opens a data file cannot work there; the card tells
171
+ the student that instead of claiming the test is wrong.
172
+
173
+ Students see one card per function, headed by the function name, with a
174
+ line for each phase: *"Against correct implementations: ..."* and
175
+ *"Against buggy implementations: ..."*. Neither gives anything away — the
176
+ first names a test that disagrees but not what it should have said, and the
177
+ second gives a chaff's id and nothing else. A chaff is judged only by the
178
+ tests of its own function, so a broken test of one function cannot flatter
179
+ or hold up another.
180
+
181
+ Bytecode hides the implementations from `inspect.getsource` and nothing
182
+ more — `dis` still tells the whole story to anyone who looks. That is a
183
+ deliberate trade: the autograder holds the grade, so this check is there to
184
+ be fast and honest rather than secret.
185
+
63
186
  ## Exit codes
64
187
 
65
188
  | Code | Meaning |