Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ releases are available on [PyPI](https://pypi.org/project/pytask) and
- {pull}`708` updates mypy and fixes type issues.
- {pull}`709` add uv pre-commit check.
- {pull}`713` removes uv as a test dependency. Closes {issue}`712`. Thanks to {user}`erooke`!
- {pull}`718` fixes {issue}`717` by properly parsing the `pdbcls` configuration option from config files. Thanks to {user}`MImmesberger` for the report!

## 0.5.5 - 2025-07-25

Expand Down
28 changes: 28 additions & 0 deletions docs/source/reference_guides/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,34 @@ paths = ["folder_1", "folder_2/task_2.py"]
```
````

````{confval} pdbcls

If you want to use a custom debugger instead of the standard Python debugger, you can
specify it with the `pdbcls` option. The value must be in the format
`module_name:class_name`.

```console
$ pytask build --pdbcls=IPython.terminal.debugger:TerminalPdb
```

Or, use the configuration file:

```toml
pdbcls = "IPython.terminal.debugger:TerminalPdb"
```

This is particularly useful when working with enhanced debuggers like `pdbpp` or `pdbp`
that provide additional features such as syntax highlighting and tab completion:

```toml
pdbcls = "pdbp:Pdb"
```

The custom debugger will be used when you invoke the `--pdb` flag for post-mortem
debugging or when using `breakpoint()` in your task code.

````

````{confval} show_errors_immediately

If you want to print the exception and tracebacks of errors as soon as they occur,
Expand Down
37 changes: 28 additions & 9 deletions src/_pytask/debugging.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,22 +60,41 @@ def pytask_extend_command_line_interface(cli: click.Group) -> None:
cli.commands["build"].params.extend(additional_parameters)


def _parse_pdbcls(value: str | None) -> tuple[str, str] | None:
"""Parse and validate pdbcls string format."""
if value is None:
return None
if isinstance(value, tuple) and len(value) == 2: # noqa: PLR2004
return value
if not isinstance(value, str):
msg = "'pdbcls' must be a string in format 'module:classname', got {value!r}"
raise TypeError(msg)
split = value.split(":")
if len(split) != 2: # noqa: PLR2004
msg = (
f"Invalid 'pdbcls' format: {value!r}. "
"Must be like 'IPython.terminal.debugger:TerminalPdb'"
)
raise ValueError(msg)
return tuple(split) # type: ignore[return-value]


def _pdbcls_callback(
ctx: click.Context, # noqa: ARG001
name: str, # noqa: ARG001
value: str | None,
) -> tuple[str, str] | None:
"""Validate the debugger class string passed to pdbcls."""
message = "'pdbcls' must be like IPython.terminal.debugger:TerminalPdb"
try:
return _parse_pdbcls(value)
except Exception as e:
raise click.BadParameter(str(e)) from e

if value is None:
return None
if isinstance(value, str):
split = value.split(":")
if len(split) != 2: # noqa: PLR2004
raise click.BadParameter(message)
return tuple(split) # type: ignore[return-value]
raise click.BadParameter(message)

@hookimpl
def pytask_parse_config(config: dict[str, Any]) -> None:
"""Parse the debugger configuration."""
config["pdbcls"] = _parse_pdbcls(config.get("pdbcls"))


@hookimpl(trylast=True)
Expand Down