rdetoolkit 1.5.1__cp312-cp312-win_amd64.whl → 1.5.3__cp312-cp312-win_amd64.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.
rdetoolkit/__init__.py CHANGED
@@ -1,9 +1,21 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import sys
4
+ import warnings
5
+
6
+ # Python 3.9 deprecation warning (Issue #360)
7
+ if sys.version_info < (3, 10):
8
+ warnings.warn(
9
+ "Python 3.9 support is deprecated and will be removed in rdetoolkit v2.0. "
10
+ "Please upgrade to Python 3.10 or later.",
11
+ DeprecationWarning,
12
+ stacklevel=2,
13
+ )
14
+
3
15
  from importlib import import_module
4
16
  from typing import Any
5
17
 
6
- __version__ = "1.5.1"
18
+ __version__ = "1.5.3"
7
19
 
8
20
  _LAZY_ATTRS: dict[str, tuple[str, str]] = {
9
21
  "DirectoryOps": ("rdetoolkit.core", "DirectoryOps"),
@@ -26,6 +38,9 @@ _LAZY_MODULES: dict[str, str] = {
26
38
  "rde2util": "rdetoolkit.rde2util",
27
39
  "rdelogger": "rdetoolkit.rdelogger",
28
40
  "workflows": "rdetoolkit.workflows",
41
+ "processing": "rdetoolkit.processing",
42
+ "storage": "rdetoolkit.storage",
43
+ "traceback": "rdetoolkit.traceback",
29
44
  "config": "rdetoolkit.models.config",
30
45
  "invoice": "rdetoolkit.models.invoice",
31
46
  "invoice_schema": "rdetoolkit.models.invoice_schema",
@@ -55,6 +70,9 @@ __all__ = [
55
70
  "rde2util",
56
71
  "rdelogger",
57
72
  "workflows",
73
+ "processing",
74
+ "storage",
75
+ "traceback",
58
76
  "config",
59
77
  "invoice",
60
78
  "invoice_schema",
@@ -13,7 +13,13 @@ if TYPE_CHECKING:
13
13
 
14
14
  app = typer.Typer(
15
15
  name="validate",
16
- help="Validate RDE schema and data files",
16
+ help="""Validate RDE schema and data files.
17
+
18
+ All validate commands use standardized exit codes:
19
+ 0 = Success (validation passed)
20
+ 1 = Validation failure (data/schema issues)
21
+ 2 = Usage error (invalid arguments, missing files)
22
+ """,
17
23
  no_args_is_help=True,
18
24
  )
19
25
 
@@ -87,10 +93,11 @@ def handle_validation_error(error: Exception, error_type: str) -> None:
87
93
  typer.echo(str(error), err=True)
88
94
  raise typer.Exit(code=1) from error
89
95
 
90
- # Unknown errors get exit code 3 (internal error)
91
- msg = f"Internal error during {error_type}: {error}"
96
+ # All other errors (including internal errors) map to exit code 2
97
+ # This includes unexpected exceptions that indicate configuration or usage issues
98
+ msg = f"Error during {error_type}: {error}"
92
99
  typer.echo(msg, err=True)
93
- raise typer.Exit(code=3) from error
100
+ raise typer.Exit(code=2) from error
94
101
 
95
102
 
96
103
  # Common options for validation commands
@@ -141,6 +148,11 @@ def invoice_schema(
141
148
  Validates that the invoice schema file conforms to the expected structure
142
149
  and contains valid field definitions.
143
150
 
151
+ Exit Codes:
152
+ 0: Success - validation passed
153
+ 1: Validation failure - schema structure issues found
154
+ 2: Usage error - file not found or invalid arguments
155
+
144
156
  Examples:
145
157
  rdetoolkit validate invoice-schema tasksupport/invoice.schema.json
146
158
  rdetoolkit validate invoice-schema schema.json --format json
@@ -151,6 +163,9 @@ def invoice_schema(
151
163
  command = InvoiceSchemaCommand(schema_path)
152
164
  result = command.execute()
153
165
  handle_validation_result(result, format_type, strict, quiet)
166
+ except typer.Exit:
167
+ # Re-raise typer.Exit to let Typer handle it properly
168
+ raise
154
169
  except FileNotFoundError:
155
170
  handle_file_not_found(schema_path, "Schema")
156
171
  except Exception as e:
@@ -177,6 +192,11 @@ def metadata_def(
177
192
  Validates that the metadata definition file conforms to the expected
178
193
  structure defined by the MetadataItem schema.
179
194
 
195
+ Exit Codes:
196
+ 0: Success - validation passed
197
+ 1: Validation failure - definition structure issues found
198
+ 2: Usage error - file not found or invalid arguments
199
+
180
200
  Examples:
181
201
  rdetoolkit validate metadata-def tasksupport/metadata_def.json
182
202
  rdetoolkit validate metadata-def metadata_def.json --format json
@@ -187,6 +207,9 @@ def metadata_def(
187
207
  command = MetadataDefCommand(metadata_def_path)
188
208
  result = command.execute()
189
209
  handle_validation_result(result, format_type, strict, quiet)
210
+ except typer.Exit:
211
+ # Re-raise typer.Exit to let Typer handle it properly
212
+ raise
190
213
  except FileNotFoundError:
191
214
  handle_file_not_found(metadata_def_path, "Metadata definition")
192
215
  except Exception as e:
@@ -195,26 +218,22 @@ def metadata_def(
195
218
 
196
219
  @app.command("invoice")
197
220
  def invoice(
198
- invoice_path: Annotated[
199
- Path,
200
- typer.Argument(
201
- help="Path to invoice.json file",
202
- exists=True,
203
- dir_okay=False,
204
- resolve_path=True,
205
- ),
206
- ],
207
- schema_path: Annotated[
208
- Path,
209
- typer.Option(
210
- "--schema",
211
- "-s",
212
- help="Path to invoice.schema.json file",
213
- exists=True,
214
- dir_okay=False,
215
- resolve_path=True,
216
- ),
217
- ],
221
+ invoice_path: Path = typer.Argument(
222
+ ...,
223
+ help="Path to invoice.json file",
224
+ exists=True,
225
+ dir_okay=False,
226
+ resolve_path=True,
227
+ ),
228
+ schema_path: Path = typer.Option(
229
+ ...,
230
+ "--schema",
231
+ "-s",
232
+ help="Path to invoice.schema.json file",
233
+ exists=True,
234
+ dir_okay=False,
235
+ resolve_path=True,
236
+ ),
218
237
  format_type: FormatOption = OutputFormat.TEXT,
219
238
  strict: StrictOption = False,
220
239
  quiet: QuietOption = False,
@@ -224,6 +243,11 @@ def invoice(
224
243
  Validates that the invoice.json file conforms to the structure and
225
244
  constraints defined in the invoice.schema.json file.
226
245
 
246
+ Exit Codes:
247
+ 0: Success - validation passed
248
+ 1: Validation failure - data violates schema constraints
249
+ 2: Usage error - files not found or invalid arguments
250
+
227
251
  Examples:
228
252
  rdetoolkit validate invoice raw/invoice.json --schema tasksupport/invoice.schema.json
229
253
  rdetoolkit validate invoice invoice.json -s schema.json --format json
@@ -251,26 +275,22 @@ def invoice(
251
275
 
252
276
  @app.command("metadata")
253
277
  def metadata(
254
- metadata_path: Annotated[
255
- Path,
256
- typer.Argument(
257
- help="Path to metadata.json file",
258
- exists=True,
259
- dir_okay=False,
260
- resolve_path=True,
261
- ),
262
- ],
263
- schema_path: Annotated[
264
- Path,
265
- typer.Option(
266
- "--schema",
267
- "-s",
268
- help="Path to metadata definition JSON file",
269
- exists=True,
270
- dir_okay=False,
271
- resolve_path=True,
272
- ),
273
- ],
278
+ metadata_path: Path = typer.Argument(
279
+ ...,
280
+ help="Path to metadata.json file",
281
+ exists=True,
282
+ dir_okay=False,
283
+ resolve_path=True,
284
+ ),
285
+ schema_path: Path = typer.Option(
286
+ ...,
287
+ "--schema",
288
+ "-s",
289
+ help="Path to metadata definition JSON file",
290
+ exists=True,
291
+ dir_okay=False,
292
+ resolve_path=True,
293
+ ),
274
294
  format_type: FormatOption = OutputFormat.TEXT,
275
295
  strict: StrictOption = False,
276
296
  quiet: QuietOption = False,
@@ -280,6 +300,11 @@ def metadata(
280
300
  Validates that the metadata.json file conforms to the structure
281
301
  defined in the metadata definition file.
282
302
 
303
+ Exit Codes:
304
+ 0: Success - validation passed
305
+ 1: Validation failure - data violates definition constraints
306
+ 2: Usage error - files not found or invalid arguments
307
+
283
308
  Examples:
284
309
  rdetoolkit validate metadata raw/metadata.json --schema tasksupport/metadata_def.json
285
310
  rdetoolkit validate metadata metadata.json -s metadata_def.json --format json
@@ -290,6 +315,9 @@ def metadata(
290
315
  command = MetadataCommand(metadata_path, schema_path)
291
316
  result = command.execute()
292
317
  handle_validation_result(result, format_type, strict, quiet)
318
+ except typer.Exit:
319
+ # Re-raise typer.Exit to let Typer handle it properly
320
+ raise
293
321
  except FileNotFoundError:
294
322
  # Determine which file is missing
295
323
  if not metadata_path.exists():
@@ -304,16 +332,14 @@ def metadata(
304
332
 
305
333
  @app.command("all")
306
334
  def validate_all(
307
- project_dir: Annotated[
308
- Optional[Path],
309
- typer.Argument(
310
- help="Root directory of RDE project (defaults to current directory)",
311
- exists=True,
312
- file_okay=False,
313
- dir_okay=True,
314
- resolve_path=True,
315
- ),
316
- ] = None,
335
+ project_dir: Optional[Path] = typer.Argument(
336
+ None,
337
+ help="Root directory of RDE project (defaults to current directory)",
338
+ exists=True,
339
+ file_okay=False,
340
+ dir_okay=True,
341
+ resolve_path=True,
342
+ ),
317
343
  format_type: FormatOption = OutputFormat.TEXT,
318
344
  strict: StrictOption = False,
319
345
  quiet: QuietOption = False,
@@ -329,7 +355,10 @@ def validate_all(
329
355
  - input/invoice/invoice.json (with schema)
330
356
  - input/metadata/metadata.json (if exists, with schema)
331
357
 
332
- Exit code 0 only if ALL validations pass.
358
+ Exit Codes:
359
+ 0: Success - all validations passed
360
+ 1: Validation failure - one or more validations failed
361
+ 2: Usage error - project directory not found or invalid arguments
333
362
 
334
363
  Examples:
335
364
  rdetoolkit validate all
@@ -8,7 +8,7 @@ from dataclasses import dataclass
8
8
  from pathlib import Path
9
9
  from typing import Optional, Union
10
10
 
11
- from rdetoolkit.validation import InvoiceValidator, MetadataValidator
11
+ from rdetoolkit.validation import InvoiceValidator, MetadataDefinitionValidator, MetadataValidator
12
12
 
13
13
 
14
14
  @dataclass
@@ -181,8 +181,7 @@ def determine_exit_code(result: ValidationResult, strict: bool = False) -> int:
181
181
  Exit codes:
182
182
  - 0: Validation passed
183
183
  - 1: Validation failed (errors or warnings in strict mode)
184
- - 2: Usage/argument errors (handled by Typer)
185
- - 3: Internal errors (handled by exception handlers)
184
+ - 2: Usage/argument errors (handled by CLI layer)
186
185
 
187
186
  Args:
188
187
  result: Validation result
@@ -428,7 +427,11 @@ class MetadataDefCommand(_ValidationErrorParser):
428
427
  """Command to validate metadata definition JSON files.
429
428
 
430
429
  This command validates metadata definition files using the
431
- MetadataValidator's schema (MetadataItem pydantic model).
430
+ MetadataDefinitionValidator (for metadata-def.json structure).
431
+
432
+ Note:
433
+ This is separate from MetadataCommand which validates metadata.json
434
+ data files against metadata definition schemas.
432
435
  """
433
436
 
434
437
  def __init__(self, metadata_def_path: str | Path) -> None:
@@ -460,7 +463,8 @@ class MetadataDefCommand(_ValidationErrorParser):
460
463
  warnings: list[ValidationWarning] = []
461
464
 
462
465
  try:
463
- validator = MetadataValidator()
466
+ # Use MetadataDefinitionValidator for metadata-def.json
467
+ validator = MetadataDefinitionValidator()
464
468
  # validate() with path parameter validates the file
465
469
  _ = validator.validate(path=self.metadata_def_path)
466
470
 
@@ -520,16 +524,15 @@ class MetadataCommand(_ValidationErrorParser):
520
524
  warnings: list[ValidationWarning] = []
521
525
 
522
526
  try:
523
- # First validate the schema/definition itself
524
- validator = MetadataValidator()
525
- _ = validator.validate(path=self.schema_path)
526
-
527
- # Then validate the metadata data against the schema
528
- # Note: Current MetadataValidator validates individual items,
529
- # not data against a separate schema. This is a structural difference
530
- # from invoice validation. We validate that the metadata file
531
- # conforms to MetadataItem structure.
532
- _ = validator.validate(path=self.metadata_path)
527
+ # First validate the schema/definition file (metadata-def.json)
528
+ # Uses MetadataDefinitionValidator for dict[str, MetadataDefEntry] structure
529
+ def_validator = MetadataDefinitionValidator()
530
+ _ = def_validator.validate(path=self.schema_path)
531
+
532
+ # Then validate the metadata data file (metadata.json)
533
+ # Uses MetadataValidator for MetadataItem structure (constant/variable)
534
+ data_validator = MetadataValidator()
535
+ _ = data_validator.validate(path=self.metadata_path)
533
536
 
534
537
  except Exception as e:
535
538
  # Parse validation errors from exception message
rdetoolkit/config.py CHANGED
@@ -6,8 +6,11 @@ from typing import Any, Final
6
6
 
7
7
  import yaml
8
8
  from pydantic import ValidationError
9
+ from tomlkit.exceptions import TOMLKitError
9
10
  from tomlkit.toml_file import TOMLFile
11
+ from yaml import YAMLError
10
12
 
13
+ from rdetoolkit.exceptions import ConfigError
11
14
  from rdetoolkit.models.config import Config, TracebackSettings, MultiDataTileSettings, SystemSettings, SmartTableSettings
12
15
  from rdetoolkit.models.rde2types import RdeFsPath
13
16
 
@@ -16,6 +19,69 @@ PYPROJECT_CONFIG_FILES: Final = ["pyproject.toml"]
16
19
  CONFIG_FILES = CONFIG_FILE + PYPROJECT_CONFIG_FILES
17
20
 
18
21
 
22
+ def _format_validation_error(
23
+ validation_error: ValidationError,
24
+ file_path: str,
25
+ ) -> ConfigError:
26
+ """Format pydantic ValidationError into user-friendly ConfigError.
27
+
28
+ Args:
29
+ validation_error: The pydantic ValidationError
30
+ file_path: Path to the configuration file
31
+
32
+ Returns:
33
+ ConfigError with detailed field-level information
34
+ """
35
+ errors = validation_error.errors()
36
+
37
+ if not errors:
38
+ return ConfigError(
39
+ "Configuration validation failed",
40
+ file_path=file_path,
41
+ error_type="validation_error",
42
+ )
43
+
44
+ # Take the first error for the main message
45
+ first_error = errors[0]
46
+ field_path = ".".join(str(loc) for loc in first_error["loc"])
47
+ error_msg = first_error["msg"]
48
+ error_type_detail = first_error["type"]
49
+
50
+ # Build detailed message
51
+ message_parts = [f"Invalid configuration in '{file_path}'"]
52
+
53
+ if field_path:
54
+ message_parts.append(f"Field '{field_path}' validation failed: {error_msg}")
55
+ else:
56
+ message_parts.append(f"Validation failed: {error_msg}")
57
+
58
+ # Add information about expected values if available
59
+ if "input" in first_error:
60
+ input_value = first_error["input"]
61
+ message_parts.append(f"Provided value: {input_value!r}")
62
+
63
+ # For extended_mode, provide specific guidance
64
+ if "extended_mode" in field_path and "enum" in error_type_detail.lower():
65
+ message_parts.append(
66
+ "Valid values for 'extended_mode': ['rdeformat', 'MultiDataTile']",
67
+ )
68
+
69
+ # Add validation error context if multiple errors exist
70
+ if len(errors) > 1:
71
+ message_parts.append(
72
+ f"Note: {len(errors)} validation error(s) found. Showing the first one.",
73
+ )
74
+
75
+ full_message = "\n".join(message_parts)
76
+
77
+ return ConfigError(
78
+ full_message,
79
+ file_path=file_path,
80
+ error_type="validation_error",
81
+ field_name=field_path,
82
+ )
83
+
84
+
19
85
  def parse_config_file(*, path: str | None = None) -> Config:
20
86
  """Parse the configuration file and return a Config object.
21
87
 
@@ -26,7 +92,7 @@ def parse_config_file(*, path: str | None = None) -> Config:
26
92
  Config: The parsed configuration object.
27
93
 
28
94
  Raises:
29
- FileNotFoundError: If the specified configuration file does not exist.
95
+ ConfigError: If the specified configuration file does not exist or cannot be parsed.
30
96
 
31
97
  File Loading Priority:
32
98
  1. If `path` is provided and the file extension is ".toml", the function will attempt to read the file as a TOML file.
@@ -39,10 +105,14 @@ def parse_config_file(*, path: str | None = None) -> Config:
39
105
  - "pyproject.toml"
40
106
 
41
107
  Note:
42
- - If the specified configuration file does not exist or is not in the correct format, an empty Config object will be returned.
108
+ - If the specified file is not a recognized config file name (not in CONFIG_FILES),
109
+ an empty Config object will be returned.
110
+ - If the specified file does not exist, ConfigError is raised.
111
+ - If the file contains invalid YAML/TOML syntax, ConfigError is raised with line/column info.
112
+ - If the configuration fails validation, ConfigError is raised with field details.
43
113
 
44
114
  Example:
45
- parse_config_file(path="config.yaml")
115
+ parse_config_file(path="rdeconfig.yaml")
46
116
 
47
117
  """
48
118
  config_data: dict[str, Any] = {
@@ -50,14 +120,57 @@ def parse_config_file(*, path: str | None = None) -> Config:
50
120
  "multidata_tile": MultiDataTileSettings().model_dump(),
51
121
  "smarttable": SmartTableSettings().model_dump(),
52
122
  }
123
+
124
+ # Check file existence when path is provided
125
+ if path is not None:
126
+ path_obj = Path(path)
127
+ if not path_obj.exists():
128
+ msg = (
129
+ f"Configuration file not found: '{path}'. "
130
+ f"Create a configuration file or use 'rdetoolkit gen-config' to generate one."
131
+ )
132
+ raise ConfigError(
133
+ msg,
134
+ file_path=path,
135
+ error_type="file_not_found",
136
+ )
137
+
53
138
  if path is not None and Path(path).name not in CONFIG_FILES:
54
139
  return Config(system=SystemSettings(), multidata_tile=MultiDataTileSettings(), smarttable=SmartTableSettings())
55
140
 
56
141
  if path is not None and is_toml(path):
57
142
  config_data = __read_pyproject_toml(path)
58
143
  elif path is not None and is_yaml(path):
59
- with open(path, encoding="utf-8") as f:
60
- config_data = yaml.safe_load(f)
144
+ try:
145
+ with open(path, encoding="utf-8") as f:
146
+ config_data = yaml.safe_load(f)
147
+ except YAMLError as e:
148
+ # Extract line and column information from YAMLError
149
+ line_number = None
150
+ column_number = None
151
+
152
+ if hasattr(e, "problem_mark") and e.problem_mark is not None:
153
+ line_number = e.problem_mark.line + 1 # YAML uses 0-indexed lines
154
+ column_number = e.problem_mark.column + 1
155
+
156
+ error_msg = "Failed to parse YAML file: invalid syntax"
157
+ if hasattr(e, "problem"):
158
+ error_msg = f"Failed to parse YAML file: {e.problem}"
159
+
160
+ raise ConfigError(
161
+ error_msg,
162
+ file_path=path,
163
+ error_type="parse_error",
164
+ line_number=line_number,
165
+ column_number=column_number,
166
+ ) from e
167
+ except OSError as e:
168
+ msg = f"Failed to read YAML file: {e}"
169
+ raise ConfigError(
170
+ msg,
171
+ file_path=path,
172
+ error_type="io_error",
173
+ ) from e
61
174
  elif path is None:
62
175
  project_path = Path.cwd()
63
176
  pyproject_toml = project_path.joinpath(PYPROJECT_CONFIG_FILES[0])
@@ -68,19 +181,81 @@ def parse_config_file(*, path: str | None = None) -> Config:
68
181
  if config_data is None:
69
182
  return Config(system=SystemSettings(), multidata_tile=MultiDataTileSettings(), smarttable=SmartTableSettings())
70
183
 
71
- return Config(**config_data)
184
+ try:
185
+ return Config(**config_data)
186
+ except ValidationError as e:
187
+ # Use helper to format validation error
188
+ if path:
189
+ raise _format_validation_error(e, path) from e
190
+ # Fallback for when path is None
191
+ msg = f"Configuration validation failed: {str(e)}"
192
+ raise ConfigError(
193
+ msg,
194
+ error_type="validation_error",
195
+ ) from e
196
+ except TypeError as e:
197
+ # This occurs when config_data is not a mapping (e.g., list or string),
198
+ # which makes Config(**config_data) invalid. Normalize it to ConfigError.
199
+ base_msg = "Configuration data must be a mapping (key-value structure)"
200
+ msg = f"{base_msg} in file '{path}'" if path else base_msg
201
+ raise ConfigError(
202
+ msg,
203
+ file_path=path,
204
+ error_type="validation_error",
205
+ ) from e
72
206
 
73
207
 
74
208
  def __read_pyproject_toml(path: str) -> dict[str, Any]:
75
209
  """Read the pyproject.toml file and return the contents as a dictionary.
76
210
 
211
+ Args:
212
+ path: Path to the pyproject.toml file
213
+
77
214
  Returns:
78
215
  dict[str, Any]: The contents of the pyproject.toml file.
216
+
217
+ Raises:
218
+ ConfigError: If the file does not exist or cannot be parsed
79
219
  """
80
- toml = TOMLFile(path)
81
- obj = toml.read()
82
- _obj = obj.unwrap()
83
- return _obj.get("tool", {}).get("rdetoolkit", {})
220
+ # Check file existence first
221
+ path_obj = Path(path)
222
+ if not path_obj.exists():
223
+ msg = (
224
+ f"Configuration file not found: '{path}'. "
225
+ f"Create a pyproject.toml file with [tool.rdetoolkit] section."
226
+ )
227
+ raise ConfigError(
228
+ msg,
229
+ file_path=path,
230
+ error_type="file_not_found",
231
+ )
232
+
233
+ try:
234
+ toml = TOMLFile(path)
235
+ obj = toml.read()
236
+ _obj = obj.unwrap()
237
+ return _obj.get("tool", {}).get("rdetoolkit", {})
238
+ except TOMLKitError as e:
239
+ # Extract line information if available
240
+ line_number = None
241
+ if hasattr(e, "line"):
242
+ line_number = e.line
243
+
244
+ error_msg = f"Failed to parse TOML file: {str(e)}"
245
+
246
+ raise ConfigError(
247
+ error_msg,
248
+ file_path=path,
249
+ error_type="parse_error",
250
+ line_number=line_number,
251
+ ) from e
252
+ except OSError as e:
253
+ msg = f"Failed to read TOML file: {e}"
254
+ raise ConfigError(
255
+ msg,
256
+ file_path=path,
257
+ error_type="io_error",
258
+ ) from e
84
259
 
85
260
 
86
261
  def is_toml(filename: str) -> bool:
@@ -153,27 +328,32 @@ def get_config(target_dir_path: RdeFsPath) -> Config | None:
153
328
 
154
329
  Returns:
155
330
  Optional[Config]: The first valid configuration found, or None if no valid configuration is found.
331
+
332
+ Raises:
333
+ ConfigError: If the target directory does not exist.
156
334
  """
157
335
  if isinstance(target_dir_path, str):
158
336
  target_dir_path = Path(target_dir_path)
159
337
  if not target_dir_path.exists():
160
- return None
338
+ msg = (
339
+ f"Configuration directory not found: '{target_dir_path}'. "
340
+ f"Ensure the directory exists and contains a valid configuration file."
341
+ )
342
+ raise ConfigError(
343
+ msg,
344
+ file_path=str(target_dir_path),
345
+ error_type="directory_not_found",
346
+ )
161
347
  for cfg_file in find_config_files(target_dir_path):
162
- try:
163
- __config = parse_config_file(path=cfg_file)
164
- except ValidationError as e:
165
- emsg = f"Invalid configuration file: {cfg_file}"
166
- raise ValueError(emsg) from e
348
+ # parse_config_file now converts ValidationError to ConfigError internally
349
+ __config = parse_config_file(path=cfg_file)
167
350
  if __config is not None:
168
351
  return __config
169
352
 
170
353
  pyproject_toml_path = get_pyproject_toml()
171
354
  if pyproject_toml_path is not None:
172
- try:
173
- __config = parse_config_file(path=str(pyproject_toml_path))
174
- except ValidationError as e:
175
- emsg = f"Invalid configuration file: {pyproject_toml_path}"
176
- raise ValueError(emsg) from e
355
+ # parse_config_file now converts ValidationError to ConfigError internally
356
+ __config = parse_config_file(path=str(pyproject_toml_path))
177
357
  if __config is not None:
178
358
  return __config
179
359
  return None
@@ -194,8 +374,16 @@ def load_config(tasksupport_path: RdeFsPath, *, config: Config | None = None) ->
194
374
  if config is not None:
195
375
  __config = config
196
376
  else:
197
- __rtn_config = get_config(tasksupport_path)
198
- __config = Config() if __rtn_config is None else __rtn_config
377
+ try:
378
+ __rtn_config = get_config(tasksupport_path)
379
+ __config = Config() if __rtn_config is None else __rtn_config
380
+ except ConfigError as e:
381
+ # Only swallow directory_not_found errors for backward compatibility
382
+ # Re-raise other ConfigError types (parse_error, validation_error, etc.)
383
+ if getattr(e, "error_type", None) == "directory_not_found":
384
+ __config = Config()
385
+ else:
386
+ raise
199
387
  return __config
200
388
 
201
389
 
Binary file