python-constricter 0.3.6__py3-none-any.whl → 0.4.2__py3-none-any.whl

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 (108) hide show
  1. constricter/__init__.py +1 -1
  2. constricter/_version.py +4 -0
  3. constricter/cli/collecting.py +8 -1
  4. constricter/cli/command.py +22 -255
  5. constricter/cli/config.py +2 -1
  6. constricter/cli/lsp/__init__.py +2 -0
  7. constricter/cli/{hints.py → lsp/connection.py} +16 -362
  8. constricter/cli/{edits.py → lsp/edits.py} +1 -1
  9. constricter/cli/{guard.py → lsp/guard.py} +5 -1
  10. constricter/cli/lsp/hints.py +391 -0
  11. constricter/cli/lsp/remembered.py +81 -0
  12. constricter/cli/lsp/stored.py +200 -0
  13. constricter/cli/options.py +27 -6
  14. constricter/cli/paths.py +76 -7
  15. constricter/cli/reading.py +36 -0
  16. constricter/cli/rounds.py +328 -0
  17. constricter/cli/schedule.py +84 -36
  18. constricter/cli/workers.py +78 -8
  19. constricter/empty.py +66 -0
  20. constricter/fix/core/imports.py +66 -13
  21. constricter/fix/core/inherited.py +2 -1
  22. constricter/fix/core/known.py +83 -53
  23. constricter/fix/core/signatures.py +14 -7
  24. constricter/fix/index/beyond.py +3 -2
  25. constricter/fix/index/callers.py +3 -2
  26. constricter/fix/index/crossed.py +327 -0
  27. constricter/fix/index/decorated.py +29 -10
  28. constricter/fix/index/loose.py +11 -6
  29. constricter/fix/index/modules.py +90 -198
  30. constricter/fix/index/offers.py +123 -9
  31. constricter/fix/index/origins.py +238 -0
  32. constricter/fix/index/own_methods.py +31 -7
  33. constricter/fix/index/own_overloads.py +2 -2
  34. constricter/fix/index/own_types.py +183 -14
  35. constricter/fix/index/params.py +81 -0
  36. constricter/fix/index/plain.py +109 -13
  37. constricter/fix/index/project.py +86 -269
  38. constricter/fix/index/stubs/__init__.py +2 -0
  39. constricter/fix/index/{atoms.py → stubs/atoms.py} +3 -3
  40. constricter/fix/index/{chained.py → stubs/chained.py} +2 -2
  41. constricter/fix/index/{classnames.py → stubs/classnames.py} +2 -2
  42. constricter/fix/index/{declared.py → stubs/declared.py} +8 -6
  43. constricter/fix/index/{installed.py → stubs/installed.py} +84 -7
  44. constricter/fix/index/stubs/names.py +200 -0
  45. constricter/fix/index/{read.py → stubs/read.py} +2 -2
  46. constricter/fix/index/{stubbed.py → stubs/signatures.py} +28 -372
  47. constricter/fix/index/stubs/stubbed.py +208 -0
  48. constricter/fix/libraries/library.py +60 -14
  49. constricter/fix/libraries/overloads.py +315 -323
  50. constricter/fix/libraries/receivers.py +274 -0
  51. constricter/fix/libraries/stdlib.py +14 -2
  52. constricter/fix/tables/attributes.json +8 -8
  53. constricter/fix/tables/functions.json +15 -0
  54. constricter/fix/tables/method_overloads.json +91 -91
  55. constricter/fix/tables/method_signatures.json +60 -2
  56. constricter/fix/tables/overloads.json +5 -2
  57. constricter/fix/tables/platform.json +37 -0
  58. constricter/fix/tables/source.json +5 -3
  59. constricter/fix/tables/variables.json +3 -0
  60. constricter/fix/values/callables.py +4 -2
  61. constricter/fix/values/called.py +89 -1
  62. constricter/fix/values/classvars.py +100 -12
  63. constricter/fix/values/decided.py +39 -6
  64. constricter/fix/values/displays.py +19 -5
  65. constricter/fix/values/doubts.py +10 -6
  66. constricter/fix/values/entered.py +2 -1
  67. constricter/fix/values/fills.py +18 -7
  68. constricter/fix/values/guesses.py +11 -5
  69. constricter/fix/values/hinted.py +175 -9
  70. constricter/fix/values/inference.py +21 -319
  71. constricter/fix/values/loops.py +335 -0
  72. constricter/fix/values/members.py +9 -6
  73. constricter/fix/values/operated.py +10 -8
  74. constricter/fix/values/returned.py +70 -558
  75. constricter/fix/values/shapes.py +85 -10
  76. constricter/fix/values/stored.py +564 -0
  77. constricter/fix/values/targets.py +49 -13
  78. constricter/notebook.py +6 -8
  79. constricter/offences.py +49 -4
  80. constricter/overrides.py +24 -0
  81. constricter/plugins/pylint.py +20 -17
  82. constricter/recording.py +93 -16
  83. constricter/rules/annotations.py +10 -688
  84. constricter/rules/binding.py +11 -6
  85. constricter/rules/calls.py +3 -2
  86. constricter/rules/checker.py +71 -485
  87. constricter/rules/declarations.py +730 -0
  88. constricter/rules/decorators.py +158 -28
  89. constricter/rules/keys.py +1 -1
  90. constricter/rules/late.py +319 -20
  91. constricter/rules/parsed.py +9 -3
  92. constricter/rules/rebinding.py +5 -3
  93. constricter/rules/recorded.py +13 -12
  94. constricter/rules/scope.py +31 -224
  95. constricter/rules/scoped.py +232 -0
  96. constricter/rules/scoping.py +346 -0
  97. constricter/rules/settings.py +189 -0
  98. constricter/rules/tables.py +1 -1
  99. constricter/rules/widened.py +1 -1
  100. constricter/trace.py +5 -9
  101. {python_constricter-0.3.6.dist-info → python_constricter-0.4.2.dist-info}/METADATA +12 -6
  102. python_constricter-0.4.2.dist-info/RECORD +162 -0
  103. {python_constricter-0.3.6.dist-info → python_constricter-0.4.2.dist-info}/licenses/LICENSE.md +1 -1
  104. constricter/cli/remembered.py +0 -52
  105. python_constricter-0.3.6.dist-info/RECORD +0 -140
  106. /constricter/cli/{protocol.py → lsp/protocol.py} +0 -0
  107. {python_constricter-0.3.6.dist-info → python_constricter-0.4.2.dist-info}/WHEEL +0 -0
  108. {python_constricter-0.3.6.dist-info → python_constricter-0.4.2.dist-info}/entry_points.txt +0 -0
constricter/__init__.py CHANGED
@@ -1,6 +1,7 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  """Every local variable typed where it's first bound."""
3
3
 
4
+ from constricter._version import __version__ # written by the build, from the git tag
4
5
  from constricter.offences import (
5
6
  COMMENT_TYPED_TARGET,
6
7
  DEFAULT_CHECKS,
@@ -25,7 +26,6 @@ from constricter.offences import (
25
26
  )
26
27
  from constricter.rules.checker import Coverage, annotation_coverage, check_source, check_tree
27
28
 
28
- __version__ = "0.3.6"
29
29
  __all__ = [
30
30
  "COMMENT_TYPED_TARGET",
31
31
  "DEFAULT_CHECKS",
@@ -0,0 +1,4 @@
1
+ # SPDX-License-Identifier: MIT
2
+ """The version this was built as, from its git tag (written by the build, not tracked)."""
3
+
4
+ __version__ = "0.4.2"
@@ -6,15 +6,21 @@ cyclic. Left to itself, the collector looked through the kept trees again and ag
6
6
  allocated: a third of the standard library's check (34s, 21s without). So while the command runs,
7
7
  it's off: what indexing built is frozen (never looked through again), and after each file, only
8
8
  what's been allocated since (the young generations) is collected, which bounds what a cycle can hold.
9
+ Not on a free-threaded build, whose collector has no generations: each collection there looks through
10
+ everything, and one after every file made a check several times as long. Its cycles wait for the run's
11
+ end (a check of pandas held 2.9 GB so, to 2.7 GB).
9
12
  """
10
13
 
11
14
  import contextlib
12
15
  import gc
16
+ import sysconfig
13
17
  from collections.abc import Generator
14
18
  from typing import Final
15
19
 
16
20
  _YOUNG: Final = 1 # the generations collected after each file: 0 and 1, never the frozen or the old
17
21
  _FREEZES: Final = hasattr(gc, "freeze") # PyPy's collector has no freezing
22
+ # Whether the collector has generations: a free-threaded build's has none, its GIL on or off.
23
+ _YOUNGER: Final = not sysconfig.get_config_var("Py_GIL_DISABLED")
18
24
 
19
25
 
20
26
  @contextlib.contextmanager
@@ -44,4 +50,5 @@ def indexed() -> None:
44
50
 
45
51
  def sweep() -> None:
46
52
  """Collect what the file just checked left behind (see the module docstring)."""
47
- _ = gc.collect(_YOUNG)
53
+ if _YOUNGER:
54
+ _ = gc.collect(_YOUNG)
@@ -5,72 +5,43 @@ import codecs
5
5
  import contextlib
6
6
  import difflib
7
7
  import importlib
8
- import io
9
8
  import json
10
9
  import sys
11
- import tokenize
12
- from collections.abc import Callable, Mapping, Sequence
10
+ from collections.abc import Callable, Sequence
13
11
  from dataclasses import replace
14
12
  from functools import partial
15
13
  from pathlib import Path
16
14
  from typing import TYPE_CHECKING, Final, NamedTuple, TextIO, TypeAlias, cast
17
15
 
18
16
  from constricter import notebook
19
- from constricter.cli import baseline, collecting, schedule, traced
17
+ from constricter.cli import baseline, collecting, schedule
18
+ from constricter.cli.lsp.protocol import HintError
20
19
  from constricter.cli.options import Mode, Options, Output
21
20
  from constricter.cli.paths import STDIN, python_files
22
- from constricter.cli.protocol import HintError
21
+ from constricter.cli.reading import encoding_of, read_text
23
22
  from constricter.cli.report import Format, Result, fix_reasons, render, statistics
24
- from constricter.cli.runs import Added, BaselineRun, CheckRun, CoverageRun, FileRun, merged
25
- from constricter.cli.workers import Workers, kept, started
23
+ from constricter.cli.rounds import checked_all, settled
24
+ from constricter.cli.runs import Added, BaselineRun, CheckRun, CoverageRun, FileRun
25
+ from constricter.cli.workers import kept
26
26
  from constricter.fix.core import fixes
27
- from constricter.fix.core.known import Callee, Hints, Outside, Returns
28
- from constricter.fix.index import callers, decorated, installed, loose, plain, project
27
+ from constricter.fix.core.known import Outside, Returns
29
28
  from constricter.noqa import lines, unsuppressed
30
29
  from constricter.offences import (
31
30
  DEFAULT_CHECKS,
32
31
  Checks,
33
32
  Edit,
34
- Fix,
35
33
  Offence,
36
34
  )
37
35
  from constricter.rules import parsed
38
36
  from constricter.rules.checker import Checked, Coverage, annotation_coverage, checked_source
39
37
 
40
38
  if TYPE_CHECKING: # slow to import, and only needed for `--infer-with`
41
- from constricter.cli import hints
39
+ from constricter.cli.lsp import hints
42
40
 
43
41
  EXIT_CLEAN: Final = 0
44
42
  EXIT_FOUND: Final = 1
45
43
  EXIT_ERROR: Final = 2
46
44
  _ALL: Final = 100 # percent
47
- _PY: Final = ".py" # a module the index names (not a notebook)
48
-
49
-
50
- def _encoding(path: Path, data: bytes) -> str:
51
- """Find the encoding of `path`, whose bytes are `data`: a notebook's is UTF-8 (it's JSON).
52
-
53
- Returns:
54
- A module's, as Python finds it: a PEP 263 declaration (`# -*- coding: latin-1 -*-`), a BOM
55
- (`utf-8-sig`), or UTF-8. Raises `SyntaxError` for an unknown or contradictory one.
56
-
57
- """
58
- return (
59
- "utf-8" if path.suffix == notebook.SUFFIX else tokenize.detect_encoding(io.BytesIO(data).readline)[0]
60
- )
61
-
62
-
63
- def _read(path: Path) -> str:
64
- """Read `path`, in its encoding; `-` is standard input.
65
-
66
- Returns:
67
- Its text.
68
-
69
- """
70
- if path == STDIN:
71
- return sys.stdin.read()
72
- data: bytes = path.read_bytes()
73
- return data.decode(_encoding(path, data))
74
45
 
75
46
 
76
47
  def _source(raw: str, name: Path) -> tuple[str, list[notebook.Line]]:
@@ -128,7 +99,6 @@ def _placed(offence: Offence, where: list[notebook.Line]) -> Offence:
128
99
 
129
100
 
130
101
  # One changed part of a fixed file (the file, or a notebook's cell): its label, old and new lines.
131
- _Hinted: TypeAlias = dict[Path, tuple[Hints, ...]] # each file's hints, a checker's each
132
102
  _Change: TypeAlias = tuple[str, list[str], list[str]]
133
103
  # One JSON value a coverage report holds, and one file's row of them.
134
104
  _Scalar: TypeAlias = str | int | float
@@ -180,17 +150,11 @@ def fix_file(path: Path, offences: Sequence[Offence]) -> int:
180
150
  if not (count := sum(1 for o in offences if o.edit is not None)):
181
151
  return 0
182
152
  data: bytes = path.read_bytes()
183
- encoding: str = _encoding(path, data)
153
+ encoding: str = encoding_of(path, data)
184
154
  _ = path.write_bytes(_fixed(data.decode(encoding), path, offences).text.encode(encoding))
185
155
  return count
186
156
 
187
157
 
188
- HINT_ROUNDS: Final = 4 # with `--fix`: how many times a file is fixed, at most (see `_settled`)
189
- # The fix kinds a scope's check comes to late, once the scope is read: what was bound to such a
190
- # name's value before had no type yet, and has one once the fix is written.
191
- _LATE_KINDS: Final = frozenset({"optional", "filled"})
192
-
193
-
194
158
  def _shown_lines(raw: str, name: Path) -> dict[tuple[int | None, int], str]:
195
159
  """Map each line of `raw` (a notebook's, in its cells) to its text, as offences are placed.
196
160
 
@@ -214,7 +178,7 @@ def _checked(path: Path, name: Path, checks: Checks, outside: Outside) -> tuple[
214
178
  Its text, and what checking it found.
215
179
 
216
180
  """
217
- raw: str = _read(path)
181
+ raw: str = read_text(path)
218
182
  return raw, checked_text(raw, name, checks, outside=outside)
219
183
 
220
184
 
@@ -360,7 +324,7 @@ def _cover_path(path: Path, _outside: Outside, options: Options) -> CoverageRun:
360
324
  """
361
325
  name: Path = options.input.name(path)
362
326
  try:
363
- return CoverageRun(annotation_coverage(_source(_read(path), name)[0], options.checks))
327
+ return CoverageRun(annotation_coverage(_source(read_text(path), name)[0], options.checks))
364
328
  except (OSError, ValueError, SyntaxError) as error:
365
329
  return CoverageRun(error=f"{name}: error: {error}")
366
330
 
@@ -372,6 +336,7 @@ def _check_all(options: Options) -> tuple[list[Path], list[FileRun]]:
372
336
  The names, and what each file found.
373
337
 
374
338
  """
339
+ schedule.forget()
375
340
  with kept():
376
341
  return _check_rounds(options)
377
342
 
@@ -381,14 +346,16 @@ def _check_rounds(options: Options) -> tuple[list[Path], list[FileRun]]:
381
346
 
382
347
  With `--infer-with`, the type checker's server runs throughout: it's asked for every file's
383
348
  hints first, and with `--fix`, each file a round changed is asked again and checked again, as its
384
- new annotations change what the checker infers (see `_settled`). A baseline records offences,
349
+ new annotations change what the checker infers (see `settled`). A baseline records offences,
385
350
  and coverage counts annotations: hints change neither, only what `--fix` offers.
386
351
 
387
352
  Returns:
388
353
  The names, and what each file found.
389
354
 
390
355
  """
391
- paths: list[Path] = list(python_files(options.input.paths, options.input.exclude))
356
+ paths: list[Path] = list(
357
+ python_files(options.input.paths, options.input.exclude, tracked=options.input.gitignore),
358
+ )
392
359
  check: Callable[[Path, Outside], FileRun]
393
360
  if options.mode is Mode.COVERAGE:
394
361
  check = partial(_cover_path, options=options)
@@ -398,18 +365,19 @@ def _check_rounds(options: Options) -> tuple[list[Path], list[FileRun]]:
398
365
  check = partial(_check_path, options=options)
399
366
  names: list[Path] = [options.input.name(path) for path in paths]
400
367
  if options.mode in {Mode.COVERAGE, Mode.WRITE_BASELINE}:
401
- return names, _checked_all(paths, check, options)[0]
368
+ return names, checked_all(paths, check, options)[0]
402
369
  if not options.infer_with:
403
- return names, _settled(paths, check, options, None)
370
+ return names, settled(paths, check, options, None)
404
371
  session: hints.Session
405
372
  # Imported only for `--infer-with`: it's slow to import, for every run's start.
406
- with cast("type[hints.Session]", importlib.import_module("constricter.cli.hints").Session)(
373
+ with cast("type[hints.Session]", importlib.import_module("constricter.cli.lsp.hints").Session)(
407
374
  options.infer_with,
408
375
  Path.cwd(),
409
376
  options.jobs,
410
377
  options.infer_memory,
378
+ kept=not options.infer_fresh,
411
379
  ) as session:
412
- runs: list[FileRun] = _settled(paths, check, options, session)
380
+ runs: list[FileRun] = settled(paths, check, options, session)
413
381
  checker: str
414
382
  path: Path
415
383
  for checker, path in session.abandoned:
@@ -418,207 +386,6 @@ def _check_rounds(options: Options) -> tuple[list[Path], list[FileRun]]:
418
386
  return names, runs
419
387
 
420
388
 
421
- def _settled(
422
- paths: Sequence[Path],
423
- check: Callable[[Path, Outside], FileRun],
424
- options: Options,
425
- session: "hints.Session | None",
426
- ) -> list[FileRun]:
427
- """Check every file, then again each one `--fix` changed in a way a second check would see.
428
-
429
- Until a round changes nothing, `HINT_ROUNDS` at most: a second pass is then left nothing.
430
-
431
- Returns:
432
- What each file found, a file's rounds joined.
433
-
434
- """
435
- runs: list[FileRun]
436
- modules: project.Index
437
- runs, modules = _called_again(
438
- paths,
439
- _checked_all(paths, check, options, session),
440
- check,
441
- options,
442
- session,
443
- )
444
- again: list[int] = [
445
- index
446
- for index, run in enumerate(runs)
447
- if options.mode is Mode.FIX and paths[index] != STDIN and _unsettled(run, session)
448
- ]
449
- for _ in range(HINT_ROUNDS - 1):
450
- if not again:
451
- break
452
- redone: list[FileRun] = _checked_all([paths[i] for i in again], check, options, session, modules)[0]
453
- index: int
454
- run: FileRun
455
- for index, run in zip(again, redone, strict=True):
456
- runs[index] = merged(runs[index], run)
457
- again = [index for index, run in zip(again, redone, strict=True) if _unsettled(run, session)]
458
- return runs
459
-
460
-
461
- def _unsettled(run: FileRun, session: "hints.Session | None") -> bool:
462
- """Check whether a round of `--fix` changed a file in a way that changes what's inferred there.
463
-
464
- Returns:
465
- Whether it fixed anything, with a type checker's hints: its new annotations change them.
466
- Without, whether it made a fix the scope's check comes to late (`_LATE_KINDS`): what's bound
467
- to such a name's value was read before its type was known.
468
-
469
- """
470
- checked: CheckRun = cast("CheckRun", run)
471
- return bool(checked.fixed) and (
472
- session is not None
473
- or any(
474
- isinstance(made.offence.edit, Fix) and made.offence.edit.kinds & _LATE_KINDS
475
- for made in checked.made
476
- )
477
- )
478
-
479
-
480
- def _called_again(
481
- paths: Sequence[Path],
482
- first: tuple[list[FileRun], project.Index],
483
- check: Callable[[Path, Outside], FileRun],
484
- options: Options,
485
- session: "hints.Session | None" = None,
486
- ) -> tuple[list[FileRun], project.Index]:
487
- """Check again the files whose functions' unannotated parameters every call passes one type.
488
-
489
- Knowing those types (see `callers`), as guesses; a file among several of one module name isn't
490
- (which its callers call can't be told). Then the other files calling those functions, knowing
491
- what they now return. With `--fix` a file's rounds are joined; otherwise the last, knowing more,
492
- stands.
493
-
494
- Returns:
495
- What each file found, and the index, with the parameters' types.
496
-
497
- """
498
- runs: list[FileRun]
499
- modules: project.Index
500
- runs, modules = first
501
- found: dict[str, callers.Parameters] = callers.parameters(
502
- (run.calls for run in runs if isinstance(run, CheckRun)),
503
- modules,
504
- )
505
- names: list[str | None] = [project.module_name(path) if path.suffix == _PY else None for path in paths]
506
- again: list[int]
507
- if not (again := [at for at, name in enumerate(names) if name in found and names.count(name) == 1]):
508
- return runs, modules
509
- calling: list[int] = _calling(runs, found, again)
510
- before: project.Index = callers.with_parameters(modules, found)
511
- modules = _checked_more(paths, runs, again, (check, options, session), before)
512
- # A caller is checked again only if a function it imports now returns something else.
513
- calling = [
514
- at
515
- for at in calling
516
- if loose.returned(modules, paths[at], {}) != loose.returned(before, paths[at], {})
517
- ]
518
- return runs, _checked_more(paths, runs, calling, (check, options, session), modules)
519
-
520
-
521
- def _checked_more(
522
- paths: Sequence[Path],
523
- runs: list[FileRun],
524
- which: Sequence[int],
525
- how: "tuple[Callable[[Path, Outside], FileRun], Options, hints.Session | None]",
526
- modules: project.Index,
527
- ) -> project.Index:
528
- """Check the files of `paths` at `which` again, knowing `modules`, into `runs`.
529
-
530
- `how`: the check, the options and the type checkers' session. With `--fix` a file's rounds are
531
- joined; otherwise the later one stands.
532
-
533
- Returns:
534
- The index, with what their functions now return.
535
-
536
- """
537
- redone: list[FileRun]
538
- redone, modules = _checked_all([paths[at] for at in which], how[0], how[1], how[2], modules)
539
- at: int
540
- run: FileRun
541
- for at, run in zip(which, redone, strict=True):
542
- runs[at] = merged(runs[at], run) if how[1].mode is Mode.FIX else run
543
- return modules
544
-
545
-
546
- def _calling(
547
- runs: Sequence[FileRun],
548
- found: Mapping[str, callers.Parameters],
549
- again: Sequence[int],
550
- ) -> list[int]:
551
- """Find the files (not among `again`) calling a function whose parameters `found` types.
552
-
553
- Returns:
554
- Their indexes.
555
-
556
- """
557
- typed: set[Callee] = {(module, function) for module, functions in found.items() for function in functions}
558
- return [
559
- at
560
- for at, run in enumerate(runs)
561
- if at not in again and isinstance(run, CheckRun) and typed & run.calls.calls.keys()
562
- ]
563
-
564
-
565
- def _checked_all(
566
- paths: Sequence[Path],
567
- check: Callable[[Path, Outside], FileRun],
568
- options: Options,
569
- session: "hints.Session | None" = None,
570
- modules: project.Index | None = None,
571
- ) -> tuple[list[FileRun], project.Index]:
572
- """Check `paths` (`--jobs` at a time), with the `session`'s hints, and `modules` (else indexed).
573
-
574
- Returns:
575
- What each file found, and the index of every file's module.
576
-
577
- """
578
- # Before a worker starts: one forked while the servers' threads run could wait on a lock one holds.
579
- hinted: _Hinted = {} if session is None else session.hints(_texts(paths))
580
- if options.input.trace is not None:
581
- hinted = traced.merged(hinted, traced.load(options.input.trace), paths)
582
- coverage: bool = options.mode is Mode.COVERAGE # needs nothing from the other files
583
- pool: Workers | None = started(paths, options.jobs) if options.jobs != 1 and len(paths) > 1 else None
584
- if modules is None and pool:
585
- modules = project.Index({}, []) if coverage else pool.index()
586
- elif modules is None:
587
- modules = project.Index({}, []) if coverage else project.index(paths)
588
- collecting.indexed()
589
- if not coverage:
590
- modules = plain.settled(
591
- decorated.passed(installed.with_installed(modules, installed.search_path())),
592
- options.checks.plain_bases,
593
- )
594
- return schedule.checked(
595
- paths,
596
- check,
597
- pool,
598
- (modules, hinted),
599
- merged if options.mode is Mode.FIX else None,
600
- )
601
-
602
-
603
- def _texts(paths: Sequence[Path]) -> dict[Path, str]:
604
- """Read each file to ask the type checker about (not a notebook, or standard input).
605
-
606
- A file that can't be read is left out: checking it reports that.
607
-
608
- Returns:
609
- Each file's text.
610
-
611
- """
612
- texts: dict[Path, str] = {}
613
- path: Path
614
- for path in paths:
615
- if path != STDIN and path.suffix != notebook.SUFFIX:
616
- # UnicodeDecodeError is a ValueError; a bad encoding declaration, a SyntaxError.
617
- with contextlib.suppress(OSError, ValueError, SyntaxError):
618
- texts[path] = _read(path)
619
- return texts
620
-
621
-
622
389
  def _report(options: Options, runs: Sequence[FileRun], files: int) -> int:
623
390
  """Print the results (`runs` is check/fix/diff mode's: every other mode has its own printing).
624
391
 
constricter/cli/config.py CHANGED
@@ -8,7 +8,7 @@ from functools import partial
8
8
  from pathlib import Path
9
9
  from typing import TYPE_CHECKING, Final, TypeAlias, cast
10
10
 
11
- from constricter.cli.protocol import SERVERS
11
+ from constricter.cli.lsp.protocol import SERVERS
12
12
  from constricter.jsonc import is_int
13
13
  from constricter.offences import FIX_KINDS, LEVELS, MESSAGES, WIDEN_KINDS
14
14
 
@@ -276,6 +276,7 @@ _READERS: dict[str, Callable[[_Toml], Default | None]] = {
276
276
  "fix-plain-bases": _strings,
277
277
  "type-comments": _flag,
278
278
  "all-scopes": _flag,
279
+ "gitignore": _flag,
279
280
  "per-path-levels": _levels,
280
281
  "per-file-ignores": partial(_lists, read=_codes),
281
282
  "narrower": partial(_lists, read=_strings), # a type hierarchy: each type, and those it's narrower than
@@ -0,0 +1,2 @@
1
+ # SPDX-License-Identifier: MIT
2
+ """constricter.cli.lsp."""