debug-dojo 0.4.1__tar.gz → 0.5.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.
@@ -0,0 +1,21 @@
1
+ # The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 to bwrob.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,17 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: debug-dojo
3
- Version: 0.4.1
4
- Summary: 🏣 Debugging dojo for Python developers
5
- Project-URL: Homepage, https://bwrob.github.io/debug-dojo/
6
- Project-URL: Documentation, https://bwrob.github.io/debug-dojo/
7
- Project-URL: Source, https://github.com/bwrob/debug-dojo/
8
- Project-URL: Changelog, https://github.com/bwrob/debug-dojo/blob/main/HISTORY.md
3
+ Version: 0.5.0
4
+ Summary: 🏣 debug dojo, a place for zen debugging
9
5
  Author-email: bwrob <bartosz.marcin.wroblewski@gmail.com>
10
6
  License-Expression: MIT
11
- Keywords: debugging,inspection,pudb,rich,tools,traceback
12
- Classifier: Development Status :: 3 - Alpha
7
+ Project-URL: Changelog, https://bwrob.github.io/debug-dojo/changelog/
8
+ Project-URL: Documentation, https://bwrob.github.io/debug-dojo/
9
+ Project-URL: Homepage, https://bwrob.github.io/debug-dojo/
10
+ Project-URL: Source, https://github.com/bwrob/debug-dojo/
11
+ Keywords: debugging,inspection,tools,development
13
12
  Classifier: Intended Audience :: Developers
14
- Classifier: License :: OSI Approved :: MIT License
15
13
  Classifier: Operating System :: OS Independent
16
14
  Classifier: Programming Language :: Python :: 3.10
17
15
  Classifier: Programming Language :: Python :: 3.11
@@ -20,6 +18,8 @@ Classifier: Programming Language :: Python :: 3.13
20
18
  Classifier: Topic :: Software Development :: Debuggers
21
19
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
20
  Requires-Python: <3.14,>=3.10
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
23
  Requires-Dist: debugpy>=1.8.0
24
24
  Requires-Dist: ipdb>=0.13.0
25
25
  Requires-Dist: pudb>=2020.1
@@ -27,20 +27,23 @@ Requires-Dist: pydantic>=2.11.7
27
27
  Requires-Dist: rich>=12.0.0
28
28
  Requires-Dist: tomlkit>=0.13.3
29
29
  Requires-Dist: typer>=0.15.0
30
- Description-Content-Type: text/markdown
30
+ Dynamic: license-file
31
31
 
32
32
  # debug dojo
33
33
 
34
34
  <p align="center">
35
- <img src="https://github.com/bwrob/debug-dojo/blob/main/docs/logo/logo_python.png?raw=true" alt="debug dojo" style="width:50%; max-width:300px;"/>
35
+ <img src="https://github.com/bwrob/debug-dojo/blob/main/docs/logo/logo_python.png?raw=true" alt="debug dojo" style="width:50%; max-width:350px;"/>
36
36
  </p>
37
37
 
38
38
  <p align="center">
39
- <em>debug dojo, a place for zen debugging</em>
39
+ <em>🏣 debug dojo, a place for zen debugging</em>
40
40
  </p>
41
41
 
42
42
  [![PyPi Version](https://img.shields.io/pypi/v/debug-dojo.svg?style=flat-square)](https://pypi.org/project/debug-dojo)
43
43
  [![PyPI pyversions](https://img.shields.io/pypi/pyversions/debug-dojo.svg?style=flat-square)](https://pypi.org/pypi/debug-dojo/)
44
+ [![downloads](https://static.pepy.tech/badge/debug-dojo/month)](https://pepy.tech/project/debug-dojo)
45
+ [![license](https://img.shields.io/github/license/bwrob/debug-dojo.svg)](https://github.com/bwrob/debug-dojo/blob/main/LICENSE)
46
+ [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/bwrob/debug-dojo/main.svg)](https://results.pre-commit.ci/latest/github/bwrob/debug-dojo/main)
44
47
 
45
48
  [**debug-dojo**](https://bwrob.github.io/debug-dojo/) is a Python package providing utilities for enhanced debugging and inspection in the terminal.
46
49
  It leverages [`rich`](https://github.com/Textualize/rich) for beautiful output and offers helpers for side-by-side object comparison, improved tracebacks from `rich`, and easy integration with different debuggers -- `debugpy`, `pudb`, `pdb`, and `ipdb`.
@@ -78,48 +81,3 @@ c(object_1, object_2) # Compare two objects side-by-side.
78
81
  ## Documentation
79
82
 
80
83
  For instructions regarding installation, configuration and usage please visit [documentation](https://bwrob.github.io/debug-dojo/).
81
- # Changelog
82
-
83
- ## v0.4.1 (2025-08-10)
84
-
85
- *release tag*: [v0.4.1](https://github.com/bwrob/debug-dojo/releases/tag/v0.4.1)
86
-
87
- - Mkdocs documentation at [debug dojo](https://bwrob.github.io/debug-dojo).
88
-
89
- ## v0.4.0 (2025-08-10)
90
-
91
- *release tag*: [v0.4.0](https://github.com/bwrob/debug-dojo/releases/tag/v0.4.0)
92
-
93
- - New configuration model `DebugDojoConfig` for better structure.
94
- - Added support for `debugger` configuration in `dojo.toml`.
95
- - Updated `dojo` command to include debugger type in command call.
96
- - Configuration versioning allows for in-flight migration of old configurations.
97
- - Improved error handling in configuration loading.
98
- - Improved catching errors from target execution.
99
-
100
- ## v0.3.2 (2025-07-28)
101
-
102
- *release tag*: [v0.3.2](https://github.com/bwrob/debug-dojo/releases/tag/v0.3.2)
103
-
104
- - New logo for the project.
105
- - Typer used for CLI implementation.
106
- - Dojo is configured via `dojo.toml` or `pyproject.toml`.
107
- - Support for `debugpy` and `ipdb` for debugging.
108
- - Fixed documentation and history.
109
-
110
- ## v0.2.0 (2025-07-20)
111
-
112
- *release tag*: [v0.2.0](https://github.com/bwrob/debug-dojo/releases/tag/v0.2.0)
113
-
114
- - Added `dojo` command for easy debugging setup.
115
- - Added `p()` function for rich printing.
116
- - Added history file for tracking changes.
117
- - Moved to `hatch` for building and packaging.
118
- - Fixed `pyproject.toml` to point to GitHub repository as the homepage.
119
-
120
- ## v0.1.0 (2025-07-19)
121
-
122
- *release tag*: [v0.1.0](https://github.com/bwrob/debug-dojo/releases/tag/v0.1.0)
123
-
124
- - Initial module to install debugging tools.
125
- - Debug mode utilities for PuDB, rich tracebacks, and object inspection.
@@ -1,15 +1,18 @@
1
1
  # debug dojo
2
2
 
3
3
  <p align="center">
4
- <img src="https://github.com/bwrob/debug-dojo/blob/main/docs/logo/logo_python.png?raw=true" alt="debug dojo" style="width:50%; max-width:300px;"/>
4
+ <img src="https://github.com/bwrob/debug-dojo/blob/main/docs/logo/logo_python.png?raw=true" alt="debug dojo" style="width:50%; max-width:350px;"/>
5
5
  </p>
6
6
 
7
7
  <p align="center">
8
- <em>debug dojo, a place for zen debugging</em>
8
+ <em>🏣 debug dojo, a place for zen debugging</em>
9
9
  </p>
10
10
 
11
11
  [![PyPi Version](https://img.shields.io/pypi/v/debug-dojo.svg?style=flat-square)](https://pypi.org/project/debug-dojo)
12
12
  [![PyPI pyversions](https://img.shields.io/pypi/pyversions/debug-dojo.svg?style=flat-square)](https://pypi.org/pypi/debug-dojo/)
13
+ [![downloads](https://static.pepy.tech/badge/debug-dojo/month)](https://pepy.tech/project/debug-dojo)
14
+ [![license](https://img.shields.io/github/license/bwrob/debug-dojo.svg)](https://github.com/bwrob/debug-dojo/blob/main/LICENSE)
15
+ [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/bwrob/debug-dojo/main.svg)](https://results.pre-commit.ci/latest/github/bwrob/debug-dojo/main)
13
16
 
14
17
  [**debug-dojo**](https://bwrob.github.io/debug-dojo/) is a Python package providing utilities for enhanced debugging and inspection in the terminal.
15
18
  It leverages [`rich`](https://github.com/Textualize/rich) for beautiful output and offers helpers for side-by-side object comparison, improved tracebacks from `rich`, and easy integration with different debuggers -- `debugpy`, `pudb`, `pdb`, and `ipdb`.
@@ -0,0 +1,126 @@
1
+ [project]
2
+ authors = [ { name = "bwrob", email = "bartosz.marcin.wroblewski@gmail.com" } ]
3
+ classifiers = [
4
+ "Intended Audience :: Developers",
5
+ "Operating System :: OS Independent",
6
+ "Programming Language :: Python :: 3.10",
7
+ "Programming Language :: Python :: 3.11",
8
+ "Programming Language :: Python :: 3.12",
9
+ "Programming Language :: Python :: 3.13",
10
+ "Topic :: Software Development :: Debuggers",
11
+ "Topic :: Software Development :: Libraries :: Python Modules",
12
+ ]
13
+ dependencies = [
14
+ "debugpy>=1.8.0",
15
+ "ipdb>=0.13.0",
16
+ "pudb>=2020.1",
17
+ "pydantic>=2.11.7",
18
+ "rich>=12.0.0",
19
+ "tomlkit>=0.13.3",
20
+ "typer>=0.15.0",
21
+ ]
22
+ description = "🏣 debug dojo, a place for zen debugging"
23
+ keywords = [ "debugging", "inspection", "tools", "development" ]
24
+ license = 'MIT'
25
+ license-files = [ 'LICENSE' ]
26
+ name = "debug-dojo"
27
+ readme = "README.md"
28
+ requires-python = ">=3.10,<3.14"
29
+ version = "0.5.0"
30
+
31
+ [project.urls]
32
+ Changelog = "https://bwrob.github.io/debug-dojo/changelog/"
33
+ Documentation = "https://bwrob.github.io/debug-dojo/"
34
+ Homepage = "https://bwrob.github.io/debug-dojo/"
35
+ Source = "https://github.com/bwrob/debug-dojo/"
36
+
37
+ [project.scripts]
38
+ "dojo" = "debug_dojo._cli:main"
39
+
40
+
41
+ [dependency-groups]
42
+
43
+ dev = [
44
+ "basedpyright>=1.31.2",
45
+ "coverage>=7.10.4",
46
+ "poethepoet>=0.37.0",
47
+ "pytest>=8.4.1",
48
+ "ruff>=0.12.9",
49
+ ]
50
+ docs = [
51
+ "mkdocs>=1.6.1",
52
+ "mkdocs-git-authors-plugin>=0.10.0",
53
+ "mkdocs-git-committers-plugin-2>=2.5.0",
54
+ "mkdocs-git-revision-date-localized-plugin>=1.4.7",
55
+ "mkdocs-material>=9.6.17",
56
+ "mkdocs-typer2>=0.1.5",
57
+ "mkdocstrings-python>=1.17.0",
58
+ ]
59
+
60
+
61
+ [tool.uv]
62
+ default-groups = "all"
63
+ package = true
64
+ required-version = ">=0.8.0"
65
+
66
+
67
+ [tool.basedpyright]
68
+ ignore = [ "examples", "tests/assets" ]
69
+ pythonVersion = "3.10"
70
+ strict = [ "src/debug_dojo/*" ]
71
+ typeCheckingMode = "all"
72
+
73
+
74
+ [tool.ruff]
75
+ line-length = 88
76
+ target-version = "py310"
77
+
78
+ [tool.ruff.lint]
79
+ exclude = [ "examples" ]
80
+ ignore = [ "COM", "D203", "D211", "D213" ]
81
+ select = [ "ALL" ]
82
+
83
+ [tool.ruff.lint.per-file-ignores]
84
+ "examples/*" = [ "INP001" ]
85
+ "examples/test_target.py" = [ "F821" ]
86
+ "src/debug_dojo/_installers.py" = [ "PLC0415", "T100" ]
87
+ "tests/*" = [ "S101", "T201" ]
88
+ "tests/assets/*" = [ "T201", "INP001", "F821" ]
89
+
90
+ [tool.ruff.format]
91
+ docstring-code-format = true
92
+
93
+
94
+ [tool.poe.tasks]
95
+
96
+ format = { cmd = "ruff format --diff --config=pyproject.toml", help = "Check code formatting using ruff." }
97
+ lint = { cmd = "uv run ruff check --config=pyproject.toml", help = "Check code rules using ruff." }
98
+ precommit = { cmd = "pre-commit run --all-files", help = "Run all code quality checks." }
99
+ test = { cmd = "pytest --verbose --doctest-modules", help = "Run tests using pytest." }
100
+ type-check = { cmd = "basedpyright", help = "Check static typing using basedpyright." }
101
+
102
+ [tool.poe.tasks.code-quality]
103
+ help = "Run all code quality checks."
104
+ ignore_fail = "return_non_zero"
105
+ sequence = [ "fix", "precommit", "format", "lint", "type-check" ]
106
+
107
+ [tool.poe.tasks.fix]
108
+ default_item_type = "cmd"
109
+ help = "Code quality fixes using ruff."
110
+ ignore_fail = "return_non_zero"
111
+ sequence = [
112
+ "ruff check --fix --unsafe-fixes --config=pyproject.toml",
113
+ "ruff format --config=pyproject.toml",
114
+ ]
115
+
116
+ [tool.poe.tasks.publish]
117
+ sequence = [
118
+ "code-quality",
119
+ { cmd = "rm -rf dist" },
120
+ { cmd = "uv build" },
121
+ { cmd = "uv publish" },
122
+ ]
123
+
124
+ [tool.docformatter]
125
+ black = true
126
+ non-cap = [ "dojo" ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -4,16 +4,29 @@ from __future__ import annotations
4
4
 
5
5
  import runpy
6
6
  import sys
7
+ import traceback
7
8
  from bdb import BdbQuit
9
+ from enum import Enum
8
10
  from pathlib import Path
11
+ from shutil import which
9
12
  from typing import Annotated
10
13
 
11
14
  import typer
12
15
  from rich import print as rich_print
13
16
 
14
- from ._config import DebugDojoConfig, DebuggerType, load_config
17
+ from ._config import load_config
18
+ from ._config_models import DebugDojoConfig, DebuggerType # noqa: TC001
15
19
  from ._installers import install_by_config
16
20
 
21
+
22
+ class ExecMode(Enum):
23
+ """Execution mode for the target."""
24
+
25
+ FILE = "file"
26
+ MODULE = "module"
27
+ EXECUTABLE = "executable"
28
+
29
+
17
30
  cli = typer.Typer(
18
31
  name="debug_dojo",
19
32
  help="Run a Python script or module with debugging tools installed.",
@@ -21,15 +34,15 @@ cli = typer.Typer(
21
34
  )
22
35
 
23
36
 
24
- def execute_with_debug( # noqa: C901
37
+ def __execute_with_debug( # noqa: C901
25
38
  target_name: str,
26
39
  target_args: list[str],
27
40
  *,
28
- target_is_module: bool,
41
+ target_mode: ExecMode,
29
42
  verbose: bool,
30
43
  config: DebugDojoConfig,
31
44
  ) -> None:
32
- """Execute a target script or module with installation of debugging tools."""
45
+ """Execute a target script or module with installed debugging tools."""
33
46
  sys.argv = [target_name, *target_args]
34
47
 
35
48
  if verbose:
@@ -38,35 +51,40 @@ def execute_with_debug( # noqa: C901
38
51
 
39
52
  install_by_config(config)
40
53
 
41
- if target_is_module:
54
+ if target_mode is ExecMode.MODULE:
42
55
  runner = runpy.run_module
43
56
  else:
57
+ if target_mode is ExecMode.EXECUTABLE:
58
+ target_name = which(target_name) or target_name
59
+
44
60
  if not Path(target_name).exists():
45
- sys.exit(1)
61
+ raise typer.Exit(1)
62
+
46
63
  runner = runpy.run_path
47
64
 
48
65
  try:
49
66
  _ = runner(target_name, run_name="__main__")
50
67
  except ImportError as e:
51
68
  rich_print(f"[red]Error importing {target_name}:[/red]\n{e}")
52
- sys.exit(1)
69
+ raise typer.Exit(1) from e
53
70
  except BdbQuit:
54
71
  rich_print("[red]Debugging session terminated by user.[/red]")
55
- sys.exit(0)
72
+ raise typer.Exit(0) from None
56
73
  except KeyboardInterrupt:
57
74
  rich_print("[red]Execution interrupted by user.[/red]")
58
- sys.exit(0)
75
+ raise typer.Exit(0) from None
59
76
  except SystemExit as e:
60
77
  if e.code:
61
78
  rich_print(f"[red]Script exited with code {e.code}.[/red]")
62
- except Exception as e: # noqa: BLE001
79
+ except Exception as e:
63
80
  rich_print(f"[red]Error while running {target_name}:[/red]\n{e}")
81
+ rich_print(traceback.format_exc())
64
82
  if config.exceptions.post_mortem:
65
- import pdb # noqa: PLC0415, T100
83
+ import ipdb # pyright: ignore[reportMissingTypeStubs] # noqa: PLC0415, T100
66
84
 
67
85
  rich_print("[blue]Entering post-mortem debugging session...[/blue]")
68
- pdb.post_mortem(e.__traceback__)
69
- sys.exit(1)
86
+ ipdb.post_mortem(e.__traceback__) # pyright: ignore[reportUnknownMemberType]
87
+ raise typer.Exit(1) from e
70
88
 
71
89
 
72
90
  def display_config(config: DebugDojoConfig) -> None:
@@ -95,23 +113,41 @@ def run_debug( # noqa: PLR0913
95
113
  ] = None,
96
114
  verbose: Annotated[
97
115
  bool,
98
- typer.Option("--verbose", "-v", is_flag=True, help="Enable verbose output"),
116
+ typer.Option("--verbose", "-v", help="Enable verbose output"),
99
117
  ] = False,
100
118
  module: Annotated[
101
119
  bool,
102
- typer.Option("--module", "-m", is_flag=True, help="Run as a module"),
120
+ typer.Option("--module", "-m", help="Run a module"),
121
+ ] = False,
122
+ executable: Annotated[
123
+ bool,
124
+ typer.Option("--exec", "-e", help="Run a command"),
103
125
  ] = False,
104
126
  ) -> None:
105
127
  """Run the command-line interface."""
128
+ if module and executable:
129
+ rich_print(
130
+ "[red]Error: --module and --command options are mutually exclusive.[/red]"
131
+ )
132
+ raise typer.Exit(1)
133
+
134
+ mode = (
135
+ ExecMode.EXECUTABLE
136
+ if executable
137
+ else ExecMode.MODULE
138
+ if module
139
+ else ExecMode.FILE
140
+ )
141
+
106
142
  config = load_config(config_path, verbose=verbose, debugger=debugger)
107
143
 
108
144
  if verbose:
109
145
  display_config(config)
110
146
 
111
147
  if target_name:
112
- execute_with_debug(
148
+ __execute_with_debug(
113
149
  target_name=target_name,
114
- target_is_module=module,
150
+ target_mode=mode,
115
151
  target_args=ctx.args,
116
152
  verbose=verbose,
117
153
  config=config,
@@ -1,7 +1,7 @@
1
- """Utilities for side-by-side inspection and comparison of Python objects using Rich.
1
+ """Utilities for side-by-side inspection and comparison of Python objects.
2
2
 
3
- This module provides functions to display attributes and methods of two objects
4
- in a visually appealing, side-by-side format in the terminal.
3
+ This module provides functions to display attributes and methods of two objects in a
4
+ visually appealing, side-by-side format in the terminal.
5
5
  """
6
6
 
7
7
  from __future__ import annotations
@@ -15,15 +15,7 @@ from rich.text import Text
15
15
 
16
16
 
17
17
  def _get_object_attributes(obj: object) -> list[str]:
18
- """Extract and format non-callable attributes of an object.
19
-
20
- Args:
21
- obj: The object to inspect.
22
-
23
- Returns:
24
- A list of strings, each representing an attribute in 'name=value' format.
25
-
26
- """
18
+ """Extract and format non-callable attributes of an object."""
27
19
  attributes: list[str] = []
28
20
  for attr_name in sorted(dir(obj)):
29
21
  # Safely get attribute, returning None if it doesn't exist
@@ -37,15 +29,7 @@ def _get_object_attributes(obj: object) -> list[str]:
37
29
 
38
30
 
39
31
  def _get_object_methods(obj: object) -> list[str]:
40
- """Extract and format public callable methods of an object.
41
-
42
- Args:
43
- obj: The object to inspect.
44
-
45
- Returns:
46
- A list of strings, each representing a method name.
47
-
48
- """
32
+ """Extract and format public callable methods of an object."""
49
33
  methods: list[str] = []
50
34
  for method_name in sorted(dir(obj)):
51
35
  # Safely get attribute, returning None if it doesn't exist
@@ -59,15 +43,8 @@ def _get_object_methods(obj: object) -> list[str]:
59
43
  def _get_simplified_object_info(obj: object) -> list[Text]:
60
44
  """Generate a simplified, Rich-formatted inspection output for an object.
61
45
 
62
- Handles basic Python types by displaying their value directly. For other
63
- objects, it lists their attributes and public methods.
64
-
65
- Args:
66
- obj: The object to generate info for.
67
-
68
- Returns:
69
- A list of Rich Text objects, each representing a line of information.
70
-
46
+ Handles basic Python types by displaying their value directly. For other objects, it
47
+ lists their attributes and public methods.
71
48
  """
72
49
  info_lines: list[Text] = []
73
50
  obj_type: str = type(obj).__name__
@@ -1,19 +1,20 @@
1
1
  """Debug Dojo configuration module.
2
2
 
3
- It includes configurations for different debuggers, exception handling,
4
- and features that can be enabled or disabled.
3
+ It includes configurations for different debuggers, exception handling, and features
4
+ that can be enabled or disabled.
5
5
  """
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
- import sys
9
+ from collections.abc import Mapping, Sequence
10
10
  from pathlib import Path
11
- from typing import Any, cast
11
+ from typing import Any, TypeAlias, cast
12
12
 
13
13
  from pydantic import ValidationError
14
14
  from rich import print as rich_print
15
15
  from tomlkit import parse
16
16
  from tomlkit.exceptions import TOMLKitError
17
+ from typer import Exit
17
18
 
18
19
  from ._config_models import (
19
20
  DebugDojoConfig,
@@ -22,6 +23,10 @@ from ._config_models import (
22
23
  DebuggerType,
23
24
  )
24
25
 
26
+ JSON_ro: TypeAlias = (
27
+ Mapping[str, "JSON_ro"] | Sequence["JSON_ro"] | str | int | float | bool | None
28
+ )
29
+
25
30
 
26
31
  def __filter_pydantic_error_msg(error: ValidationError) -> str:
27
32
  """Filter out specific lines from a Pydantic validation error."""
@@ -32,8 +37,11 @@ def __filter_pydantic_error_msg(error: ValidationError) -> str:
32
37
  )
33
38
 
34
39
 
35
- def resolve_config_path(config_path: Path | None) -> Path | None:
36
- """Resolve the configuration path, returning a default if none is provided."""
40
+ def __resolve_config_path(config_path: Path | None) -> Path | None:
41
+ """Resolve the configuration path.
42
+
43
+ Returning a default if none is provided.
44
+ """
37
45
  if config_path:
38
46
  if not config_path.exists():
39
47
  msg = f"Configuration file not found:\n{config_path.resolve()}"
@@ -44,16 +52,16 @@ def resolve_config_path(config_path: Path | None) -> Path | None:
44
52
  for path in (Path("dojo.toml"), Path("pyproject.toml")):
45
53
  if path.exists():
46
54
  return path.resolve()
55
+
56
+ # None means - use default config values
47
57
  return None
48
58
 
49
59
 
50
- def load_raw_config(
51
- config_path: Path,
52
- ) -> dict[str, Any]: # pyright: ignore[reportExplicitAny]
60
+ def __load_raw_config(config_path: Path) -> JSON_ro:
53
61
  """Load the Debug Dojo configuration from a file.
54
62
 
55
- Currently supports 'dojo.toml' or 'pyproject.toml'.
56
- If no path is provided, it checks the current directory for these files.
63
+ Currently supports 'dojo.toml' or 'pyproject.toml'. If no path is provided, it
64
+ checks the current directory for these files.
57
65
  """
58
66
  config_str = config_path.read_text(encoding="utf-8")
59
67
 
@@ -66,7 +74,10 @@ def load_raw_config(
66
74
  # If config is in [tool.debug_dojo] (pyproject.toml), extract it.
67
75
  if config_path.name == "pyproject.toml":
68
76
  try:
69
- dojo_config = cast("dict[str, Any]", config_data["tool"]["debug_dojo"])
77
+ dojo_config = cast(
78
+ "dict[str, Any]", # pyright: ignore[reportExplicitAny]
79
+ config_data["tool"]["debug_dojo"],
80
+ )
70
81
  except KeyError:
71
82
  return {}
72
83
  else:
@@ -75,28 +86,11 @@ def load_raw_config(
75
86
  return config_data
76
87
 
77
88
 
78
- def load_config( # noqa: C901
79
- config_path: Path | None = None,
80
- *,
81
- verbose: bool = False,
82
- debugger: DebuggerType | None = None,
89
+ def __validated_and_updated_config(
90
+ raw_config: JSON_ro, *, verbose: bool
83
91
  ) -> DebugDojoConfig:
84
- """Load the Debug Dojo configuration and return a DebugDojoConfig instance."""
85
- resolved_path = resolve_config_path(config_path)
86
-
87
- if verbose:
88
- if resolved_path:
89
- msg = f"Using configuration file: {resolved_path}."
90
- else:
91
- msg = "No configuration file found, using default settings."
92
- rich_print(f"[blue]{msg}[/blue]")
93
-
94
- if not resolved_path:
95
- return DebugDojoConfig()
96
-
97
- raw_config = load_raw_config(resolved_path)
98
-
99
92
  config = None
93
+
100
94
  for model in (DebugDojoConfigV2, DebugDojoConfigV1):
101
95
  model_name = model.__name__
102
96
  try:
@@ -106,7 +100,6 @@ def load_config( # noqa: C901
106
100
  msg = (
107
101
  f"[yellow]Configuration validation error for {model_name}:\n"
108
102
  f"{__filter_pydantic_error_msg(e)}\n\n"
109
- f"Please check your configuration file {resolved_path}.[/yellow]"
110
103
  )
111
104
  rich_print(msg)
112
105
  else:
@@ -119,17 +112,45 @@ def load_config( # noqa: C901
119
112
  break
120
113
 
121
114
  if not config:
122
- msg = (
123
- f"[red]Unsupported configuration version in {resolved_path.resolve()}.\n"
124
- "Please update your configuration file.[/red]"
125
- )
115
+ msg = "[red]Unsupported configuration version or error.[/red]"
126
116
  rich_print(msg)
127
- sys.exit(1)
117
+ raise Exit(code=1)
128
118
 
129
119
  while not isinstance(config, DebugDojoConfig):
130
120
  config = config.update()
131
121
 
132
- # If a debugger is specified, update the config.
122
+ return config
123
+
124
+
125
+ def load_config(
126
+ config_path: Path | None = None,
127
+ *,
128
+ verbose: bool = False,
129
+ debugger: DebuggerType | None = None,
130
+ ) -> DebugDojoConfig:
131
+ """Load the Debug Dojo configuration.
132
+
133
+ Return a DebugDojoConfig instance with the loaded configuration.
134
+
135
+ If no configuration file is found, it returns a default configuration. If a debugger
136
+ is specified, it overrides the config.
137
+ """
138
+ resolved_path = __resolve_config_path(config_path)
139
+
140
+ if verbose:
141
+ if resolved_path:
142
+ msg = f"Using configuration file: {resolved_path}."
143
+ else:
144
+ msg = "No configuration file found, using default settings."
145
+ rich_print(f"[blue]{msg}[/blue]")
146
+
147
+ if not resolved_path:
148
+ return DebugDojoConfig()
149
+
150
+ raw_config = __load_raw_config(resolved_path)
151
+ config = __validated_and_updated_config(raw_config, verbose=verbose)
152
+
153
+ # If a debugger is specified, override the config.
133
154
  if debugger:
134
155
  config.debuggers.default = debugger
135
156