docstring-format-checker 1.11.4__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.
- docstring_format_checker/__init__.py +27 -0
- docstring_format_checker/cli.py +917 -0
- docstring_format_checker/config.py +622 -0
- docstring_format_checker/core.py +2289 -0
- docstring_format_checker/utils/__init__.py +0 -0
- docstring_format_checker/utils/exceptions.py +103 -0
- docstring_format_checker-1.11.4.dist-info/METADATA +546 -0
- docstring_format_checker-1.11.4.dist-info/RECORD +10 -0
- docstring_format_checker-1.11.4.dist-info/WHEEL +4 -0
- docstring_format_checker-1.11.4.dist-info/entry_points.txt +4 -0
|
@@ -0,0 +1,622 @@
|
|
|
1
|
+
# ============================================================================ #
|
|
2
|
+
# #
|
|
3
|
+
# Title: Configuration Management #
|
|
4
|
+
# Purpose: Configuration for docstring format checking #
|
|
5
|
+
# Notes: Notes #
|
|
6
|
+
# Author: chrimaho #
|
|
7
|
+
# Created: Created #
|
|
8
|
+
# References: References #
|
|
9
|
+
# Sources: Sources #
|
|
10
|
+
# Edited: Edited #
|
|
11
|
+
# #
|
|
12
|
+
# ============================================================================ #
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# ---------------------------------------------------------------------------- #
|
|
16
|
+
# #
|
|
17
|
+
# Overview ####
|
|
18
|
+
# #
|
|
19
|
+
# ---------------------------------------------------------------------------- #
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# ---------------------------------------------------------------------------- #
|
|
23
|
+
# Description ####
|
|
24
|
+
# ---------------------------------------------------------------------------- #
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
"""
|
|
28
|
+
!!! note "Summary"
|
|
29
|
+
Configuration handling for the docstring format checker.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# ---------------------------------------------------------------------------- #
|
|
34
|
+
# #
|
|
35
|
+
# Setup ####
|
|
36
|
+
# #
|
|
37
|
+
# ---------------------------------------------------------------------------- #
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
## --------------------------------------------------------------------------- #
|
|
41
|
+
## Imports ####
|
|
42
|
+
## --------------------------------------------------------------------------- #
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# ## Python StdLib Imports ----
|
|
46
|
+
import sys
|
|
47
|
+
from dataclasses import dataclass, field
|
|
48
|
+
from pathlib import Path
|
|
49
|
+
from typing import Any, Literal, Optional, Union
|
|
50
|
+
|
|
51
|
+
# ## Local First Party Imports ----
|
|
52
|
+
from docstring_format_checker.utils.exceptions import (
|
|
53
|
+
InvalidConfigError,
|
|
54
|
+
InvalidConfigError_DuplicateOrderValues,
|
|
55
|
+
InvalidTypeValuesError,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
if sys.version_info >= (3, 11):
|
|
60
|
+
# ## Python StdLib Imports ----
|
|
61
|
+
import tomllib
|
|
62
|
+
else:
|
|
63
|
+
# ## Python Third Party Imports ----
|
|
64
|
+
import tomli as tomllib
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
## --------------------------------------------------------------------------- #
|
|
68
|
+
## Exports ####
|
|
69
|
+
## --------------------------------------------------------------------------- #
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
__all__: list[str] = [
|
|
73
|
+
"GlobalConfig",
|
|
74
|
+
"SectionConfig",
|
|
75
|
+
"Config",
|
|
76
|
+
"DEFAULT_CONFIG",
|
|
77
|
+
"load_config",
|
|
78
|
+
"find_config_file",
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
## --------------------------------------------------------------------------- #
|
|
83
|
+
## Constants ####
|
|
84
|
+
## --------------------------------------------------------------------------- #
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
VALID_TYPES: tuple[str, ...] = (
|
|
88
|
+
"free_text", # Free text sections (summary, details, examples, notes)
|
|
89
|
+
"list_name", # Simple name sections (name)
|
|
90
|
+
"list_type", # Simple type sections (raises, yields)
|
|
91
|
+
"list_name_and_type", # Params-style sections (name (type): description)
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# ---------------------------------------------------------------------------- #
|
|
96
|
+
# #
|
|
97
|
+
# Config ####
|
|
98
|
+
# #
|
|
99
|
+
# ---------------------------------------------------------------------------- #
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
## --------------------------------------------------------------------------- #
|
|
103
|
+
## GlobalConfig ####
|
|
104
|
+
## --------------------------------------------------------------------------- #
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@dataclass
|
|
108
|
+
class GlobalConfig:
|
|
109
|
+
"""
|
|
110
|
+
!!! note "Summary"
|
|
111
|
+
Global configuration for docstring checking behavior.
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
allow_undefined_sections: bool = field(
|
|
115
|
+
default=False,
|
|
116
|
+
metadata={
|
|
117
|
+
"title": "Allow Undefined Sections",
|
|
118
|
+
"description": "Allow sections not defined in the configuration.",
|
|
119
|
+
},
|
|
120
|
+
)
|
|
121
|
+
require_docstrings: bool = field(
|
|
122
|
+
default=True,
|
|
123
|
+
metadata={
|
|
124
|
+
"title": "Require Docstrings",
|
|
125
|
+
"description": "Require docstrings for all functions/methods.",
|
|
126
|
+
},
|
|
127
|
+
)
|
|
128
|
+
check_private: bool = field(
|
|
129
|
+
default=False,
|
|
130
|
+
metadata={
|
|
131
|
+
"title": "Check Private Members",
|
|
132
|
+
"description": "Check docstrings for private members (starting with an underscore).",
|
|
133
|
+
},
|
|
134
|
+
)
|
|
135
|
+
validate_param_types: bool = field(
|
|
136
|
+
default=True,
|
|
137
|
+
metadata={
|
|
138
|
+
"title": "Validate Parameter Types",
|
|
139
|
+
"description": "Validate that parameter types are provided in the docstring.",
|
|
140
|
+
},
|
|
141
|
+
)
|
|
142
|
+
optional_style: Literal["silent", "validate", "strict"] = field(
|
|
143
|
+
default="validate",
|
|
144
|
+
metadata={
|
|
145
|
+
"title": "Optional Style",
|
|
146
|
+
"description": "The style for reporting issues in optional sections.",
|
|
147
|
+
},
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
## --------------------------------------------------------------------------- #
|
|
152
|
+
## SectionConfig ####
|
|
153
|
+
## --------------------------------------------------------------------------- #
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@dataclass
|
|
157
|
+
class SectionConfig:
|
|
158
|
+
"""
|
|
159
|
+
!!! note "Summary"
|
|
160
|
+
Configuration for a docstring section.
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
name: str = field(
|
|
164
|
+
metadata={
|
|
165
|
+
"title": "Name",
|
|
166
|
+
"description": "Name of the docstring section.",
|
|
167
|
+
},
|
|
168
|
+
)
|
|
169
|
+
type: Literal["free_text", "list_name", "list_type", "list_name_and_type"] = field(
|
|
170
|
+
metadata={
|
|
171
|
+
"title": "Type",
|
|
172
|
+
"description": "Type of the section content.",
|
|
173
|
+
},
|
|
174
|
+
)
|
|
175
|
+
order: Optional[int] = field(
|
|
176
|
+
default=None,
|
|
177
|
+
metadata={
|
|
178
|
+
"title": "Order",
|
|
179
|
+
"description": "Order of the section in the docstring.",
|
|
180
|
+
},
|
|
181
|
+
)
|
|
182
|
+
admonition: Union[bool, str] = field(
|
|
183
|
+
default=False,
|
|
184
|
+
metadata={
|
|
185
|
+
"title": "Admonition",
|
|
186
|
+
"description": "Admonition style for the section. Can be False (no admonition) or a string specifying the admonition type.",
|
|
187
|
+
},
|
|
188
|
+
)
|
|
189
|
+
prefix: str = field(
|
|
190
|
+
default="",
|
|
191
|
+
metadata={
|
|
192
|
+
"title": "Prefix",
|
|
193
|
+
"description": "Prefix string for the admonition values.",
|
|
194
|
+
},
|
|
195
|
+
)
|
|
196
|
+
required: bool = field(
|
|
197
|
+
default=False,
|
|
198
|
+
metadata={
|
|
199
|
+
"title": "Required",
|
|
200
|
+
"description": "Whether this section is required in the docstring.",
|
|
201
|
+
},
|
|
202
|
+
)
|
|
203
|
+
message: str = field(
|
|
204
|
+
default="",
|
|
205
|
+
metadata={
|
|
206
|
+
"title": "Message",
|
|
207
|
+
"description": "Optional message for validation errors.",
|
|
208
|
+
},
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
def __post_init__(self) -> None:
|
|
212
|
+
"""
|
|
213
|
+
!!! note "Summary"
|
|
214
|
+
Validate configuration after initialization.
|
|
215
|
+
"""
|
|
216
|
+
self._validate_types()
|
|
217
|
+
self._validate_admonition_prefix_combination()
|
|
218
|
+
|
|
219
|
+
def _validate_types(self) -> None:
|
|
220
|
+
"""
|
|
221
|
+
!!! note "Summary"
|
|
222
|
+
Validate the 'type' field.
|
|
223
|
+
"""
|
|
224
|
+
if self.type not in VALID_TYPES:
|
|
225
|
+
raise InvalidTypeValuesError(f"Invalid section type: {self.type}. Valid types: {VALID_TYPES}")
|
|
226
|
+
|
|
227
|
+
def _validate_admonition_prefix_combination(self) -> None:
|
|
228
|
+
"""
|
|
229
|
+
!!! note "Summary"
|
|
230
|
+
Validate admonition and prefix combination rules.
|
|
231
|
+
"""
|
|
232
|
+
|
|
233
|
+
if isinstance(self.admonition, bool):
|
|
234
|
+
# Rule: admonition cannot be True (only False or string)
|
|
235
|
+
if self.admonition is True:
|
|
236
|
+
raise ValueError(f"Section '{self.name}': admonition cannot be True, must be False or a string")
|
|
237
|
+
|
|
238
|
+
# Rule: if admonition is False, prefix cannot be provided
|
|
239
|
+
if self.admonition is False and self.prefix:
|
|
240
|
+
raise ValueError(f"Section '{self.name}': when admonition=False, prefix cannot be provided")
|
|
241
|
+
|
|
242
|
+
elif isinstance(self.admonition, str):
|
|
243
|
+
# Rule: if admonition is a string, prefix must be provided
|
|
244
|
+
if not self.prefix:
|
|
245
|
+
raise ValueError(f"Section '{self.name}': when admonition is a string, prefix must be provided")
|
|
246
|
+
|
|
247
|
+
else:
|
|
248
|
+
raise ValueError(
|
|
249
|
+
f"Section '{self.name}': admonition must be a boolean or string, got {type(self.admonition)}"
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
## --------------------------------------------------------------------------- #
|
|
254
|
+
## Validations ####
|
|
255
|
+
## --------------------------------------------------------------------------- #
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def _validate_config_order(config_sections: list[SectionConfig]) -> None:
|
|
259
|
+
"""
|
|
260
|
+
!!! note "Summary"
|
|
261
|
+
Validate that section order values are unique.
|
|
262
|
+
|
|
263
|
+
Params:
|
|
264
|
+
config_sections (list[SectionConfig]):
|
|
265
|
+
List of section configurations to validate.
|
|
266
|
+
|
|
267
|
+
Raises:
|
|
268
|
+
(InvalidConfigError_DuplicateOrderValues):
|
|
269
|
+
If duplicate order values are found.
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
(None):
|
|
273
|
+
Nothing is returned.
|
|
274
|
+
"""
|
|
275
|
+
|
|
276
|
+
# Validate no duplicate order values
|
|
277
|
+
order_values: list[int] = [section.order for section in config_sections if section.order is not None]
|
|
278
|
+
seen_orders: set[int] = set()
|
|
279
|
+
duplicate_orders: set[int] = set()
|
|
280
|
+
|
|
281
|
+
for order in order_values:
|
|
282
|
+
if order in seen_orders:
|
|
283
|
+
duplicate_orders.add(order)
|
|
284
|
+
else:
|
|
285
|
+
seen_orders.add(order)
|
|
286
|
+
|
|
287
|
+
if duplicate_orders:
|
|
288
|
+
raise InvalidConfigError_DuplicateOrderValues(
|
|
289
|
+
f"Configuration contains duplicate order values: {sorted(duplicate_orders)}. "
|
|
290
|
+
"Each section must have a unique order value."
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
# ---------------------------------------------------------------------------- #
|
|
295
|
+
# #
|
|
296
|
+
# Config Container ####
|
|
297
|
+
# #
|
|
298
|
+
# ---------------------------------------------------------------------------- #
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
@dataclass
|
|
302
|
+
class Config:
|
|
303
|
+
"""
|
|
304
|
+
!!! note "Summary"
|
|
305
|
+
Complete configuration containing global settings and section definitions.
|
|
306
|
+
"""
|
|
307
|
+
|
|
308
|
+
global_config: GlobalConfig
|
|
309
|
+
sections: list[SectionConfig]
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
# ---------------------------------------------------------------------------- #
|
|
313
|
+
# #
|
|
314
|
+
# Default Configuration ####
|
|
315
|
+
# #
|
|
316
|
+
# ---------------------------------------------------------------------------- #
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
DEFAULT_SECTIONS: list[SectionConfig] = [
|
|
320
|
+
SectionConfig(
|
|
321
|
+
order=1,
|
|
322
|
+
name="summary",
|
|
323
|
+
type="free_text",
|
|
324
|
+
admonition="note",
|
|
325
|
+
prefix="!!!",
|
|
326
|
+
required=True,
|
|
327
|
+
),
|
|
328
|
+
SectionConfig(
|
|
329
|
+
order=2,
|
|
330
|
+
name="details",
|
|
331
|
+
type="free_text",
|
|
332
|
+
admonition="info",
|
|
333
|
+
prefix="???+",
|
|
334
|
+
required=False,
|
|
335
|
+
),
|
|
336
|
+
SectionConfig(
|
|
337
|
+
order=3,
|
|
338
|
+
name="params",
|
|
339
|
+
type="list_name_and_type",
|
|
340
|
+
required=True,
|
|
341
|
+
),
|
|
342
|
+
SectionConfig(
|
|
343
|
+
order=4,
|
|
344
|
+
name="returns",
|
|
345
|
+
type="list_name_and_type",
|
|
346
|
+
required=False,
|
|
347
|
+
),
|
|
348
|
+
SectionConfig(
|
|
349
|
+
order=5,
|
|
350
|
+
name="yields",
|
|
351
|
+
type="list_type",
|
|
352
|
+
required=False,
|
|
353
|
+
),
|
|
354
|
+
SectionConfig(
|
|
355
|
+
order=6,
|
|
356
|
+
name="raises",
|
|
357
|
+
type="list_type",
|
|
358
|
+
required=False,
|
|
359
|
+
),
|
|
360
|
+
SectionConfig(
|
|
361
|
+
order=7,
|
|
362
|
+
name="examples",
|
|
363
|
+
type="free_text",
|
|
364
|
+
admonition="example",
|
|
365
|
+
prefix="???+",
|
|
366
|
+
required=False,
|
|
367
|
+
),
|
|
368
|
+
SectionConfig(
|
|
369
|
+
order=8,
|
|
370
|
+
name="notes",
|
|
371
|
+
type="free_text",
|
|
372
|
+
admonition="note",
|
|
373
|
+
prefix="???",
|
|
374
|
+
required=False,
|
|
375
|
+
),
|
|
376
|
+
]
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
DEFAULT_CONFIG: Config = Config(
|
|
380
|
+
global_config=GlobalConfig(),
|
|
381
|
+
sections=DEFAULT_SECTIONS,
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def load_config(config_path: Optional[Union[str, Path]] = None) -> Config:
|
|
386
|
+
"""
|
|
387
|
+
!!! note "Summary"
|
|
388
|
+
Load configuration from a TOML file or return default configuration.
|
|
389
|
+
|
|
390
|
+
Params:
|
|
391
|
+
config_path (Optional[Union[str, Path]]):
|
|
392
|
+
Path to the TOML configuration file.
|
|
393
|
+
If `None`, looks for `pyproject.toml` in current directory.
|
|
394
|
+
Default: `None`.
|
|
395
|
+
|
|
396
|
+
Raises:
|
|
397
|
+
(FileNotFoundError):
|
|
398
|
+
If the specified config file doesn't exist.
|
|
399
|
+
(InvalidConfigError):
|
|
400
|
+
If the configuration is invalid.
|
|
401
|
+
|
|
402
|
+
Returns:
|
|
403
|
+
(Config):
|
|
404
|
+
Configuration object containing global settings and section definitions.
|
|
405
|
+
"""
|
|
406
|
+
# Resolve config file path
|
|
407
|
+
resolved_path = _resolve_config_path(config_path)
|
|
408
|
+
if resolved_path is None:
|
|
409
|
+
return DEFAULT_CONFIG
|
|
410
|
+
|
|
411
|
+
# Parse TOML configuration
|
|
412
|
+
config_data = _parse_toml_file(resolved_path)
|
|
413
|
+
|
|
414
|
+
# Extract tool configuration
|
|
415
|
+
tool_config = _extract_tool_config(config_data)
|
|
416
|
+
if tool_config is None:
|
|
417
|
+
return DEFAULT_CONFIG
|
|
418
|
+
|
|
419
|
+
# Parse configuration components
|
|
420
|
+
global_config = _parse_global_config(tool_config)
|
|
421
|
+
sections_config = _parse_sections_config(tool_config)
|
|
422
|
+
|
|
423
|
+
return Config(global_config=global_config, sections=sections_config)
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
def _resolve_config_path(config_path: Optional[Union[str, Path]]) -> Optional[Path]:
|
|
427
|
+
"""
|
|
428
|
+
!!! note "Summary"
|
|
429
|
+
Resolve configuration file path.
|
|
430
|
+
|
|
431
|
+
Params:
|
|
432
|
+
config_path (Optional[Union[str, Path]]):
|
|
433
|
+
Optional path to configuration file.
|
|
434
|
+
|
|
435
|
+
Raises:
|
|
436
|
+
(FileNotFoundError):
|
|
437
|
+
If specified config file does not exist.
|
|
438
|
+
|
|
439
|
+
Returns:
|
|
440
|
+
(Optional[Path]):
|
|
441
|
+
Resolved Path object or None if no config found.
|
|
442
|
+
"""
|
|
443
|
+
if config_path is None:
|
|
444
|
+
# Look for pyproject.toml in current directory
|
|
445
|
+
pyproject_path: Path = Path.cwd().joinpath("pyproject.toml")
|
|
446
|
+
if pyproject_path.exists():
|
|
447
|
+
return pyproject_path
|
|
448
|
+
else:
|
|
449
|
+
return None
|
|
450
|
+
|
|
451
|
+
# Convert to Path object and check existence
|
|
452
|
+
config_path = Path(config_path)
|
|
453
|
+
if not config_path.exists():
|
|
454
|
+
raise FileNotFoundError(f"Configuration file not found: {config_path}")
|
|
455
|
+
|
|
456
|
+
return config_path
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
def _parse_toml_file(config_path: Path) -> dict[str, Any]:
|
|
460
|
+
"""
|
|
461
|
+
!!! note "Summary"
|
|
462
|
+
Parse TOML configuration file.
|
|
463
|
+
|
|
464
|
+
Params:
|
|
465
|
+
config_path (Path):
|
|
466
|
+
Path to TOML file to parse.
|
|
467
|
+
|
|
468
|
+
Raises:
|
|
469
|
+
(InvalidConfigError):
|
|
470
|
+
If TOML parsing fails.
|
|
471
|
+
|
|
472
|
+
Returns:
|
|
473
|
+
(dict[str, Any]):
|
|
474
|
+
Parsed TOML data as dictionary.
|
|
475
|
+
"""
|
|
476
|
+
try:
|
|
477
|
+
with open(config_path, "rb") as f:
|
|
478
|
+
return tomllib.load(f)
|
|
479
|
+
except Exception as e:
|
|
480
|
+
raise InvalidConfigError(f"Failed to parse TOML file {config_path}: {e}") from e
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
def _extract_tool_config(config_data: dict[str, Any]) -> Optional[dict[str, Any]]:
|
|
484
|
+
"""
|
|
485
|
+
!!! note "Summary"
|
|
486
|
+
Extract tool configuration from TOML data.
|
|
487
|
+
|
|
488
|
+
Params:
|
|
489
|
+
config_data (dict[str, Any]):
|
|
490
|
+
Parsed TOML data dictionary.
|
|
491
|
+
|
|
492
|
+
Returns:
|
|
493
|
+
(Optional[dict[str, Any]]):
|
|
494
|
+
Tool configuration dictionary or None if not found.
|
|
495
|
+
"""
|
|
496
|
+
if "tool" not in config_data:
|
|
497
|
+
return None
|
|
498
|
+
|
|
499
|
+
tool_section = config_data["tool"]
|
|
500
|
+
if "dfc" in tool_section:
|
|
501
|
+
return tool_section["dfc"]
|
|
502
|
+
elif "docstring-format-checker" in tool_section:
|
|
503
|
+
return tool_section["docstring-format-checker"]
|
|
504
|
+
|
|
505
|
+
return None
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
def _parse_global_config(tool_config: dict[str, Any]) -> GlobalConfig:
|
|
509
|
+
"""
|
|
510
|
+
!!! note "Summary"
|
|
511
|
+
Parse global configuration flags.
|
|
512
|
+
|
|
513
|
+
Params:
|
|
514
|
+
tool_config (dict[str, Any]):
|
|
515
|
+
Tool configuration dictionary.
|
|
516
|
+
|
|
517
|
+
Returns:
|
|
518
|
+
(GlobalConfig):
|
|
519
|
+
Parsed global configuration object.
|
|
520
|
+
"""
|
|
521
|
+
# Validate optional_style if provided
|
|
522
|
+
optional_style: str = tool_config.get("optional_style", "validate")
|
|
523
|
+
valid_styles: tuple[str, str, str] = ("silent", "validate", "strict")
|
|
524
|
+
if optional_style not in valid_styles:
|
|
525
|
+
raise InvalidConfigError(
|
|
526
|
+
f"Invalid optional_style: '{optional_style}'. Must be one of: {', '.join(valid_styles)}"
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
return GlobalConfig(
|
|
530
|
+
allow_undefined_sections=tool_config.get("allow_undefined_sections", False),
|
|
531
|
+
require_docstrings=tool_config.get("require_docstrings", True),
|
|
532
|
+
check_private=tool_config.get("check_private", False),
|
|
533
|
+
validate_param_types=tool_config.get("validate_param_types", True),
|
|
534
|
+
optional_style=optional_style, # type:ignore
|
|
535
|
+
)
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
def _parse_sections_config(tool_config: dict[str, Any]) -> list[SectionConfig]:
|
|
539
|
+
"""
|
|
540
|
+
!!! note "Summary"
|
|
541
|
+
Parse sections configuration.
|
|
542
|
+
|
|
543
|
+
Params:
|
|
544
|
+
tool_config (dict[str, Any]):
|
|
545
|
+
Tool configuration dictionary.
|
|
546
|
+
|
|
547
|
+
Returns:
|
|
548
|
+
(list[SectionConfig]):
|
|
549
|
+
List of section configuration objects or defaults.
|
|
550
|
+
"""
|
|
551
|
+
if "sections" not in tool_config:
|
|
552
|
+
return DEFAULT_SECTIONS
|
|
553
|
+
|
|
554
|
+
sections_config: list[SectionConfig] = []
|
|
555
|
+
sections_data = tool_config["sections"]
|
|
556
|
+
|
|
557
|
+
for section_data in sections_data:
|
|
558
|
+
try:
|
|
559
|
+
# Get admonition value with proper default handling
|
|
560
|
+
admonition_value: Union[str, bool] = section_data.get("admonition")
|
|
561
|
+
if admonition_value is None:
|
|
562
|
+
admonition_value = False # Use SectionConfig default
|
|
563
|
+
|
|
564
|
+
section = SectionConfig(
|
|
565
|
+
order=section_data.get("order"),
|
|
566
|
+
name=section_data.get("name", ""),
|
|
567
|
+
type=section_data.get("type", ""),
|
|
568
|
+
admonition=admonition_value,
|
|
569
|
+
prefix=section_data.get("prefix", ""),
|
|
570
|
+
required=section_data.get("required", False),
|
|
571
|
+
)
|
|
572
|
+
sections_config.append(section)
|
|
573
|
+
except (KeyError, TypeError, ValueError, InvalidTypeValuesError) as e:
|
|
574
|
+
raise InvalidConfigError(f"Invalid section configuration: {section_data}. Error: {e}") from e
|
|
575
|
+
|
|
576
|
+
# Validate and sort sections
|
|
577
|
+
if sections_config:
|
|
578
|
+
_validate_config_order(config_sections=sections_config)
|
|
579
|
+
sections_config.sort(key=lambda x: x.order if x.order is not None else float("inf"))
|
|
580
|
+
else:
|
|
581
|
+
sections_config = DEFAULT_SECTIONS
|
|
582
|
+
|
|
583
|
+
return sections_config
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
def find_config_file(start_path: Optional[Path] = None) -> Optional[Path]:
|
|
587
|
+
"""
|
|
588
|
+
!!! note "Summary"
|
|
589
|
+
Find configuration file by searching up the directory tree.
|
|
590
|
+
|
|
591
|
+
Params:
|
|
592
|
+
start_path (Optional[Path]):
|
|
593
|
+
Directory to start searching from.
|
|
594
|
+
If `None`, resolves to current directory.
|
|
595
|
+
Default: `None`.
|
|
596
|
+
|
|
597
|
+
Returns:
|
|
598
|
+
(Optional[Path]):
|
|
599
|
+
Path to the configuration file if found, None otherwise.
|
|
600
|
+
"""
|
|
601
|
+
if start_path is None:
|
|
602
|
+
start_path = Path.cwd()
|
|
603
|
+
|
|
604
|
+
current_path: Path = start_path.resolve()
|
|
605
|
+
|
|
606
|
+
while current_path != current_path.parent:
|
|
607
|
+
pyproject_path: Path = current_path.joinpath("pyproject.toml")
|
|
608
|
+
if pyproject_path.exists():
|
|
609
|
+
# Check if it contains dfc configuration
|
|
610
|
+
try:
|
|
611
|
+
with open(pyproject_path, "rb") as f:
|
|
612
|
+
config_data: dict[str, Any] = tomllib.load(f)
|
|
613
|
+
if "tool" in config_data and (
|
|
614
|
+
"dfc" in config_data["tool"] or "docstring-format-checker" in config_data["tool"]
|
|
615
|
+
):
|
|
616
|
+
return pyproject_path
|
|
617
|
+
except Exception:
|
|
618
|
+
pass
|
|
619
|
+
|
|
620
|
+
current_path = current_path.parent
|
|
621
|
+
|
|
622
|
+
return None
|