paperlint 14.0.0 → 14.1.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.
@@ -0,0 +1,448 @@
1
+ {
2
+ "study": "demo-witness",
3
+ "preset": "linter-config-witness",
4
+ "kind": "labels",
5
+ "title": "Witness cards",
6
+ "intro": "Each card shows one requirement, one piece of real code and one question about that code. The linter settings behind the card are under «Settings behind this card»; you do not need them to answer.",
7
+ "seed": 11,
8
+ "groups": {
9
+ "all": "Witness cards"
10
+ },
11
+ "questions": [
12
+ {
13
+ "id": "witness",
14
+ "text": "Does the requirement forbid this line, or apply to this file?",
15
+ "short": "answer",
16
+ "options": [
17
+ {
18
+ "value": "covers",
19
+ "label": "Yes",
20
+ "icon": "✅",
21
+ "example": "«Use `===`» and the line `a == b`: yes, the requirement forbids it"
22
+ },
23
+ {
24
+ "value": "excludes",
25
+ "label": "No",
26
+ "icon": "🚫",
27
+ "example": "«No `console` in shipped code» and a test file: no, tests are not shipped"
28
+ },
29
+ {
30
+ "value": "open",
31
+ "label": "Unclear: the wording could be read either way",
32
+ "icon": "↔️",
33
+ "example": "«Use `===`» and the line `x == null`: unclear, a common exception the wording neither allows nor forbids"
34
+ },
35
+ {
36
+ "value": "unsure",
37
+ "label": "I can't judge (tell me why)",
38
+ "icon": "❔",
39
+ "example": "the code is in a language you cannot read: say so in the box",
40
+ "ask_why": "Why can't you judge it?"
41
+ }
42
+ ]
43
+ }
44
+ ],
45
+ "practice": [
46
+ {
47
+ "body": "Functions take at most 3 parameters",
48
+ "situation": {
49
+ "summary": "One setting allows up to 5 parameters, the other up to 3.",
50
+ "question": "Does «Functions take at most 3 parameters» forbid {line}?"
51
+ },
52
+ "example": {
53
+ "file": "src/orders/create.ts",
54
+ "lines": [
55
+ {
56
+ "n": 11,
57
+ "text": "",
58
+ "mark": false
59
+ },
60
+ {
61
+ "n": 12,
62
+ "text": "export function createOrder(id, items, user, coupon) {",
63
+ "mark": true
64
+ },
65
+ {
66
+ "n": 13,
67
+ "text": " return { id, items, user, coupon };",
68
+ "mark": false
69
+ }
70
+ ],
71
+ "message": "Function 'createOrder' has too many parameters (4). Maximum allowed is 3.",
72
+ "caught_by": "a"
73
+ },
74
+ "details": {
75
+ "a": [
76
+ {
77
+ "key": "option:max",
78
+ "raw": "3",
79
+ "plain": "at most 3 parameters",
80
+ "matches_example": true
81
+ }
82
+ ],
83
+ "b": [
84
+ {
85
+ "key": "option:max",
86
+ "raw": "5",
87
+ "plain": "at most 5 parameters",
88
+ "matches_example": false
89
+ }
90
+ ]
91
+ },
92
+ "rules": [
93
+ {
94
+ "linter": "eslint",
95
+ "id": "max-params",
96
+ "explanation": {
97
+ "source": "eslint docs",
98
+ "what": "Enforce a maximum number of parameters in function definitions.",
99
+ "example_bad": "function foo (bar, baz, qux, qxx) {}",
100
+ "example_good": "function foo (bar, baz, qux) {}",
101
+ "url": "https://eslint.org/docs/latest/rules/max-params"
102
+ }
103
+ }
104
+ ],
105
+ "expected": {
106
+ "witness": "covers"
107
+ },
108
+ "why": "The words fix the number 3, and the marked line has 4 parameters."
109
+ },
110
+ {
111
+ "body": "Use `===`, never `==`",
112
+ "situation": {
113
+ "summary": "One setting allows `== null`, the other forbids every `==`.",
114
+ "question": "Does «Use `===`, never `==`» forbid {line}?"
115
+ },
116
+ "example": {
117
+ "file": "src/cache/lookup.ts",
118
+ "lines": [
119
+ {
120
+ "n": 41,
121
+ "text": " const entry = store.get(key);",
122
+ "mark": false
123
+ },
124
+ {
125
+ "n": 42,
126
+ "text": " if (entry == null) return undefined;",
127
+ "mark": true
128
+ }
129
+ ],
130
+ "message": "Expected '===' and instead saw '=='.",
131
+ "caught_by": "b"
132
+ },
133
+ "details": {
134
+ "a": [
135
+ {
136
+ "key": "option:null",
137
+ "raw": "\"ignore\"",
138
+ "plain": "allows `== null` and `!= null`",
139
+ "matches_example": false
140
+ }
141
+ ],
142
+ "b": [
143
+ {
144
+ "key": "rule",
145
+ "raw": "\"always\"",
146
+ "plain": "every `==` and `!=`",
147
+ "matches_example": true
148
+ }
149
+ ]
150
+ },
151
+ "rules": [
152
+ {
153
+ "linter": "eslint",
154
+ "id": "eqeqeq",
155
+ "explanation": {
156
+ "source": "none"
157
+ }
158
+ }
159
+ ],
160
+ "expected": {
161
+ "witness": "covers"
162
+ },
163
+ "why": "«never `==`» names no exception, so `== null` is forbidden too."
164
+ },
165
+ {
166
+ "body": "No `console` calls in shipped code",
167
+ "situation": {
168
+ "summary": "One setting checks test files, the other leaves them out.",
169
+ "question": "Should this requirement apply to test files?"
170
+ },
171
+ "example": {
172
+ "file": "test/report.test.ts",
173
+ "lines": [
174
+ {
175
+ "n": 7,
176
+ "text": " console.log(result);",
177
+ "mark": true
178
+ }
179
+ ],
180
+ "message": "Unexpected console statement.",
181
+ "caught_by": "a"
182
+ },
183
+ "details": {
184
+ "a": [
185
+ {
186
+ "key": "files",
187
+ "raw": "**/*.ts",
188
+ "plain": "every TypeScript file",
189
+ "matches_example": true
190
+ }
191
+ ],
192
+ "b": [
193
+ {
194
+ "key": "files",
195
+ "raw": "**/*.ts",
196
+ "plain": "every TypeScript file",
197
+ "matches_example": true
198
+ },
199
+ {
200
+ "key": "ignores",
201
+ "raw": "test/**",
202
+ "plain": "except the files under test/",
203
+ "matches_example": true
204
+ }
205
+ ]
206
+ },
207
+ "rules": [
208
+ {
209
+ "linter": "eslint",
210
+ "id": "no-console",
211
+ "explanation": {
212
+ "source": "none"
213
+ }
214
+ }
215
+ ],
216
+ "expected": {
217
+ "witness": "excludes"
218
+ },
219
+ "why": "Test files are not shipped, so the words do not reach them."
220
+ }
221
+ ],
222
+ "items": [
223
+ {
224
+ "source_id": "req-logging:tests",
225
+ "group": "all",
226
+ "body": "Never log with `print`; use the `logging` module",
227
+ "situation": {
228
+ "summary": "One setting checks the scripts/ folder, the other leaves it out.",
229
+ "question": "Should this requirement apply to the scripts/ folder?"
230
+ },
231
+ "example": {
232
+ "file": "scripts/seed_db.py",
233
+ "url": "https://example.com/demo-shop/blob/0000000/scripts/seed_db.py#L18",
234
+ "lines": [
235
+ {
236
+ "n": 16,
237
+ "text": "for row in rows:",
238
+ "mark": false
239
+ },
240
+ {
241
+ "n": 17,
242
+ "text": " db.insert(row)",
243
+ "mark": false
244
+ },
245
+ {
246
+ "n": 18,
247
+ "text": "print(f\"seeded {len(rows)} rows\")",
248
+ "mark": true
249
+ }
250
+ ],
251
+ "message": "`print` found",
252
+ "caught_by": "b"
253
+ },
254
+ "more_like_this": {
255
+ "count": 3,
256
+ "files": [
257
+ "scripts/migrate.py",
258
+ "scripts/backfill_prices.py"
259
+ ]
260
+ },
261
+ "details": {
262
+ "a": [
263
+ {
264
+ "key": "rule",
265
+ "raw": "T201",
266
+ "plain": "flags every call to `print`",
267
+ "matches_example": null
268
+ },
269
+ {
270
+ "key": "ignores",
271
+ "raw": "scripts/**",
272
+ "plain": "except the files under scripts/",
273
+ "matches_example": true
274
+ }
275
+ ],
276
+ "b": [
277
+ {
278
+ "key": "rule",
279
+ "raw": "T201",
280
+ "plain": "flags every call to `print`",
281
+ "matches_example": null
282
+ }
283
+ ]
284
+ },
285
+ "rules": [
286
+ {
287
+ "linter": "ruff",
288
+ "id": "T201",
289
+ "explanation": {
290
+ "source": "ruff rule",
291
+ "what": "Checks for `print` statements.",
292
+ "why": "`print` statements used for debugging should be omitted from production code. They can lead to the accidental inclusion of sensitive information in logs, and are not configurable by clients, unlike `logging` statements.",
293
+ "example_bad": "def sum_less_than_four(a, b):\n print(f\"Calling with {a} and {b}\")\n return a + b < 4",
294
+ "example_good": "import logging\n\nlogging.basicConfig(level=logging.INFO)\nlogger = logging.getLogger(__name__)\n\n\ndef sum_less_than_four(a, b):\n logger.debug(\"Calling with %s and %s\", a, b)\n return a + b < 4",
295
+ "url": "https://docs.astral.sh/ruff/rules/print/"
296
+ }
297
+ }
298
+ ],
299
+ "repeat": {
300
+ "group": "req-logging",
301
+ "index": 1,
302
+ "of": 2
303
+ }
304
+ },
305
+ {
306
+ "source_id": "req-logging:debug-flag",
307
+ "group": "all",
308
+ "body": "Never log with `print`; use the `logging` module",
309
+ "situation": {
310
+ "summary": "One setting allows `print` behind a debug flag, the other forbids every `print`.",
311
+ "question": "Does «Never log with `print`; use the `logging` module» forbid {line}?"
312
+ },
313
+ "example": {
314
+ "file": "shop/cart.py",
315
+ "lines": [
316
+ {
317
+ "n": 30,
318
+ "text": " if DEBUG:",
319
+ "mark": false
320
+ },
321
+ {
322
+ "n": 31,
323
+ "text": " print(\"cart\", cart.id, cart.total)",
324
+ "mark": true
325
+ }
326
+ ],
327
+ "message": "`print` found",
328
+ "caught_by": "a"
329
+ },
330
+ "details": {
331
+ "a": [
332
+ {
333
+ "key": "rule",
334
+ "raw": "T201",
335
+ "plain": "flags every call to `print`",
336
+ "matches_example": null
337
+ }
338
+ ],
339
+ "b": [
340
+ {
341
+ "key": "rule",
342
+ "raw": "T201",
343
+ "plain": "flags every call to `print`",
344
+ "matches_example": null
345
+ },
346
+ {
347
+ "key": "option:allow-debug",
348
+ "raw": "true",
349
+ "plain": "allows `print` inside `if DEBUG:`",
350
+ "matches_example": true
351
+ }
352
+ ]
353
+ },
354
+ "rules": [
355
+ {
356
+ "linter": "ruff",
357
+ "id": "T201",
358
+ "explanation": {
359
+ "source": "none"
360
+ }
361
+ }
362
+ ],
363
+ "repeat": {
364
+ "group": "req-logging",
365
+ "index": 2,
366
+ "of": 2
367
+ }
368
+ },
369
+ {
370
+ "source_id": "req-imports:barrel",
371
+ "group": "all",
372
+ "body": "Import from a package's public entry point, never from its internals",
373
+ "situation": {
374
+ "summary": "One setting forbids any path below a package's folder, the other only paths into src/internal/.",
375
+ "question": "Does «Import from a package's public entry point, never from its internals» forbid {line}?"
376
+ },
377
+ "example": {
378
+ "file": "apps/web/src/checkout/total.ts",
379
+ "lines": [
380
+ {
381
+ "n": 1,
382
+ "text": "import { money } from \"@demo/format\";",
383
+ "mark": false
384
+ },
385
+ {
386
+ "n": 2,
387
+ "text": "import { round } from \"@demo/format/src/round\";",
388
+ "mark": true
389
+ },
390
+ {
391
+ "n": 3,
392
+ "text": "",
393
+ "mark": false
394
+ }
395
+ ],
396
+ "message": "'@demo/format/src/round' import is restricted from being used by a pattern. Import from the package's entry point.",
397
+ "caught_by": "a"
398
+ },
399
+ "more_like_this": {
400
+ "count": 1,
401
+ "files": [
402
+ "apps/admin/src/report/sum.ts"
403
+ ]
404
+ },
405
+ "details": {
406
+ "a": [
407
+ {
408
+ "key": "option:patterns",
409
+ "raw": "[{\"group\": [\"@demo/*/*\"], \"message\": \"Import from the package's entry point.\"}]",
410
+ "plain": "forbids any import path one or more folders below a `@demo/` package",
411
+ "matches_example": true
412
+ },
413
+ {
414
+ "key": "files",
415
+ "raw": "apps/**/*.ts",
416
+ "plain": "the TypeScript files under apps/",
417
+ "matches_example": true
418
+ }
419
+ ],
420
+ "b": [
421
+ {
422
+ "key": "option:patterns",
423
+ "raw": "[{\"group\": [\"@demo/*/src/internal/**\"]}]",
424
+ "plain": "forbids only import paths into a package's src/internal/ folder",
425
+ "matches_example": false
426
+ },
427
+ {
428
+ "key": "files",
429
+ "raw": "apps/**/*.ts",
430
+ "plain": "the TypeScript files under apps/",
431
+ "matches_example": true
432
+ }
433
+ ]
434
+ },
435
+ "rules": [
436
+ {
437
+ "linter": "eslint",
438
+ "id": "no-restricted-imports",
439
+ "explanation": {
440
+ "source": "config message",
441
+ "what": "Import from the package's entry point.",
442
+ "url": "https://eslint.org/docs/latest/rules/no-restricted-imports"
443
+ }
444
+ }
445
+ ]
446
+ }
447
+ ]
448
+ }