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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ releases are available on [PyPI](https://pypi.org/project/pytask) and
- [#970](https://github.com/pytask-dev/pytask/pull/970) prevents tee-sys capture from
replaying output that was already displayed when capture stops before its buffer is
read.
- [#972](https://github.com/pytask-dev/pytask/pull/972) uses
`importlib.import_module` for dynamic imports of custom PDB classes and warning
categories.
- [#968](https://github.com/pytask-dev/pytask/pull/968) improves the performance of file
descriptor capture for tasks that produce no output.
- Documents the pytest provenance of the adapted debugging, marker, and warning
Expand Down
4 changes: 2 additions & 2 deletions src/_pytask/debugging.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from __future__ import annotations

import functools
import importlib
import pdb # noqa: T100
import sys
from typing import TYPE_CHECKING
Expand Down Expand Up @@ -173,8 +174,7 @@ def _import_pdb_cls(
modname, classname = usepdb_cls

try:
__import__(modname)
mod = sys.modules[modname]
mod = importlib.import_module(modname)

# Handle --pdbcls=pdb:pdb.Pdb (useful e.g. with pdbpp or pdbp).
parts = classname.split(".")
Expand Down
3 changes: 2 additions & 1 deletion src/_pytask/warnings_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from __future__ import annotations

import functools
import importlib
import re
import textwrap
import warnings
Expand Down Expand Up @@ -124,7 +125,7 @@ def _resolve_warning_category(category: str) -> type[Warning]:
klass = category
else:
module, _, klass = category.rpartition(".")
m = __import__(module, None, None, [klass])
m = importlib.import_module(module)
cat = getattr(m, klass)
if not issubclass(cat, Warning):
msg = f"{cat} is not a Warning subclass"
Expand Down
26 changes: 26 additions & 0 deletions tests/test_debugging.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
from __future__ import annotations

import os
import pdb # noqa: T100
import re
import sys
import textwrap
from contextlib import ExitStack as does_not_raise # noqa: N813
from types import SimpleNamespace

import click
import pytest

from _pytask.debugging import PytaskPDB
from _pytask.debugging import _pdbcls_callback
from pytask import ExitCode
from pytask import cli
Expand Down Expand Up @@ -70,6 +73,29 @@ def task_example():
_flush(child)


def test_import_pdb_cls_uses_import_module(monkeypatch):
class CustomPdb(pdb.Pdb):
pass

module = SimpleNamespace(CustomPdb=CustomPdb)
imported = []

def import_module(name):
imported.append(name)
return module

monkeypatch.setattr("importlib.import_module", import_module)
monkeypatch.setattr(
PytaskPDB, "_config", {"pdbcls": ("package.debuggers", "CustomPdb")}
)
monkeypatch.setattr(PytaskPDB, "_wrapped_pdb_cls", None)

wrapped = PytaskPDB._import_pdb_cls(None, None) # type: ignore[arg-type]

assert imported == ["package.debuggers"]
assert issubclass(wrapped, CustomPdb)


@pytest.mark.skipif(not IS_PEXPECT_INSTALLED, reason="pexpect is not installed.")
@pytest.mark.skipif(sys.platform == "win32", reason="pexpect cannot spawn on Windows.")
def test_post_mortem_on_error_w_kwargs(tmp_path):
Expand Down
21 changes: 21 additions & 0 deletions tests/test_warnings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,36 @@

import sys
import textwrap
from types import SimpleNamespace

import pytest

from _pytask.warnings_utils import _resolve_warning_category
from pytask import ExitCode
from pytask import build
from pytask import cli
from tests.conftest import run_in_subprocess


def test_resolve_warning_category_uses_import_module(monkeypatch):
class CustomWarning(Warning):
pass

module = SimpleNamespace(CustomWarning=CustomWarning)
imported = []

def import_module(name):
imported.append(name)
return module

monkeypatch.setattr("importlib.import_module", import_module)

result = _resolve_warning_category("package.warnings.CustomWarning")

assert imported == ["package.warnings"]
assert result is CustomWarning


@pytest.mark.parametrize(
"disable_warnings",
[pytest.param(True, marks=pytest.mark.filterwarnings("ignore:warning!!!")), False],
Expand Down