pll-python 0.5.0 → 0.6.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 +6 -5
  2. package/cli.cjs +32 -17
  3. package/package.json +1 -1
  4. package/worker.cjs +20 -18
package/README.md CHANGED
@@ -34,9 +34,9 @@ print(greeting)
34
34
  | Line in the file | What it does |
35
35
  | --- | --- |
36
36
  | `#level raw` | Nothing checked. Plain Python plus PLL's libraries. This is what you get with no line at all. |
37
- | `#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. |
37
+ | `#level beginner` | Warns about reassigning a variable, hiding another name (a built-in like `list`, or one of PLL's own like `circle`), `global` / `nonlocal`, and removing a name with `del` (not an item: `del d[key]` is fine). Type annotations are checked as the program runs. If it finds a problem, the file does **not** run. |
38
38
  | `#level intermediate` | Same, but reassignment is allowed inside a function. |
39
- | `#level advanced` | No pre-run checks; annotations still checked, by Python's own rules. |
39
+ | `#level advanced` | One check before the run: names used where they have no value, which every level but `raw` makes. Annotations still checked, by Python's own rules. |
40
40
 
41
41
  There is deliberately **no flag to override the level**. A file behaves the
42
42
  same way everywhere, which is the point of putting it in the file.
@@ -46,7 +46,8 @@ same way everywhere, which is the point of putting it in the file.
46
46
  `test_*` functions in the same file run once the program finishes, against
47
47
  what it defined, as they do in the editor. A test that prints shows what it
48
48
  printed under its result; `@pytest.mark.skip`, `skipif` and `xfail` work as
49
- in pytest:
49
+ in pytest. A test written with parameters is not run: what is wrong with it
50
+ is said above `Tests:`, apart from the tests that ran.
50
51
 
51
52
  ```bash
52
53
  pll hw.py # the program, then its tests
@@ -203,7 +204,7 @@ be fast and honest rather than secret.
203
204
  | 0 | Ran, and any tests passed |
204
205
  | 1 | The program raised, or Ctrl+C stopped it (even if it caught the `KeyboardInterrupt`; its tests are then not run) |
205
206
  | 2 | Level checks found problems, so it was not run |
206
- | 3 | A test failed |
207
+ | 3 | A test failed, or could not be run (it takes parameters) |
207
208
  | 64 | Bad usage, or Python could not start (it is given two minutes) |
208
209
  | 130 | A second Ctrl+C gave up waiting for the program to stop |
209
210
  | 141 | Its output could no longer be read - `pll hw.py \| head` |
@@ -261,7 +262,7 @@ folder as the script or a folder inside it (`open("data/2024.csv")`), and
261
262
  `__file__` is set, so `Path(__file__).parent` works. Files the program
262
263
  writes appear there afterwards, and files it deletes are deleted — the same
263
264
  as in the editor, with the same limits: at most 100 files, each at most
264
- 2 MB and 8 MB in all, nearest first; hidden and tools' folders (`.git`,
265
+ 50 MB and 100 MB in all, nearest first; hidden and tools' folders (`.git`,
265
266
  `venv`, `node_modules`) are left out. Files move as bytes, so a CSV in
266
267
  another encoding or a picture arrives exactly as it is.
267
268
 
package/cli.cjs CHANGED
@@ -137,19 +137,17 @@ var HEADER_RE = /^#\s*level\s+([a-z]+)\s*$/;
137
137
  function parseLevel(source) {
138
138
  const lines = source.split(/\r?\n/);
139
139
  for (const raw of lines) {
140
- const line = raw.trim();
141
- if (line.length === 0) {
140
+ if (raw.trim().length === 0) {
142
141
  continue;
143
142
  }
144
- const match = HEADER_RE.exec(line);
145
- if (!match) {
146
- return DEFAULT_LEVEL;
147
- }
148
- const name = match[1];
149
- return isLevel(name) ? name : DEFAULT_LEVEL;
143
+ return headerLevel(raw) ?? DEFAULT_LEVEL;
150
144
  }
151
145
  return DEFAULT_LEVEL;
152
146
  }
147
+ function headerLevel(line) {
148
+ const match = HEADER_RE.exec(line.trim());
149
+ return match !== null && isLevel(match[1]) ? match[1] : null;
150
+ }
153
151
  function levelHeaderProblem(source) {
154
152
  const lines = source.split(/\r?\n/);
155
153
  const valid = new RegExp(`^#\\s*level\\s+(?:${LEVEL_NAMES.join("|")})\\s*$`);
@@ -274,7 +272,7 @@ function closestLevel(named) {
274
272
  return best !== null && bestDistance <= Math.max(2, Math.floor(best.length / 3)) ? best : null;
275
273
  }
276
274
  function levelHasStaticChecks(level) {
277
- return level === LEVEL_BEGINNER || level === LEVEL_INTERMEDIATE;
275
+ return level !== LEVEL_RAW;
278
276
  }
279
277
 
280
278
  // src/run/finding.ts
@@ -303,11 +301,13 @@ function findingLocation(finding) {
303
301
  };
304
302
  }
305
303
  function formatFriendlyError(finding) {
304
+ return serializedFindingLines(serializeFinding(finding));
305
+ }
306
+ function serializedFindingLines(finding) {
306
307
  const lines = [];
307
308
  lines.push(`${finding.errorType}: ${finding.headline}`);
308
- const location = findingLocation(finding);
309
- if (location) {
310
- lines.push(` at ${location.label}`);
309
+ if (finding.location) {
310
+ lines.push(` at ${finding.location.label}`);
311
311
  }
312
312
  if (finding.howToFix.length > 0) {
313
313
  lines.push("");
@@ -345,7 +345,12 @@ function explainTestReport(event, fileName, level) {
345
345
  ...event,
346
346
  tests: event.tests.map(
347
347
  ({ error, ...test }) => error ? { ...test, finding: serializeFinding(findRuntimeFinding(fileName, level, error)) } : test
348
- )
348
+ ),
349
+ ...event.malformed ? {
350
+ malformed: event.malformed.map(
351
+ ({ problem, ...test }) => problem ? { ...test, finding: serializeFinding({ ...problem, fileName: problem.fileName ?? fileName, level }) } : test
352
+ )
353
+ } : {}
349
354
  };
350
355
  }
351
356
 
@@ -1002,8 +1007,8 @@ function needsPackages(code, siblings = []) {
1002
1007
  }
1003
1008
 
1004
1009
  // src/core/files.ts
1005
- var MAX_FILE_BYTES = 2 * 1024 * 1024;
1006
- var MAX_TOTAL_BYTES = 8 * 1024 * 1024;
1010
+ var MAX_FILE_BYTES = 50 * 1024 * 1024;
1011
+ var MAX_TOTAL_BYTES = 100 * 1024 * 1024;
1007
1012
  var MAX_FILES = 100;
1008
1013
  var MB = 1024 * 1024;
1009
1014
  var WHY_LEFT_OUT = {
@@ -1273,7 +1278,7 @@ var Tally = class {
1273
1278
  } else if (event.kind === "testReport") {
1274
1279
  this.testsRan = true;
1275
1280
  this.testsStopped = event.stopped === true;
1276
- this.testFailures += event.failed + event.errors;
1281
+ this.testFailures += event.failed + event.errors + (event.malformed?.length ?? 0);
1277
1282
  } else if (event.kind === "done") {
1278
1283
  if (event.exitCode !== void 0) this.exitCode = event.exitCode;
1279
1284
  if (event.stopped) this.stopReached = true;
@@ -2338,6 +2343,16 @@ var CliView = class {
2338
2343
  }
2339
2344
  }
2340
2345
  testReport(event) {
2346
+ for (const test of event.malformed ?? []) {
2347
+ if (test.finding) {
2348
+ const lines = serializedFindingLines(test.finding);
2349
+ this.problem(this.red(lines[0]));
2350
+ for (const line of lines.slice(1)) {
2351
+ this.problem(line);
2352
+ }
2353
+ this.problem("");
2354
+ }
2355
+ }
2341
2356
  const bad = event.failed + event.errors;
2342
2357
  const parts = [
2343
2358
  ...event.passed ? [`${event.passed} passed`] : [],
@@ -2566,7 +2581,7 @@ ${USAGE_HINT}
2566
2581
  return EXIT.ok;
2567
2582
  }
2568
2583
  if (args.version) {
2569
- process.stdout.write("0.5.0\n");
2584
+ process.stdout.write("0.6.0\n");
2570
2585
  return EXIT.ok;
2571
2586
  }
2572
2587
  if (args.error !== void 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pll-python",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Run Python with PLL's language levels from the command line: level checks, in-file tests, friendly errors and the built-in image and table libraries, all on Pyodide. No Python install needed.",
5
5
  "license": "MIT",
6
6
  "repository": {