Skip to content

Commit 86df1e5

Browse files
Add --prefer-stubs=y option
1 parent 67e59ac commit 86df1e5

File tree

9 files changed

+45
-0
lines changed

9 files changed

+45
-0
lines changed

doc/whatsnew/fragments/9626.bugfix

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Add `--prefer-stubs=yes` option to opt-in to the astroid 3.2 feature
2+
that prefers `.pyi` stubs over same-named `.py` files. This has the
3+
potential to reduce `no-member` errors but at the cost of more errors
4+
such as `not-an-iterable` from function bodies appearing as `...`.
5+
6+
Defaults to `no`.
7+
8+
Closes #9626
9+
Closes #9623

examples/pylintrc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ load-plugins=
8787
# Pickle collected data for later comparisons.
8888
persistent=yes
8989

90+
# Resolve imports to .pyi stubs if available. May reduce no-member messages
91+
# and increase not-an-iterable messages.
92+
prefer-stubs=no
93+
9094
# Minimum Python version to use for version dependent checks. Will default to
9195
# the version used to run pylint.
9296
py-version=3.10

examples/pyproject.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ limit-inference-results = 100
7777
# Pickle collected data for later comparisons.
7878
persistent = true
7979

80+
# Resolve imports to .pyi stubs if available. May reduce no-member messages
81+
# and increase not-an-iterable messages.
82+
prefer-stubs = false
83+
8084
# Minimum Python version to use for version dependent checks. Will default to the
8185
# version used to run pylint.
8286
py-version = "3.10"

pylint/lint/base_options.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,17 @@ def _make_linter_options(linter: PyLinter) -> Options:
415415
"Useful if running pylint in a server-like mode.",
416416
},
417417
),
418+
(
419+
"prefer-stubs",
420+
{
421+
"default": False,
422+
"type": "yn",
423+
"metavar": "<y or n>",
424+
"help": "Resolve imports to .pyi stubs if available. May "
425+
"reduce no-member messages and increase not-an-iterable "
426+
"messages.",
427+
},
428+
),
418429
)
419430

420431

pylint/lint/pylinter.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,6 +1074,7 @@ def open(self) -> None:
10741074
MANAGER.max_inferable_values = self.config.limit_inference_results
10751075
MANAGER.extension_package_whitelist.update(self.config.extension_pkg_allow_list)
10761076
MANAGER.module_denylist.update(self.config.ignored_modules)
1077+
MANAGER.prefer_stubs = self.config.prefer_stubs
10771078
if self.config.extension_pkg_whitelist:
10781079
MANAGER.extension_package_whitelist.update(
10791080
self.config.extension_pkg_whitelist

pylint/utils/utils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
"suggestion-mode",
5353
"analyse-fallback-blocks",
5454
"allow-global-unused-variables",
55+
"prefer-stubs",
5556
]
5657
GLOBAL_OPTION_INT = Literal["max-line-length", "docstring-min-length"]
5758
GLOBAL_OPTION_LIST = Literal["ignored-modules"]
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""If .pyi stubs are NOT preferred, no diagnostics emitted."""
2+
import more_itertools
3+
4+
for val in more_itertools.chunked([1, 2, 3], n=1):
5+
print(val)

tests/lint/test_pylinter.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,12 @@ def test_open_pylinter_denied_modules(linter: PyLinter) -> None:
5959
assert MANAGER.module_denylist == {"mod1", "mod2", "mod3"}
6060
finally:
6161
MANAGER.module_denylist = set()
62+
63+
64+
def test_open_pylinter_prefer_stubs(linter: PyLinter) -> None:
65+
try:
66+
linter.config.prefer_stubs = True
67+
linter.open()
68+
assert MANAGER.prefer_stubs
69+
finally:
70+
MANAGER.prefer_stubs = False

tests/test_functional.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ def revert_stateful_config_changes(linter: PyLinter) -> Iterator[PyLinter]:
5151
yield linter
5252
# Revert any stateful configuration changes.
5353
MANAGER.brain["module_denylist"] = set()
54+
MANAGER.brain["prefer_stubs"] = False
5455

5556

5657
@pytest.mark.usefixtures("revert_stateful_config_changes")

0 commit comments

Comments
 (0)