Skip to content

Commit fa578d7

Browse files
authored
Merge pull request #6205 from bluetech/type-annotations-8
Add type annotations to _pytest.compat and _pytest._code.code
2 parents e379604 + eaa34a9 commit fa578d7

File tree

12 files changed

+380
-325
lines changed

12 files changed

+380
-325
lines changed

src/_pytest/_code/code.py

Lines changed: 153 additions & 91 deletions
Large diffs are not rendered by default.

src/_pytest/_code/source.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from ast import PyCF_ONLY_AST as _AST_FLAG
99
from bisect import bisect_right
1010
from types import FrameType
11+
from typing import Iterator
1112
from typing import List
1213
from typing import Optional
1314
from typing import Sequence
@@ -60,7 +61,7 @@ def __getitem__(self, key: int) -> str:
6061
raise NotImplementedError()
6162

6263
@overload # noqa: F811
63-
def __getitem__(self, key: slice) -> "Source":
64+
def __getitem__(self, key: slice) -> "Source": # noqa: F811
6465
raise NotImplementedError()
6566

6667
def __getitem__(self, key: Union[int, slice]) -> Union[str, "Source"]: # noqa: F811
@@ -73,6 +74,9 @@ def __getitem__(self, key: Union[int, slice]) -> Union[str, "Source"]: # noqa:
7374
newsource.lines = self.lines[key.start : key.stop]
7475
return newsource
7576

77+
def __iter__(self) -> Iterator[str]:
78+
return iter(self.lines)
79+
7680
def __len__(self) -> int:
7781
return len(self.lines)
7882

src/_pytest/assertion/rewrite.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,13 +1074,14 @@ def try_makedirs(cache_dir) -> bool:
10741074

10751075
def get_cache_dir(file_path: Path) -> Path:
10761076
"""Returns the cache directory to write .pyc files for the given .py file path"""
1077-
if sys.version_info >= (3, 8) and sys.pycache_prefix:
1077+
# Type ignored until added in next mypy release.
1078+
if sys.version_info >= (3, 8) and sys.pycache_prefix: # type: ignore
10781079
# given:
10791080
# prefix = '/tmp/pycs'
10801081
# path = '/home/user/proj/test_app.py'
10811082
# we want:
10821083
# '/tmp/pycs/home/user/proj'
1083-
return Path(sys.pycache_prefix) / Path(*file_path.parts[1:-1])
1084+
return Path(sys.pycache_prefix) / Path(*file_path.parts[1:-1]) # type: ignore
10841085
else:
10851086
# classic pycache directory
10861087
return file_path.parent / "__pycache__"

src/_pytest/compat.py

Lines changed: 34 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@
1010
from contextlib import contextmanager
1111
from inspect import Parameter
1212
from inspect import signature
13+
from typing import Any
1314
from typing import Callable
1415
from typing import Generic
1516
from typing import Optional
1617
from typing import overload
18+
from typing import Tuple
1719
from typing import TypeVar
20+
from typing import Union
1821

1922
import attr
2023
import py
@@ -40,12 +43,13 @@
4043

4144

4245
if sys.version_info >= (3, 8):
43-
from importlib import metadata as importlib_metadata # noqa: F401
46+
# Type ignored until next mypy release.
47+
from importlib import metadata as importlib_metadata # type: ignore
4448
else:
4549
import importlib_metadata # noqa: F401
4650

4751

48-
def _format_args(func):
52+
def _format_args(func: Callable[..., Any]) -> str:
4953
return str(signature(func))
5054

5155

@@ -66,12 +70,12 @@ def fspath(p):
6670
fspath = os.fspath
6771

6872

69-
def is_generator(func):
73+
def is_generator(func: object) -> bool:
7074
genfunc = inspect.isgeneratorfunction(func)
7175
return genfunc and not iscoroutinefunction(func)
7276

7377

74-
def iscoroutinefunction(func):
78+
def iscoroutinefunction(func: object) -> bool:
7579
"""
7680
Return True if func is a coroutine function (a function defined with async
7781
def syntax, and doesn't contain yield), or a function decorated with
@@ -84,7 +88,7 @@ def syntax, and doesn't contain yield), or a function decorated with
8488
return inspect.iscoroutinefunction(func) or getattr(func, "_is_coroutine", False)
8589

8690

87-
def getlocation(function, curdir=None):
91+
def getlocation(function, curdir=None) -> str:
8892
function = get_real_func(function)
8993
fn = py.path.local(inspect.getfile(function))
9094
lineno = function.__code__.co_firstlineno
@@ -93,7 +97,7 @@ def getlocation(function, curdir=None):
9397
return "%s:%d" % (fn, lineno + 1)
9498

9599

96-
def num_mock_patch_args(function):
100+
def num_mock_patch_args(function) -> int:
97101
""" return number of arguments used up by mock arguments (if any) """
98102
patchings = getattr(function, "patchings", None)
99103
if not patchings:
@@ -112,7 +116,13 @@ def num_mock_patch_args(function):
112116
)
113117

114118

115-
def getfuncargnames(function, *, name: str = "", is_method=False, cls=None):
119+
def getfuncargnames(
120+
function: Callable[..., Any],
121+
*,
122+
name: str = "",
123+
is_method: bool = False,
124+
cls: Optional[type] = None
125+
) -> Tuple[str, ...]:
116126
"""Returns the names of a function's mandatory arguments.
117127
118128
This should return the names of all function arguments that:
@@ -180,7 +190,7 @@ def nullcontext():
180190
from contextlib import nullcontext # noqa
181191

182192

183-
def get_default_arg_names(function):
193+
def get_default_arg_names(function: Callable[..., Any]) -> Tuple[str, ...]:
184194
# Note: this code intentionally mirrors the code at the beginning of getfuncargnames,
185195
# to get the arguments which were excluded from its result because they had default values
186196
return tuple(
@@ -199,18 +209,18 @@ def get_default_arg_names(function):
199209
)
200210

201211

202-
def _translate_non_printable(s):
212+
def _translate_non_printable(s: str) -> str:
203213
return s.translate(_non_printable_ascii_translate_table)
204214

205215

206216
STRING_TYPES = bytes, str
207217

208218

209-
def _bytes_to_ascii(val):
219+
def _bytes_to_ascii(val: bytes) -> str:
210220
return val.decode("ascii", "backslashreplace")
211221

212222

213-
def ascii_escaped(val):
223+
def ascii_escaped(val: Union[bytes, str]):
214224
"""If val is pure ascii, returns it as a str(). Otherwise, escapes
215225
bytes objects into a sequence of escaped bytes:
216226
@@ -307,7 +317,7 @@ def getimfunc(func):
307317
return func
308318

309319

310-
def safe_getattr(object, name, default):
320+
def safe_getattr(object: Any, name: str, default: Any) -> Any:
311321
""" Like getattr but return default upon any Exception or any OutcomeException.
312322
313323
Attribute access can potentially fail for 'evil' Python objects.
@@ -321,7 +331,7 @@ def safe_getattr(object, name, default):
321331
return default
322332

323333

324-
def safe_isclass(obj):
334+
def safe_isclass(obj: object) -> bool:
325335
"""Ignore any exception via isinstance on Python 3."""
326336
try:
327337
return inspect.isclass(obj)
@@ -342,39 +352,26 @@ def safe_isclass(obj):
342352
)
343353

344354

345-
def _setup_collect_fakemodule():
355+
def _setup_collect_fakemodule() -> None:
346356
from types import ModuleType
347357
import pytest
348358

349-
pytest.collect = ModuleType("pytest.collect")
350-
pytest.collect.__all__ = [] # used for setns
359+
# Types ignored because the module is created dynamically.
360+
pytest.collect = ModuleType("pytest.collect") # type: ignore
361+
pytest.collect.__all__ = [] # type: ignore # used for setns
351362
for attr_name in COLLECT_FAKEMODULE_ATTRIBUTES:
352-
setattr(pytest.collect, attr_name, getattr(pytest, attr_name))
363+
setattr(pytest.collect, attr_name, getattr(pytest, attr_name)) # type: ignore
353364

354365

355366
class CaptureIO(io.TextIOWrapper):
356-
def __init__(self):
367+
def __init__(self) -> None:
357368
super().__init__(io.BytesIO(), encoding="UTF-8", newline="", write_through=True)
358369

359-
def getvalue(self):
370+
def getvalue(self) -> str:
371+
assert isinstance(self.buffer, io.BytesIO)
360372
return self.buffer.getvalue().decode("UTF-8")
361373

362374

363-
class FuncargnamesCompatAttr:
364-
""" helper class so that Metafunc, Function and FixtureRequest
365-
don't need to each define the "funcargnames" compatibility attribute.
366-
"""
367-
368-
@property
369-
def funcargnames(self):
370-
""" alias attribute for ``fixturenames`` for pre-2.3 compatibility"""
371-
import warnings
372-
from _pytest.deprecated import FUNCARGNAMES
373-
374-
warnings.warn(FUNCARGNAMES, stacklevel=2)
375-
return self.fixturenames
376-
377-
378375
if sys.version_info < (3, 5, 2): # pragma: no cover
379376

380377
def overload(f): # noqa: F811
@@ -407,7 +404,9 @@ def __get__(
407404
raise NotImplementedError()
408405

409406
@overload # noqa: F811
410-
def __get__(self, instance: _S, owner: Optional["Type[_S]"] = ...) -> _T:
407+
def __get__( # noqa: F811
408+
self, instance: _S, owner: Optional["Type[_S]"] = ...
409+
) -> _T:
411410
raise NotImplementedError()
412411

413412
def __get__(self, instance, owner=None): # noqa: F811

src/_pytest/fixtures.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
from _pytest._code.code import TerminalRepr
1919
from _pytest.compat import _format_args
2020
from _pytest.compat import _PytestWrapper
21-
from _pytest.compat import FuncargnamesCompatAttr
2221
from _pytest.compat import get_real_func
2322
from _pytest.compat import get_real_method
2423
from _pytest.compat import getfslineno
@@ -29,6 +28,7 @@
2928
from _pytest.compat import NOTSET
3029
from _pytest.compat import safe_getattr
3130
from _pytest.deprecated import FIXTURE_POSITIONAL_ARGUMENTS
31+
from _pytest.deprecated import FUNCARGNAMES
3232
from _pytest.outcomes import fail
3333
from _pytest.outcomes import TEST_OUTCOME
3434

@@ -336,7 +336,7 @@ def prune_dependency_tree(self):
336336
self.names_closure[:] = sorted(closure, key=self.names_closure.index)
337337

338338

339-
class FixtureRequest(FuncargnamesCompatAttr):
339+
class FixtureRequest:
340340
""" A request for a fixture from a test or fixture function.
341341
342342
A request object gives access to the requesting test context
@@ -363,6 +363,12 @@ def fixturenames(self):
363363
result.extend(set(self._fixture_defs).difference(result))
364364
return result
365365

366+
@property
367+
def funcargnames(self):
368+
""" alias attribute for ``fixturenames`` for pre-2.3 compatibility"""
369+
warnings.warn(FUNCARGNAMES, stacklevel=2)
370+
return self.fixturenames
371+
366372
@property
367373
def node(self):
368374
""" underlying collection node (depends on current request scope)"""

src/_pytest/python.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
from _pytest.compat import safe_isclass
3232
from _pytest.compat import STRING_TYPES
3333
from _pytest.config import hookimpl
34+
from _pytest.deprecated import FUNCARGNAMES
3435
from _pytest.main import FSHookProxy
3536
from _pytest.mark import MARK_GEN
3637
from _pytest.mark.structures import get_unpacked_marks
@@ -882,7 +883,7 @@ def setmulti2(self, valtypes, argnames, valset, id, marks, scopenum, param_index
882883
self.marks.extend(normalize_mark_list(marks))
883884

884885

885-
class Metafunc(fixtures.FuncargnamesCompatAttr):
886+
class Metafunc:
886887
"""
887888
Metafunc objects are passed to the :func:`pytest_generate_tests <_pytest.hookspec.pytest_generate_tests>` hook.
888889
They help to inspect a test function and to generate tests according to
@@ -916,6 +917,12 @@ def __init__(self, definition, fixtureinfo, config, cls=None, module=None):
916917
self._ids = set()
917918
self._arg2fixturedefs = fixtureinfo.name2fixturedefs
918919

920+
@property
921+
def funcargnames(self):
922+
""" alias attribute for ``fixturenames`` for pre-2.3 compatibility"""
923+
warnings.warn(FUNCARGNAMES, stacklevel=2)
924+
return self.fixturenames
925+
919926
def parametrize(self, argnames, argvalues, indirect=False, ids=None, scope=None):
920927
""" Add new invocations to the underlying test function using the list
921928
of argvalues for the given argnames. Parametrization is performed
@@ -1333,7 +1340,7 @@ def write_docstring(tw, doc, indent=" "):
13331340
tw.write(indent + line + "\n")
13341341

13351342

1336-
class Function(FunctionMixin, nodes.Item, fixtures.FuncargnamesCompatAttr):
1343+
class Function(FunctionMixin, nodes.Item):
13371344
""" a Function Item is responsible for setting up and executing a
13381345
Python test function.
13391346
"""
@@ -1420,6 +1427,12 @@ def _pyfuncitem(self):
14201427
"(compatonly) for code expecting pytest-2.2 style request objects"
14211428
return self
14221429

1430+
@property
1431+
def funcargnames(self):
1432+
""" alias attribute for ``fixturenames`` for pre-2.3 compatibility"""
1433+
warnings.warn(FUNCARGNAMES, stacklevel=2)
1434+
return self.fixturenames
1435+
14231436
def runtest(self):
14241437
""" execute the underlying test function. """
14251438
self.ihook.pytest_pyfunc_call(pyfuncitem=self)

src/_pytest/python_api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ def raises(
552552

553553

554554
@overload # noqa: F811
555-
def raises(
555+
def raises( # noqa: F811
556556
expected_exception: Union["Type[_E]", Tuple["Type[_E]", ...]],
557557
func: Callable,
558558
*args: Any,

src/_pytest/recwarn.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,18 +60,18 @@ def warns(
6060
*,
6161
match: "Optional[Union[str, Pattern]]" = ...
6262
) -> "WarningsChecker":
63-
... # pragma: no cover
63+
raise NotImplementedError()
6464

6565

6666
@overload # noqa: F811
67-
def warns(
67+
def warns( # noqa: F811
6868
expected_warning: Union["Type[Warning]", Tuple["Type[Warning]", ...]],
6969
func: Callable,
7070
*args: Any,
7171
match: Optional[Union[str, "Pattern"]] = ...,
7272
**kwargs: Any
7373
) -> Union[Any]:
74-
... # pragma: no cover
74+
raise NotImplementedError()
7575

7676

7777
def warns( # noqa: F811

0 commit comments

Comments
 (0)