python-checks 0.1.1__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.
Files changed (101) hide show
  1. python_checks-0.1.1/README.md → python_checks-0.2.0/PKG-INFO +27 -0
  2. python_checks-0.1.1/PKG-INFO → python_checks-0.2.0/README.md +11 -16
  3. {python_checks-0.1.1 → python_checks-0.2.0}/pyproject.toml +1 -1
  4. {python_checks-0.1.1 → python_checks-0.2.0}/pyproject.toml.orig +1 -1
  5. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/core/__init__.py +11 -11
  6. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/core/_markers.py +74 -36
  7. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/environment/_render.py +4 -1
  8. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/environment/_settings.py +5 -0
  9. {python_checks-0.1.1 → python_checks-0.2.0}/LICENSE +0 -0
  10. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/__init__.py +0 -0
  11. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/__init__.py +0 -0
  12. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/_kind.py +0 -0
  13. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/_location.py +0 -0
  14. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/_names.py +0 -0
  15. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/api/__init__.py +0 -0
  16. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/api/_endpoint_declarations.py +0 -0
  17. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/api/_marker.py +0 -0
  18. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/calls/__init__.py +0 -0
  19. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/calls/_confined_functions.py +0 -0
  20. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/calls/_marker.py +0 -0
  21. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/database/__init__.py +0 -0
  22. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/database/_bound_checks.py +0 -0
  23. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/database/_confined_calls.py +0 -0
  24. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/database/_marker.py +0 -0
  25. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/database/_model_boundary.py +0 -0
  26. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/database/_model_columns.py +0 -0
  27. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/database/_raw_sql.py +0 -0
  28. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/database/_schema_drift.py +0 -0
  29. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/database/_statement_keys.py +0 -0
  30. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/effects/__init__.py +0 -0
  31. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/effects/_determinism.py +0 -0
  32. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/effects/_log_events.py +0 -0
  33. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/effects/_marker.py +0 -0
  34. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/hygiene/__init__.py +0 -0
  35. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/hygiene/_dependency_bounds.py +0 -0
  36. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/hygiene/_marker.py +0 -0
  37. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/imports/__init__.py +0 -0
  38. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/imports/_confined.py +0 -0
  39. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/imports/_marker.py +0 -0
  40. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/imports/_sealed.py +0 -0
  41. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/imports/_statements.py +0 -0
  42. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/placement/__init__.py +0 -0
  43. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/placement/_class_modules.py +0 -0
  44. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/placement/_class_placement.py +0 -0
  45. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/placement/_marker.py +0 -0
  46. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/placement/_operation_shape.py +0 -0
  47. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/placement/_required_class.py +0 -0
  48. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/signatures/__init__.py +0 -0
  49. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/signatures/_function_length.py +0 -0
  50. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/signatures/_functions.py +0 -0
  51. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/signatures/_keyword_only.py +0 -0
  52. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/signatures/_marker.py +0 -0
  53. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/signatures/_module_length.py +0 -0
  54. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/signatures/_nesting.py +0 -0
  55. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/signatures/_signature_layout.py +0 -0
  56. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/types/__init__.py +0 -0
  57. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/types/_annotation_shapes.py +0 -0
  58. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/types/_config_fields.py +0 -0
  59. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/types/_confined_types.py +0 -0
  60. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/types/_constant_annotations.py +0 -0
  61. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/types/_frozen_dataclasses.py +0 -0
  62. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/checks/types/_marker.py +0 -0
  63. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/cli/__init__.py +0 -0
  64. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/cli/_app.py +0 -0
  65. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/cli/_protocols.py +0 -0
  66. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/cli/commands/__init__.py +0 -0
  67. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/cli/commands/_explain.py +0 -0
  68. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/cli/commands/_list.py +0 -0
  69. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/cli/commands/_run.py +0 -0
  70. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/cli/commands/_summary.py +0 -0
  71. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/cli/commands/_sync.py +0 -0
  72. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/config/__init__.py +0 -0
  73. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/config/_base.py +0 -0
  74. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/config/_config.py +0 -0
  75. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/config/_constants.py +0 -0
  76. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/config/_errors.py +0 -0
  77. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/config/_loader.py +0 -0
  78. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/config/_toml.py +0 -0
  79. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/contracts/__init__.py +0 -0
  80. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/contracts/_constants.py +0 -0
  81. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/contracts/_layout.py +0 -0
  82. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/contracts/_render.py +0 -0
  83. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/contracts/_settings.py +0 -0
  84. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/core/_constants.py +0 -0
  85. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/core/_discovery.py +0 -0
  86. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/core/_edit.py +0 -0
  87. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/core/_errors.py +0 -0
  88. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/core/_fixer.py +0 -0
  89. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/core/_format.py +0 -0
  90. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/core/_protocols.py +0 -0
  91. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/core/_registry.py +0 -0
  92. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/core/_report.py +0 -0
  93. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/core/_runner.py +0 -0
  94. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/core/_settings.py +0 -0
  95. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/core/_source.py +0 -0
  96. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/core/_violation.py +0 -0
  97. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/environment/__init__.py +0 -0
  98. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/environment/_constants.py +0 -0
  99. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/py.typed +0 -0
  100. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/sync/__init__.py +0 -0
  101. {python_checks-0.1.1 → python_checks-0.2.0}/src/py_checks/sync/_sync.py +0 -0
@@ -1,3 +1,19 @@
1
+ Metadata-Version: 2.4
2
+ Name: python-checks
3
+ Version: 0.2.0
4
+ Summary: Проверки архитектурных соглашений проекта
5
+ Author: Armontex
6
+ Author-email: Armontex <windle1337@gmail.com>
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Requires-Dist: libcst>=1.9,<2
10
+ Requires-Dist: pydantic>=2.13.5,<3
11
+ Requires-Dist: pydantic-settings>=2.15.0,<3
12
+ Requires-Dist: rich>=14.0.0,<16.0.0
13
+ Requires-Dist: typer>=0.27.2,<0.28
14
+ Requires-Python: >=3.14
15
+ Description-Content-Type: text/markdown
16
+
1
17
  <div align="center">
2
18
 
3
19
  <img src="docs/logo.svg" width="112" alt="py-checks">
@@ -165,6 +181,13 @@ text("SELECT 1") # db-ok: raw-sql: проба живости, формы ORM н
165
181
  правило. У каждой группы есть своё короткое слово (`# db-ok`, `# type-ok`,
166
182
  `# signature-ok`, …): человек помнит группу, а не двадцать семь кодов.
167
183
 
184
+ Пометок в строке может стоять несколько — подпись в столбик собирает их на
185
+ последней строке:
186
+
187
+ ```python
188
+ ) -> object: # signature-ok: так зовёт pydantic # type-ok: сырой ввод
189
+ ```
190
+
168
191
  Пометка без кода, с опечаткой в коде или без причины — сама нарушение. Молча
169
192
  неработающая пометка выглядит как отключённая проверка, а на деле проверка
170
193
  работает и просто её не видит.
@@ -230,6 +253,10 @@ settings = ["myservice.config.settings:Settings"]
230
253
  докстринга класса и `description` поля, если оно у него есть, — так объяснение
231
254
  живёт рядом с полем, а не в файле, который его переживёт.
232
255
 
256
+ Шапку файла можно написать свою — `header = "# Generated by py-checks."`,
257
+ вместе с `#`. Нужна она проекту, который пишет комментарии на другом языке;
258
+ пустая оставляет шапку библиотеки.
259
+
233
260
  Классы импортируются, а не читаются текстом: имя переменной — значение
234
261
  атрибута, собранное вызовом, и прочитать его исходником значит выполнить этот
235
262
  вызов самому.
@@ -1,19 +1,3 @@
1
- Metadata-Version: 2.4
2
- Name: python-checks
3
- Version: 0.1.1
4
- Summary: Проверки архитектурных соглашений проекта
5
- Author: Armontex
6
- Author-email: Armontex <windle1337@gmail.com>
7
- License-Expression: MIT
8
- License-File: LICENSE
9
- Requires-Dist: libcst>=1.9,<2
10
- Requires-Dist: pydantic>=2.13.5,<3
11
- Requires-Dist: pydantic-settings>=2.15.0,<3
12
- Requires-Dist: rich>=14.0.0,<16.0.0
13
- Requires-Dist: typer>=0.27.2,<0.28
14
- Requires-Python: >=3.14
15
- Description-Content-Type: text/markdown
16
-
17
1
  <div align="center">
18
2
 
19
3
  <img src="docs/logo.svg" width="112" alt="py-checks">
@@ -181,6 +165,13 @@ text("SELECT 1") # db-ok: raw-sql: проба живости, формы ORM н
181
165
  правило. У каждой группы есть своё короткое слово (`# db-ok`, `# type-ok`,
182
166
  `# signature-ok`, …): человек помнит группу, а не двадцать семь кодов.
183
167
 
168
+ Пометок в строке может стоять несколько — подпись в столбик собирает их на
169
+ последней строке:
170
+
171
+ ```python
172
+ ) -> object: # signature-ok: так зовёт pydantic # type-ok: сырой ввод
173
+ ```
174
+
184
175
  Пометка без кода, с опечаткой в коде или без причины — сама нарушение. Молча
185
176
  неработающая пометка выглядит как отключённая проверка, а на деле проверка
186
177
  работает и просто её не видит.
@@ -246,6 +237,10 @@ settings = ["myservice.config.settings:Settings"]
246
237
  докстринга класса и `description` поля, если оно у него есть, — так объяснение
247
238
  живёт рядом с полем, а не в файле, который его переживёт.
248
239
 
240
+ Шапку файла можно написать свою — `header = "# Generated by py-checks."`,
241
+ вместе с `#`. Нужна она проекту, который пишет комментарии на другом языке;
242
+ пустая оставляет шапку библиотеки.
243
+
249
244
  Классы импортируются, а не читаются текстом: имя переменной — значение
250
245
  атрибута, собранное вызовом, и прочитать его исходником значит выполнить этот
251
246
  вызов самому.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-checks"
3
- version = "0.1.1"
3
+ version = "0.2.0"
4
4
  description = "Проверки архитектурных соглашений проекта"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-checks"
3
- version = "0.1.1"
3
+ version = "0.2.0"
4
4
  description = "Проверки архитектурных соглашений проекта"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -11,7 +11,7 @@ from py_checks.core._edit import Edit, apply, column
11
11
  from py_checks.core._errors import ParseError, UnknownCheckError
12
12
  from py_checks.core._fixer import fix
13
13
  from py_checks.core._format import reformat
14
- from py_checks.core._markers import MARKER, Marker, complaints, read, surviving
14
+ from py_checks.core._markers import MARKER, Marker, complaints, markers, surviving
15
15
  from py_checks.core._protocols import Check, FileCheck, ProjectCheck, Scope
16
16
  from py_checks.core._registry import Checks, available, get
17
17
  from py_checks.core._report import report
@@ -21,19 +21,19 @@ from py_checks.core._source import ParsedFile
21
21
  from py_checks.core._violation import Violation
22
22
 
23
23
  __all__ = [
24
- "EXIT_OK",
25
- "EXIT_VIOLATION",
26
- "GROUP",
27
- "MARKER",
28
- "SYNTAX",
29
24
  "Check",
30
25
  "Checks",
26
+ "EXIT_OK",
27
+ "EXIT_VIOLATION",
31
28
  "Edit",
32
29
  "FileCheck",
33
- "ProjectCheck",
30
+ "GROUP",
31
+ "MARKER",
34
32
  "Marker",
35
33
  "ParseError",
36
34
  "ParsedFile",
35
+ "ProjectCheck",
36
+ "SYNTAX",
37
37
  "Scope",
38
38
  "SettingsMismatchError",
39
39
  "UnknownCheckError",
@@ -42,15 +42,15 @@ __all__ = [
42
42
  "available",
43
43
  "column",
44
44
  "complaints",
45
- "fix",
46
45
  "examine",
46
+ "fix",
47
47
  "get",
48
48
  "inspect",
49
+ "markers",
49
50
  "python_files",
50
- "read",
51
51
  "reformat",
52
- "surviving",
53
- "settings_as",
54
52
  "report",
53
+ "settings_as",
55
54
  "survey",
55
+ "surviving",
56
56
  ]
@@ -49,33 +49,73 @@ class Marker:
49
49
  column: int
50
50
 
51
51
 
52
- def read(
52
+ def markers(
53
53
  *,
54
54
  line: str,
55
55
  aliases: Mapping[str, frozenset[str]],
56
- ) -> Marker | None:
57
- """Маркер из строки, если он там есть.
56
+ ) -> tuple[Marker, ...]:
57
+ """Маркеры строки: их может быть несколько.
58
+
59
+ Подпись в столбик собирает пометки в одну строку — `# signature-ok: так
60
+ зовёт библиотека # type-ok: сырой ввод`, — и снимать они должны обе.
58
61
 
59
62
  Разбор нарочно не падает на кривой записи: маркер без кода или без причины
60
63
  читается и попадает в `complaints`, иначе о нём никто бы не узнал.
61
64
  """
62
- if MARKER in line:
63
- codes, _, reason = line.split(MARKER, maxsplit=1)[1].partition(":")
64
- if not _named(text=codes):
65
- return None
66
- return Marker(
67
- codes=frozenset(_codes(text=codes)),
68
- reason=reason.strip(),
69
- column=line.index(MARKER) + 1,
65
+ found = sorted(
66
+ _starts(
67
+ line=line,
68
+ aliases=aliases,
70
69
  )
71
- for text, codes in aliases.items():
72
- if text in line:
73
- return Marker(
74
- codes=codes,
75
- reason=line.split(text, maxsplit=1)[1].removeprefix(":").strip(),
76
- column=line.index(text) + 1,
70
+ )
71
+ bounds = [*(start for start, _ in found), len(line)]
72
+ return tuple(
73
+ marker
74
+ for number, (start, word) in enumerate(found)
75
+ if (
76
+ marker := _marker(
77
+ text=line[start : bounds[number + 1]],
78
+ word=word,
79
+ aliases=aliases,
80
+ column=start + 1,
77
81
  )
78
- return None
82
+ )
83
+ )
84
+
85
+
86
+ def _starts(
87
+ *,
88
+ line: str,
89
+ aliases: Mapping[str, frozenset[str]],
90
+ ) -> Iterator[tuple[int, str]]:
91
+ for word in (MARKER, *aliases):
92
+ start = line.find(word)
93
+ if start != -1:
94
+ yield start, word
95
+
96
+
97
+ def _marker(
98
+ *,
99
+ text: str,
100
+ word: str,
101
+ aliases: Mapping[str, frozenset[str]],
102
+ column: int,
103
+ ) -> Marker | None:
104
+ tail = text.removeprefix(word)
105
+ if word != MARKER:
106
+ return Marker(
107
+ codes=aliases[word],
108
+ reason=tail.removeprefix(":").strip(),
109
+ column=column,
110
+ )
111
+ codes, _, reason = tail.partition(":")
112
+ if not _named(text=codes):
113
+ return None
114
+ return Marker(
115
+ codes=frozenset(_codes(text=codes)),
116
+ reason=reason.strip(),
117
+ column=column,
118
+ )
79
119
 
80
120
 
81
121
  def surviving(
@@ -109,18 +149,16 @@ def complaints(
109
149
  нарушение.
110
150
  """
111
151
  for number, line in enumerate(file.lines, start=1):
112
- marker = read(
152
+ for marker in markers(
113
153
  line=line,
114
154
  aliases=aliases,
115
- )
116
- if marker is None:
117
- continue
118
- yield from _wrong(
119
- marker=marker,
120
- path=file.path,
121
- line=number,
122
- known=known,
123
- )
155
+ ):
156
+ yield from _wrong(
157
+ marker=marker,
158
+ path=file.path,
159
+ line=number,
160
+ known=known,
161
+ )
124
162
 
125
163
 
126
164
  def _named(*, text: str) -> bool:
@@ -146,17 +184,17 @@ def _covered(
146
184
  file: ParsedFile,
147
185
  aliases: Mapping[str, frozenset[str]],
148
186
  ) -> bool:
149
- for line in _span(
150
- violation=violation,
151
- file=file,
152
- ):
153
- marker = read(
187
+ return any(
188
+ violation.code in marker.codes
189
+ for line in _span(
190
+ violation=violation,
191
+ file=file,
192
+ )
193
+ for marker in markers(
154
194
  line=line,
155
195
  aliases=aliases,
156
196
  )
157
- if marker is not None and violation.code in marker.codes:
158
- return True
159
- return False
197
+ )
160
198
 
161
199
 
162
200
  def _span(
@@ -56,9 +56,12 @@ def render(
56
56
  seen=set(),
57
57
  )
58
58
  ]
59
+ head = declared.header.strip() or HEADER.format(
60
+ section=f"{prefix(source=config.origin)}{SECTION}"
61
+ )
59
62
  return root / declared.path, "\n".join(
60
63
  [
61
- HEADER.format(section=f"{prefix(source=config.origin)}{SECTION}"),
64
+ f"{head.rstrip()}\n",
62
65
  *blocks,
63
66
  ]
64
67
  )
@@ -22,10 +22,15 @@ class Example(CheckSettings):
22
22
  «ничего не собирать»: проект без настроек из окружения — обычное дело.
23
23
 
24
24
  `path` — куда писать; по умолчанию `.env.example` в корне.
25
+
26
+ `header` — шапка файла со своим текстом, вместе с `#`. Пустая строка
27
+ оставляет шапку библиотеки: проекту, который пишет комментарии на другом
28
+ языке, своя нужна, остальным — нет.
25
29
  """
26
30
 
27
31
  settings: tuple[str, ...] = ()
28
32
  path: str = FILE
33
+ header: str = ""
29
34
 
30
35
 
31
36
  def example(*, config: Config) -> Example:
File without changes