Skip to content

Commit ded1a04

Browse files
committed
Added support for specifying options for the pytest plugin via pytest config files
Closes #440.
1 parent 3c8d46f commit ded1a04

5 files changed

Lines changed: 121 additions & 12 deletions

File tree

docs/userguide.rst

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,24 @@ previous section). To use it, run ``pytest`` with the appropriate
157157
158158
pytest --typeguard-packages=foo.bar,xyz
159159
160-
There is currently no support for specifying a customized module finder.
160+
It is also possible to set option for the pytest plugin using pytest's own
161+
configuration. For example, here's how you might specify several options in
162+
``pyproject.toml``:
163+
164+
.. code-block:: toml
165+
166+
[tool.pytest.ini_options]
167+
typeguard-packages = """
168+
foo.bar
169+
xyz"""
170+
typeguard-debug-instrumentation = true
171+
typeguard-typecheck-fail-callback = "mypackage:failcallback"
172+
typeguard-forward-ref-policy = "ERROR"
173+
typeguard-collection-check-strategy = "ALL_ITEMS"
174+
175+
See the next section for details on how the individual options work.
176+
177+
.. note:: There is currently no support for specifying a customized module finder.
161178

162179
Setting configuration options
163180
-----------------------------

docs/versionhistory.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ This library adheres to
66

77
**UNRELEASED**
88

9+
- Added support for specifying options for the pytest plugin via pytest config files
10+
(`#440 <https://github.com/agronholm/typeguard/issues/440>`_)
911
- Avoid creating reference cycles when type checking unions
1012
- Fixed ``Optional[...]`` being removed from the AST if it was located within a
1113
subscript (`#442 <https://github.com/agronholm/typeguard/issues/442>`_)

src/typeguard/_pytest_plugin.py

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import sys
44
import warnings
5+
from typing import Any, Literal
56

67
from pytest import Config, Parser
78

@@ -12,18 +13,33 @@
1213

1314

1415
def pytest_addoption(parser: Parser) -> None:
16+
def add_ini_option(
17+
opt_type: (
18+
Literal["string", "paths", "pathlist", "args", "linelist", "bool"] | None
19+
)
20+
) -> None:
21+
parser.addini(
22+
group.options[-1].names()[0][2:],
23+
group.options[-1].attrs()["help"],
24+
opt_type,
25+
)
26+
1527
group = parser.getgroup("typeguard")
1628
group.addoption(
1729
"--typeguard-packages",
1830
action="store",
1931
help="comma separated name list of packages and modules to instrument for "
2032
"type checking, or :all: to instrument all modules loaded after typeguard",
2133
)
34+
add_ini_option("linelist")
35+
2236
group.addoption(
2337
"--typeguard-debug-instrumentation",
2438
action="store_true",
2539
help="print all instrumented code to stderr",
2640
)
41+
add_ini_option("bool")
42+
2743
group.addoption(
2844
"--typeguard-typecheck-fail-callback",
2945
action="store",
@@ -33,6 +49,8 @@ def pytest_addoption(parser: Parser) -> None:
3349
"handle a TypeCheckError"
3450
),
3551
)
52+
add_ini_option("string")
53+
3654
group.addoption(
3755
"--typeguard-forward-ref-policy",
3856
action="store",
@@ -42,21 +60,31 @@ def pytest_addoption(parser: Parser) -> None:
4260
"annotations"
4361
),
4462
)
63+
add_ini_option("string")
64+
4565
group.addoption(
4666
"--typeguard-collection-check-strategy",
4767
action="store",
4868
choices=list(CollectionCheckStrategy.__members__),
4969
help="determines how thoroughly to check collections (list, dict, etc)",
5070
)
71+
add_ini_option("string")
5172

5273

5374
def pytest_configure(config: Config) -> None:
54-
packages_option = config.getoption("typeguard_packages")
55-
if packages_option:
56-
if packages_option == ":all:":
57-
packages: list[str] | None = None
75+
def getoption(name: str) -> Any:
76+
return config.getoption(name.replace("-", "_")) or config.getini(name)
77+
78+
packages: list[str] | None = []
79+
if packages_option := config.getoption("typeguard_packages"):
80+
packages = [pkg.strip() for pkg in packages_option.split(",")]
81+
elif packages_ini := config.getini("typeguard-packages"):
82+
packages = packages_ini
83+
84+
if packages:
85+
if packages == [":all:"]:
86+
packages = None
5887
else:
59-
packages = [pkg.strip() for pkg in packages_option.split(",")]
6088
already_imported_packages = sorted(
6189
package for package in packages if package in sys.modules
6290
)
@@ -70,11 +98,11 @@ def pytest_configure(config: Config) -> None:
7098

7199
install_import_hook(packages=packages)
72100

73-
debug_option = config.getoption("typeguard_debug_instrumentation")
101+
debug_option = getoption("typeguard-debug-instrumentation")
74102
if debug_option:
75103
global_config.debug_instrumentation = True
76104

77-
fail_callback_option = config.getoption("typeguard_typecheck_fail_callback")
105+
fail_callback_option = getoption("typeguard-typecheck-fail-callback")
78106
if fail_callback_option:
79107
callback = resolve_reference(fail_callback_option)
80108
if not callable(callback):
@@ -85,14 +113,12 @@ def pytest_configure(config: Config) -> None:
85113

86114
global_config.typecheck_fail_callback = callback
87115

88-
forward_ref_policy_option = config.getoption("typeguard_forward_ref_policy")
116+
forward_ref_policy_option = getoption("typeguard-forward-ref-policy")
89117
if forward_ref_policy_option:
90118
forward_ref_policy = ForwardRefPolicy.__members__[forward_ref_policy_option]
91119
global_config.forward_ref_policy = forward_ref_policy
92120

93-
collection_check_strategy_option = config.getoption(
94-
"typeguard_collection_check_strategy"
95-
)
121+
collection_check_strategy_option = getoption("typeguard-collection-check-strategy")
96122
if collection_check_strategy_option:
97123
collection_check_strategy = CollectionCheckStrategy.__members__[
98124
collection_check_strategy_option

tests/conftest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import typing_extensions
1010

1111
version_re = re.compile(r"_py(\d)(\d)\.py$")
12+
pytest_plugins = ["pytester"]
1213

1314

1415
def pytest_ignore_collect(path, config):

tests/test_pytest_plugin.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
from textwrap import dedent
2+
3+
from pytest import Pytester
4+
5+
from typeguard import CollectionCheckStrategy, ForwardRefPolicy, config
6+
7+
8+
def test_config_options(pytester: Pytester) -> None:
9+
pytester.makepyprojecttoml(
10+
'''
11+
[tool.pytest.ini_options]
12+
typeguard-packages = """
13+
mypackage
14+
otherpackage"""
15+
typeguard-debug-instrumentation = true
16+
typeguard-typecheck-fail-callback = "mypackage:failcallback"
17+
typeguard-forward-ref-policy = "ERROR"
18+
typeguard-collection-check-strategy = "ALL_ITEMS"
19+
'''
20+
)
21+
pytester.makepyfile(
22+
mypackage=(
23+
dedent(
24+
"""
25+
def failcallback():
26+
pass
27+
"""
28+
)
29+
)
30+
)
31+
32+
pytester.plugins = ["typeguard"]
33+
pytester.syspathinsert()
34+
pytestconfig = pytester.parseconfigure()
35+
assert pytestconfig.getini("typeguard-packages") == ["mypackage", "otherpackage"]
36+
assert config.typecheck_fail_callback.__name__ == "failcallback"
37+
assert config.debug_instrumentation is True
38+
assert config.forward_ref_policy is ForwardRefPolicy.ERROR
39+
assert config.collection_check_strategy is CollectionCheckStrategy.ALL_ITEMS
40+
41+
42+
def test_commandline_options(pytester: Pytester) -> None:
43+
pytester.makepyfile(
44+
mypackage=(
45+
dedent(
46+
"""
47+
def failcallback():
48+
pass
49+
"""
50+
)
51+
)
52+
)
53+
54+
pytester.plugins = ["typeguard"]
55+
pytester.syspathinsert()
56+
pytestconfig = pytester.parseconfigure(
57+
"--typeguard-packages=mypackage,otherpackage"
58+
)
59+
assert pytestconfig.getoption("typeguard_packages") == "mypackage,otherpackage"
60+
assert config.typecheck_fail_callback.__name__ == "failcallback"
61+
assert config.debug_instrumentation is True
62+
assert config.forward_ref_policy is ForwardRefPolicy.ERROR
63+
assert config.collection_check_strategy is CollectionCheckStrategy.ALL_ITEMS

0 commit comments

Comments
 (0)