pl-e2e-test 0.1.7__tar.gz → 0.2.0__tar.gz
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.
- {pl_e2e_test-0.1.7/src/pl_e2e_test.egg-info → pl_e2e_test-0.2.0}/PKG-INFO +71 -50
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/README.md +70 -49
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/pyproject.toml +1 -1
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0/src/pl_e2e_test.egg-info}/PKG-INFO +71 -50
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/src/pl_e2e_test.egg-info/SOURCES.txt +4 -0
- pl_e2e_test-0.2.0/src/prairielearn_e2e/__init__.py +50 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/src/prairielearn_e2e/_git.py +47 -3
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/src/prairielearn_e2e/cli.py +37 -6
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/src/prairielearn_e2e/config.py +10 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/src/prairielearn_e2e/discovery.py +3 -17
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/src/prairielearn_e2e/fuzz.py +236 -42
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/src/prairielearn_e2e/harness.py +1 -1
- pl_e2e_test-0.2.0/src/prairielearn_e2e/instances.py +161 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/src/prairielearn_e2e/parallel.py +96 -9
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/src/prairielearn_e2e/pytest_plugin.py +90 -1
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/src/prairielearn_e2e/reporting.py +48 -3
- pl_e2e_test-0.2.0/src/prairielearn_e2e/repository.py +602 -0
- pl_e2e_test-0.2.0/src/prairielearn_e2e/runner.py +48 -0
- pl_e2e_test-0.2.0/src/prairielearn_e2e/runtime.py +387 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/tests/stub_git.py +33 -7
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/tests/test_cli.py +14 -4
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/tests/test_config.py +4 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/tests/test_discovery.py +28 -18
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/tests/test_fuzz.py +211 -1
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/tests/test_git.py +25 -1
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/tests/test_harness.py +84 -1
- pl_e2e_test-0.2.0/tests/test_instances.py +222 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/tests/test_parallel.py +186 -4
- pl_e2e_test-0.2.0/tests/test_publish_version.py +479 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/tests/test_pytest_plugin.py +125 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/tests/test_question_tests.py +96 -2
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/tests/test_reporting.py +35 -2
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/tests/test_repository.py +240 -1
- pl_e2e_test-0.2.0/tests/test_runtime.py +473 -0
- pl_e2e_test-0.1.7/src/prairielearn_e2e/__init__.py +0 -23
- pl_e2e_test-0.1.7/src/prairielearn_e2e/repository.py +0 -298
- pl_e2e_test-0.1.7/src/prairielearn_e2e/runner.py +0 -28
- pl_e2e_test-0.1.7/tests/test_publish_version.py +0 -169
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/LICENSE +0 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/MANIFEST.in +0 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/setup.cfg +0 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/src/pl_e2e_test.egg-info/dependency_links.txt +0 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/src/pl_e2e_test.egg-info/entry_points.txt +0 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/src/pl_e2e_test.egg-info/requires.txt +0 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/src/pl_e2e_test.egg-info/top_level.txt +0 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/src/prairielearn_e2e/__main__.py +0 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/src/prairielearn_e2e/backend.py +0 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/src/prairielearn_e2e/contract.py +0 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/src/prairielearn_e2e/errors.py +0 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/src/prairielearn_e2e/expected_errors.py +0 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/src/prairielearn_e2e/html_checks.py +0 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/src/prairielearn_e2e/metadata.py +0 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/src/prairielearn_e2e/question_tests.py +0 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/src/prairielearn_e2e/regression.py +0 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/src/prairielearn_e2e/server.py +0 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/src/prairielearn_e2e/sharding.py +0 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/tests/__init__.py +0 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/tests/conftest.py +0 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/tests/test_contract.py +0 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/tests/test_html_checks.py +0 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/tests/test_metadata.py +0 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/tests/test_regression.py +0 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/tests/test_server.py +0 -0
- {pl_e2e_test-0.1.7 → pl_e2e_test-0.2.0}/tests/test_sharding.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pl-e2e-test
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Fuzz PrairieLearn question lifecycles against the real Python element runtime
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/PrairieLearn/pl-e2e-test
|
|
@@ -38,6 +38,8 @@ The package installs the reusable `prairielearn_e2e` Python API and a single
|
|
|
38
38
|
- `pl-e2e-test parallel` runs local shards and merges their results.
|
|
39
39
|
- `pl-e2e-test add-seed` records a failing concrete variant seed.
|
|
40
40
|
- `pl-e2e-test reports` merges parallel shard results into terminal and JUnit reports.
|
|
41
|
+
- `pl-e2e-test instances` shows configured, installed, cached, and local PrairieLearn
|
|
42
|
+
revisions.
|
|
41
43
|
- `pl-e2e-test version` prints the installed version.
|
|
42
44
|
- `pl-e2e-test help [COMMAND]` prints top-level help, or one command's help.
|
|
43
45
|
|
|
@@ -45,6 +47,7 @@ The package installs the reusable `prairielearn_e2e` Python API and a single
|
|
|
45
47
|
|
|
46
48
|
- Python 3.13 or newer
|
|
47
49
|
- Git 2.30 or newer
|
|
50
|
+
- uv 0.9 or newer
|
|
48
51
|
|
|
49
52
|
The harness depends on pytest, Pyright, Ruff, Chevron, JSON Schema, lxml, NumPy,
|
|
50
53
|
and PrairieLearn. PrairieLearn's Python package is currently consumed from its Git
|
|
@@ -241,19 +244,34 @@ jobs:
|
|
|
241
244
|
```
|
|
242
245
|
|
|
243
246
|
The course must provide `.prairielearn/schemas/infoQuestion.json`. The harness checks
|
|
244
|
-
question metadata against that schema and
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
247
|
+
question metadata against that schema and resolves the configured PrairieLearn version
|
|
248
|
+
before each run. The default, `latest`, resolves the Git source's remote `HEAD`; a full
|
|
249
|
+
commit, branch, or unambiguous tag can be selected instead. Moving selectors require
|
|
250
|
+
network access and never silently fall back to stale cache state.
|
|
251
|
+
|
|
252
|
+
The selected revision's Python package and core elements are always used together. The
|
|
253
|
+
harness keeps a blob-filtered bare repository in the platform-standard user cache and
|
|
254
|
+
hydrates only `apps/prairielearn/python` and `apps/prairielearn/elements`. It launches
|
|
255
|
+
the selected Python package through a frozen uv overlay, without changing the course's
|
|
256
|
+
`pyproject.toml`, `uv.lock`, or virtual environment. A previously hydrated exact commit
|
|
257
|
+
can be reused offline when its uv dependencies are also cached.
|
|
249
258
|
|
|
250
259
|
To use an existing PrairieLearn worktree or bare repository instead, pass
|
|
251
260
|
`--prairielearn-path /path/to/PrairieLearn`. The repository must already contain the
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
261
|
+
selected revision. An explicit path is authoritative: the harness resolves selectors
|
|
262
|
+
against its local refs, does not fetch into it, and does not fall back to the managed
|
|
263
|
+
cache. For a local repository, `latest` means its current `HEAD`.
|
|
264
|
+
|
|
265
|
+
Inspect available revisions without contacting remotes:
|
|
266
|
+
|
|
267
|
+
```sh
|
|
268
|
+
uv run pl-e2e-test instances
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Add `--refresh` to resolve the configured moving selector and verify remote access.
|
|
272
|
+
The table distinguishes fully hydrated runtime revisions from legacy element-only cache
|
|
273
|
+
entries. Standard cache environment settings such as `XDG_CACHE_HOME` can relocate the
|
|
274
|
+
managed cache; CI systems can persist that directory between runs if desired.
|
|
257
275
|
|
|
258
276
|
### Harness configuration
|
|
259
277
|
|
|
@@ -261,11 +279,17 @@ Selector settings are read automatically from the course's `pyproject.toml`:
|
|
|
261
279
|
|
|
262
280
|
```toml
|
|
263
281
|
[tool.pl-e2e-test]
|
|
282
|
+
prairielearnVersion = "latest"
|
|
264
283
|
elementSolutionSelectors = ["pl-sketch"]
|
|
265
284
|
nonGradableSelectors = ['pl-sketch[read-only="true"]', 'pl-sketch[read-only="1"]']
|
|
266
285
|
```
|
|
267
286
|
|
|
268
|
-
These are the defaults. `
|
|
287
|
+
These are the defaults. Omitting `prairielearnVersion` is equivalent to `"latest"`.
|
|
288
|
+
Use a full commit for reproducible or offline runs, or a branch/tag when a named moving
|
|
289
|
+
target is intentional. `--prairielearn-version SELECTOR` overrides the TOML value for
|
|
290
|
+
one invocation, and replay commands always print the resolved full commit.
|
|
291
|
+
|
|
292
|
+
`elementSolutionSelectors` identifies elements that provide
|
|
269
293
|
solutions without a `correct_answers` entry. It only relaxes that entry requirement;
|
|
270
294
|
it does not generate a test submission. `nonGradableSelectors` excludes display-only
|
|
271
295
|
answers from grading checks. Each supplied list replaces its default; omitted settings
|
|
@@ -303,7 +327,7 @@ does not provide one itself.
|
|
|
303
327
|
For every gradable `answers-name`, the correct test case must provide:
|
|
304
328
|
|
|
305
329
|
- a non-null value in `data["raw_submitted_answers"]` under the answer name, or one or
|
|
306
|
-
more compound input keys prefixed with `answers-name
|
|
330
|
+
more compound input keys prefixed with `answers-name-` or `answers-name.`; and
|
|
307
331
|
- `data["partial_scores"][answers_name]["score"] = 1`.
|
|
308
332
|
|
|
309
333
|
Raw values must survive PrairieLearn's JSON boundary and have the same browser transport
|
|
@@ -332,11 +356,12 @@ def test(data):
|
|
|
332
356
|
```
|
|
333
357
|
|
|
334
358
|
Compound elements keep the declared answer name as their grading identity while using
|
|
335
|
-
namespaced child fields for browser inputs.
|
|
336
|
-
`answers-name="match"` may test and parse
|
|
337
|
-
|
|
338
|
-
`
|
|
339
|
-
|
|
359
|
+
namespaced child fields for browser inputs. Hyphen and dot namespace boundaries are
|
|
360
|
+
supported. For example, an element with `answers-name="match"` may test and parse
|
|
361
|
+
`match-dropdown-0` and `match-dropdown-1`, while `answers-name="average"` may use
|
|
362
|
+
`average.main`. The element stores its score under the declared answer name and does not
|
|
363
|
+
need to invent a direct raw value for it. After parsing, every raw field associated with
|
|
364
|
+
a declared gradable answer must either remain non-null under its child key or produce a
|
|
340
365
|
non-null value under its declared parent answer. This also supports elements that
|
|
341
366
|
normalize `answer-input` into `answer`. Unassociated transport fields from file-producing
|
|
342
367
|
elements may instead be consumed into `submitted_answers["_files"]`; they are not answer
|
|
@@ -454,12 +479,16 @@ course's TOML settings, or `config=load_config("settings.toml")` for an explicit
|
|
|
454
479
|
`load_config` and `HarnessConfig` are exported from `prairielearn_e2e`. Omitting
|
|
455
480
|
`config` uses the built-in defaults.
|
|
456
481
|
|
|
482
|
+
The command-line runner and high-level `run_parallel` API perform version resolution
|
|
483
|
+
and uv bootstrapping. Manually constructed `PrairieLearnBackend` and `QuestionHarness`
|
|
484
|
+
objects continue to use the PrairieLearn package already loaded by their interpreter.
|
|
485
|
+
|
|
457
486
|
## Development
|
|
458
487
|
|
|
459
488
|
This project uses uv 0.9 or newer:
|
|
460
489
|
|
|
461
490
|
```sh
|
|
462
|
-
|
|
491
|
+
make install
|
|
463
492
|
make test
|
|
464
493
|
make format
|
|
465
494
|
make smoke-dist
|
|
@@ -471,44 +500,36 @@ into an isolated target, and imports it using the locked development dependencie
|
|
|
471
500
|
|
|
472
501
|
## Publishing
|
|
473
502
|
|
|
474
|
-
There are three supported publishing paths. Choose one for a release; do not run the
|
|
475
|
-
local and GitHub paths for the same version because PyPI versions are immutable.
|
|
476
|
-
|
|
477
|
-
### GitHub-hosted trusted publishing
|
|
478
|
-
|
|
479
503
|
Configure the PyPI project to trust the `publish.yml` workflow in the GitHub `pypi`
|
|
480
|
-
environment.
|
|
504
|
+
environment. Releases must start from a clean `main` that is synchronized with
|
|
505
|
+
`origin/main`, with `gh` authenticated and the publishing workflow available. To bump
|
|
506
|
+
the current version, run one of:
|
|
481
507
|
|
|
482
508
|
```sh
|
|
483
|
-
make publish
|
|
509
|
+
make publish patch
|
|
510
|
+
make publish minor
|
|
511
|
+
make publish major
|
|
484
512
|
```
|
|
485
513
|
|
|
486
|
-
|
|
487
|
-
the full checks, updates `pyproject.toml` and `uv.lock`, builds and smoke-tests the
|
|
488
|
-
distributions, creates a release commit and annotated `v0.2.0` tag, and atomically
|
|
489
|
-
pushes the branch and tag. The tag starts the workflow on `ubuntu-latest`; the workflow
|
|
490
|
-
rechecks the tag/version match, tests, builds, generates attestations, and publishes
|
|
491
|
-
with PyPI trusted-publishing OIDC credentials. Use `REMOTE=name` for a different Git
|
|
492
|
-
remote.
|
|
493
|
-
|
|
494
|
-
If an unpushed commit already contains the requested version, use the guarded bypass:
|
|
514
|
+
If the synchronized commit already contains the intended, not-yet-tagged version, use:
|
|
495
515
|
|
|
496
516
|
```sh
|
|
497
|
-
make publish
|
|
517
|
+
make publish current
|
|
498
518
|
```
|
|
499
519
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
520
|
+
The release command runs the shared `make release-check` gate, updates
|
|
521
|
+
`pyproject.toml` and `uv.lock` when needed, creates a release commit and annotated tag,
|
|
522
|
+
atomically pushes `main` and the tag, and watches the trusted-publishing workflow to
|
|
523
|
+
completion. Preview the full process without changing files, refs, remotes, or workflows
|
|
524
|
+
with:
|
|
504
525
|
|
|
505
526
|
```sh
|
|
506
|
-
make publish
|
|
527
|
+
make publish patch DRY_RUN=1
|
|
507
528
|
```
|
|
508
529
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
530
|
+
If the release tag exists but trusted publishing needs to be dispatched again, run
|
|
531
|
+
`make publish-pypi`. This validates that the tag matches the current version and is
|
|
532
|
+
reachable from `origin/main`, then dispatches and watches a new workflow run.
|
|
512
533
|
|
|
513
534
|
### Local self-hosted GitHub runner
|
|
514
535
|
|
|
@@ -517,11 +538,11 @@ inside Docker. The container has no host mounts and does not receive the Docker
|
|
|
517
538
|
Authenticate `gh` with repository administration permission, then run:
|
|
518
539
|
|
|
519
540
|
```sh
|
|
520
|
-
make
|
|
521
|
-
make publish-self-hosted RELEASE_TAG=v0.2.0
|
|
541
|
+
make publish-self-hosted
|
|
522
542
|
```
|
|
523
543
|
|
|
524
|
-
The tag must already exist. This
|
|
525
|
-
`
|
|
526
|
-
|
|
527
|
-
|
|
544
|
+
The current version's tag must already exist. This ensures the local runner is online,
|
|
545
|
+
dispatches `publish.yml` from `main` with the `self-hosted` runner choice, and watches
|
|
546
|
+
the run. `make runner-ensure` starts Docker Desktop on macOS when needed and repairs the
|
|
547
|
+
runner before dispatch. Other lifecycle commands are `runner-status`, `runner-stop`,
|
|
548
|
+
`runner-remove`, `runner-logs`, and `clean-runner`.
|
|
@@ -14,6 +14,8 @@ The package installs the reusable `prairielearn_e2e` Python API and a single
|
|
|
14
14
|
- `pl-e2e-test parallel` runs local shards and merges their results.
|
|
15
15
|
- `pl-e2e-test add-seed` records a failing concrete variant seed.
|
|
16
16
|
- `pl-e2e-test reports` merges parallel shard results into terminal and JUnit reports.
|
|
17
|
+
- `pl-e2e-test instances` shows configured, installed, cached, and local PrairieLearn
|
|
18
|
+
revisions.
|
|
17
19
|
- `pl-e2e-test version` prints the installed version.
|
|
18
20
|
- `pl-e2e-test help [COMMAND]` prints top-level help, or one command's help.
|
|
19
21
|
|
|
@@ -21,6 +23,7 @@ The package installs the reusable `prairielearn_e2e` Python API and a single
|
|
|
21
23
|
|
|
22
24
|
- Python 3.13 or newer
|
|
23
25
|
- Git 2.30 or newer
|
|
26
|
+
- uv 0.9 or newer
|
|
24
27
|
|
|
25
28
|
The harness depends on pytest, Pyright, Ruff, Chevron, JSON Schema, lxml, NumPy,
|
|
26
29
|
and PrairieLearn. PrairieLearn's Python package is currently consumed from its Git
|
|
@@ -217,19 +220,34 @@ jobs:
|
|
|
217
220
|
```
|
|
218
221
|
|
|
219
222
|
The course must provide `.prairielearn/schemas/infoQuestion.json`. The harness checks
|
|
220
|
-
question metadata against that schema and
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
223
|
+
question metadata against that schema and resolves the configured PrairieLearn version
|
|
224
|
+
before each run. The default, `latest`, resolves the Git source's remote `HEAD`; a full
|
|
225
|
+
commit, branch, or unambiguous tag can be selected instead. Moving selectors require
|
|
226
|
+
network access and never silently fall back to stale cache state.
|
|
227
|
+
|
|
228
|
+
The selected revision's Python package and core elements are always used together. The
|
|
229
|
+
harness keeps a blob-filtered bare repository in the platform-standard user cache and
|
|
230
|
+
hydrates only `apps/prairielearn/python` and `apps/prairielearn/elements`. It launches
|
|
231
|
+
the selected Python package through a frozen uv overlay, without changing the course's
|
|
232
|
+
`pyproject.toml`, `uv.lock`, or virtual environment. A previously hydrated exact commit
|
|
233
|
+
can be reused offline when its uv dependencies are also cached.
|
|
225
234
|
|
|
226
235
|
To use an existing PrairieLearn worktree or bare repository instead, pass
|
|
227
236
|
`--prairielearn-path /path/to/PrairieLearn`. The repository must already contain the
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
237
|
+
selected revision. An explicit path is authoritative: the harness resolves selectors
|
|
238
|
+
against its local refs, does not fetch into it, and does not fall back to the managed
|
|
239
|
+
cache. For a local repository, `latest` means its current `HEAD`.
|
|
240
|
+
|
|
241
|
+
Inspect available revisions without contacting remotes:
|
|
242
|
+
|
|
243
|
+
```sh
|
|
244
|
+
uv run pl-e2e-test instances
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Add `--refresh` to resolve the configured moving selector and verify remote access.
|
|
248
|
+
The table distinguishes fully hydrated runtime revisions from legacy element-only cache
|
|
249
|
+
entries. Standard cache environment settings such as `XDG_CACHE_HOME` can relocate the
|
|
250
|
+
managed cache; CI systems can persist that directory between runs if desired.
|
|
233
251
|
|
|
234
252
|
### Harness configuration
|
|
235
253
|
|
|
@@ -237,11 +255,17 @@ Selector settings are read automatically from the course's `pyproject.toml`:
|
|
|
237
255
|
|
|
238
256
|
```toml
|
|
239
257
|
[tool.pl-e2e-test]
|
|
258
|
+
prairielearnVersion = "latest"
|
|
240
259
|
elementSolutionSelectors = ["pl-sketch"]
|
|
241
260
|
nonGradableSelectors = ['pl-sketch[read-only="true"]', 'pl-sketch[read-only="1"]']
|
|
242
261
|
```
|
|
243
262
|
|
|
244
|
-
These are the defaults. `
|
|
263
|
+
These are the defaults. Omitting `prairielearnVersion` is equivalent to `"latest"`.
|
|
264
|
+
Use a full commit for reproducible or offline runs, or a branch/tag when a named moving
|
|
265
|
+
target is intentional. `--prairielearn-version SELECTOR` overrides the TOML value for
|
|
266
|
+
one invocation, and replay commands always print the resolved full commit.
|
|
267
|
+
|
|
268
|
+
`elementSolutionSelectors` identifies elements that provide
|
|
245
269
|
solutions without a `correct_answers` entry. It only relaxes that entry requirement;
|
|
246
270
|
it does not generate a test submission. `nonGradableSelectors` excludes display-only
|
|
247
271
|
answers from grading checks. Each supplied list replaces its default; omitted settings
|
|
@@ -279,7 +303,7 @@ does not provide one itself.
|
|
|
279
303
|
For every gradable `answers-name`, the correct test case must provide:
|
|
280
304
|
|
|
281
305
|
- a non-null value in `data["raw_submitted_answers"]` under the answer name, or one or
|
|
282
|
-
more compound input keys prefixed with `answers-name
|
|
306
|
+
more compound input keys prefixed with `answers-name-` or `answers-name.`; and
|
|
283
307
|
- `data["partial_scores"][answers_name]["score"] = 1`.
|
|
284
308
|
|
|
285
309
|
Raw values must survive PrairieLearn's JSON boundary and have the same browser transport
|
|
@@ -308,11 +332,12 @@ def test(data):
|
|
|
308
332
|
```
|
|
309
333
|
|
|
310
334
|
Compound elements keep the declared answer name as their grading identity while using
|
|
311
|
-
namespaced child fields for browser inputs.
|
|
312
|
-
`answers-name="match"` may test and parse
|
|
313
|
-
|
|
314
|
-
`
|
|
315
|
-
|
|
335
|
+
namespaced child fields for browser inputs. Hyphen and dot namespace boundaries are
|
|
336
|
+
supported. For example, an element with `answers-name="match"` may test and parse
|
|
337
|
+
`match-dropdown-0` and `match-dropdown-1`, while `answers-name="average"` may use
|
|
338
|
+
`average.main`. The element stores its score under the declared answer name and does not
|
|
339
|
+
need to invent a direct raw value for it. After parsing, every raw field associated with
|
|
340
|
+
a declared gradable answer must either remain non-null under its child key or produce a
|
|
316
341
|
non-null value under its declared parent answer. This also supports elements that
|
|
317
342
|
normalize `answer-input` into `answer`. Unassociated transport fields from file-producing
|
|
318
343
|
elements may instead be consumed into `submitted_answers["_files"]`; they are not answer
|
|
@@ -430,12 +455,16 @@ course's TOML settings, or `config=load_config("settings.toml")` for an explicit
|
|
|
430
455
|
`load_config` and `HarnessConfig` are exported from `prairielearn_e2e`. Omitting
|
|
431
456
|
`config` uses the built-in defaults.
|
|
432
457
|
|
|
458
|
+
The command-line runner and high-level `run_parallel` API perform version resolution
|
|
459
|
+
and uv bootstrapping. Manually constructed `PrairieLearnBackend` and `QuestionHarness`
|
|
460
|
+
objects continue to use the PrairieLearn package already loaded by their interpreter.
|
|
461
|
+
|
|
433
462
|
## Development
|
|
434
463
|
|
|
435
464
|
This project uses uv 0.9 or newer:
|
|
436
465
|
|
|
437
466
|
```sh
|
|
438
|
-
|
|
467
|
+
make install
|
|
439
468
|
make test
|
|
440
469
|
make format
|
|
441
470
|
make smoke-dist
|
|
@@ -447,44 +476,36 @@ into an isolated target, and imports it using the locked development dependencie
|
|
|
447
476
|
|
|
448
477
|
## Publishing
|
|
449
478
|
|
|
450
|
-
There are three supported publishing paths. Choose one for a release; do not run the
|
|
451
|
-
local and GitHub paths for the same version because PyPI versions are immutable.
|
|
452
|
-
|
|
453
|
-
### GitHub-hosted trusted publishing
|
|
454
|
-
|
|
455
479
|
Configure the PyPI project to trust the `publish.yml` workflow in the GitHub `pypi`
|
|
456
|
-
environment.
|
|
480
|
+
environment. Releases must start from a clean `main` that is synchronized with
|
|
481
|
+
`origin/main`, with `gh` authenticated and the publishing workflow available. To bump
|
|
482
|
+
the current version, run one of:
|
|
457
483
|
|
|
458
484
|
```sh
|
|
459
|
-
make publish
|
|
485
|
+
make publish patch
|
|
486
|
+
make publish minor
|
|
487
|
+
make publish major
|
|
460
488
|
```
|
|
461
489
|
|
|
462
|
-
|
|
463
|
-
the full checks, updates `pyproject.toml` and `uv.lock`, builds and smoke-tests the
|
|
464
|
-
distributions, creates a release commit and annotated `v0.2.0` tag, and atomically
|
|
465
|
-
pushes the branch and tag. The tag starts the workflow on `ubuntu-latest`; the workflow
|
|
466
|
-
rechecks the tag/version match, tests, builds, generates attestations, and publishes
|
|
467
|
-
with PyPI trusted-publishing OIDC credentials. Use `REMOTE=name` for a different Git
|
|
468
|
-
remote.
|
|
469
|
-
|
|
470
|
-
If an unpushed commit already contains the requested version, use the guarded bypass:
|
|
490
|
+
If the synchronized commit already contains the intended, not-yet-tagged version, use:
|
|
471
491
|
|
|
472
492
|
```sh
|
|
473
|
-
make publish
|
|
493
|
+
make publish current
|
|
474
494
|
```
|
|
475
495
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
496
|
+
The release command runs the shared `make release-check` gate, updates
|
|
497
|
+
`pyproject.toml` and `uv.lock` when needed, creates a release commit and annotated tag,
|
|
498
|
+
atomically pushes `main` and the tag, and watches the trusted-publishing workflow to
|
|
499
|
+
completion. Preview the full process without changing files, refs, remotes, or workflows
|
|
500
|
+
with:
|
|
480
501
|
|
|
481
502
|
```sh
|
|
482
|
-
make publish
|
|
503
|
+
make publish patch DRY_RUN=1
|
|
483
504
|
```
|
|
484
505
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
506
|
+
If the release tag exists but trusted publishing needs to be dispatched again, run
|
|
507
|
+
`make publish-pypi`. This validates that the tag matches the current version and is
|
|
508
|
+
reachable from `origin/main`, then dispatches and watches a new workflow run.
|
|
488
509
|
|
|
489
510
|
### Local self-hosted GitHub runner
|
|
490
511
|
|
|
@@ -493,11 +514,11 @@ inside Docker. The container has no host mounts and does not receive the Docker
|
|
|
493
514
|
Authenticate `gh` with repository administration permission, then run:
|
|
494
515
|
|
|
495
516
|
```sh
|
|
496
|
-
make
|
|
497
|
-
make publish-self-hosted RELEASE_TAG=v0.2.0
|
|
517
|
+
make publish-self-hosted
|
|
498
518
|
```
|
|
499
519
|
|
|
500
|
-
The tag must already exist. This
|
|
501
|
-
`
|
|
502
|
-
|
|
503
|
-
|
|
520
|
+
The current version's tag must already exist. This ensures the local runner is online,
|
|
521
|
+
dispatches `publish.yml` from `main` with the `self-hosted` runner choice, and watches
|
|
522
|
+
the run. `make runner-ensure` starts Docker Desktop on macOS when needed and repairs the
|
|
523
|
+
runner before dispatch. Other lifecycle commands are `runner-status`, `runner-stop`,
|
|
524
|
+
`runner-remove`, `runner-logs`, and `clean-runner`.
|