pll-python 0.1.0 → 0.1.2
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.
- package/README.md +124 -1
- package/cli.cjs +1493 -1183
- package/package.json +2 -2
- package/worker.cjs +21 -1
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 (
|
|
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 |
|