-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Add type annotations to _pytest.compat and _pytest._code.code #6205
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f760356
c7a83a0
04d68fb
5bfe793
e3ac44d
307add0
a649f15
562d481
eaa34a9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1074,13 +1074,14 @@ def try_makedirs(cache_dir) -> bool: | |
|
||
def get_cache_dir(file_path: Path) -> Path: | ||
"""Returns the cache directory to write .pyc files for the given .py file path""" | ||
if sys.version_info >= (3, 8) and sys.pycache_prefix: | ||
# Type ignored until added in next mypy release. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be good to link an issue in general, but ok for now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mypy can detect outdated |
||
if sys.version_info >= (3, 8) and sys.pycache_prefix: # type: ignore | ||
# given: | ||
# prefix = '/tmp/pycs' | ||
# path = '/home/user/proj/test_app.py' | ||
# we want: | ||
# '/tmp/pycs/home/user/proj' | ||
return Path(sys.pycache_prefix) / Path(*file_path.parts[1:-1]) | ||
return Path(sys.pycache_prefix) / Path(*file_path.parts[1:-1]) # type: ignore | ||
else: | ||
# classic pycache directory | ||
return file_path.parent / "__pycache__" |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,11 +10,14 @@ | |
from contextlib import contextmanager | ||
from inspect import Parameter | ||
from inspect import signature | ||
from typing import Any | ||
from typing import Callable | ||
from typing import Generic | ||
from typing import Optional | ||
from typing import overload | ||
from typing import Tuple | ||
from typing import TypeVar | ||
from typing import Union | ||
|
||
import attr | ||
import py | ||
|
@@ -40,12 +43,13 @@ | |
|
||
|
||
if sys.version_info >= (3, 8): | ||
from importlib import metadata as importlib_metadata # noqa: F401 | ||
# Type ignored until next mypy release. | ||
from importlib import metadata as importlib_metadata # type: ignore | ||
else: | ||
import importlib_metadata # noqa: F401 | ||
|
||
|
||
def _format_args(func): | ||
def _format_args(func: Callable[..., Any]) -> str: | ||
return str(signature(func)) | ||
|
||
|
||
|
@@ -66,12 +70,12 @@ def fspath(p): | |
fspath = os.fspath | ||
|
||
|
||
def is_generator(func): | ||
def is_generator(func: object) -> bool: | ||
genfunc = inspect.isgeneratorfunction(func) | ||
return genfunc and not iscoroutinefunction(func) | ||
|
||
|
||
def iscoroutinefunction(func): | ||
def iscoroutinefunction(func: object) -> bool: | ||
""" | ||
Return True if func is a coroutine function (a function defined with async | ||
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 | |
return inspect.iscoroutinefunction(func) or getattr(func, "_is_coroutine", False) | ||
|
||
|
||
def getlocation(function, curdir=None): | ||
def getlocation(function, curdir=None) -> str: | ||
function = get_real_func(function) | ||
fn = py.path.local(inspect.getfile(function)) | ||
lineno = function.__code__.co_firstlineno | ||
|
@@ -93,7 +97,7 @@ def getlocation(function, curdir=None): | |
return "%s:%d" % (fn, lineno + 1) | ||
|
||
|
||
def num_mock_patch_args(function): | ||
def num_mock_patch_args(function) -> int: | ||
""" return number of arguments used up by mock arguments (if any) """ | ||
patchings = getattr(function, "patchings", None) | ||
if not patchings: | ||
|
@@ -112,7 +116,13 @@ def num_mock_patch_args(function): | |
) | ||
|
||
|
||
def getfuncargnames(function, *, name: str = "", is_method=False, cls=None): | ||
def getfuncargnames( | ||
function: Callable[..., Any], | ||
*, | ||
name: str = "", | ||
is_method: bool = False, | ||
cls: Optional[type] = None | ||
) -> Tuple[str, ...]: | ||
"""Returns the names of a function's mandatory arguments. | ||
|
||
This should return the names of all function arguments that: | ||
|
@@ -180,7 +190,7 @@ def nullcontext(): | |
from contextlib import nullcontext # noqa | ||
|
||
|
||
def get_default_arg_names(function): | ||
def get_default_arg_names(function: Callable[..., Any]) -> Tuple[str, ...]: | ||
# Note: this code intentionally mirrors the code at the beginning of getfuncargnames, | ||
# to get the arguments which were excluded from its result because they had default values | ||
return tuple( | ||
|
@@ -199,18 +209,18 @@ def get_default_arg_names(function): | |
) | ||
|
||
|
||
def _translate_non_printable(s): | ||
def _translate_non_printable(s: str) -> str: | ||
return s.translate(_non_printable_ascii_translate_table) | ||
|
||
|
||
STRING_TYPES = bytes, str | ||
|
||
|
||
def _bytes_to_ascii(val): | ||
def _bytes_to_ascii(val: bytes) -> str: | ||
return val.decode("ascii", "backslashreplace") | ||
|
||
|
||
def ascii_escaped(val): | ||
def ascii_escaped(val: Union[bytes, str]): | ||
"""If val is pure ascii, returns it as a str(). Otherwise, escapes | ||
bytes objects into a sequence of escaped bytes: | ||
|
||
|
@@ -307,7 +317,7 @@ def getimfunc(func): | |
return func | ||
|
||
|
||
def safe_getattr(object, name, default): | ||
def safe_getattr(object: Any, name: str, default: Any) -> Any: | ||
""" Like getattr but return default upon any Exception or any OutcomeException. | ||
|
||
Attribute access can potentially fail for 'evil' Python objects. | ||
|
@@ -321,7 +331,7 @@ def safe_getattr(object, name, default): | |
return default | ||
|
||
|
||
def safe_isclass(obj): | ||
def safe_isclass(obj: object) -> bool: | ||
"""Ignore any exception via isinstance on Python 3.""" | ||
try: | ||
return inspect.isclass(obj) | ||
|
@@ -342,39 +352,26 @@ def safe_isclass(obj): | |
) | ||
|
||
|
||
def _setup_collect_fakemodule(): | ||
def _setup_collect_fakemodule() -> None: | ||
from types import ModuleType | ||
import pytest | ||
|
||
pytest.collect = ModuleType("pytest.collect") | ||
pytest.collect.__all__ = [] # used for setns | ||
# Types ignored because the module is created dynamically. | ||
pytest.collect = ModuleType("pytest.collect") # type: ignore | ||
pytest.collect.__all__ = [] # type: ignore # used for setns | ||
for attr_name in COLLECT_FAKEMODULE_ATTRIBUTES: | ||
setattr(pytest.collect, attr_name, getattr(pytest, attr_name)) | ||
setattr(pytest.collect, attr_name, getattr(pytest, attr_name)) # type: ignore | ||
|
||
|
||
class CaptureIO(io.TextIOWrapper): | ||
def __init__(self): | ||
def __init__(self) -> None: | ||
super().__init__(io.BytesIO(), encoding="UTF-8", newline="", write_through=True) | ||
|
||
def getvalue(self): | ||
def getvalue(self) -> str: | ||
assert isinstance(self.buffer, io.BytesIO) | ||
return self.buffer.getvalue().decode("UTF-8") | ||
|
||
|
||
class FuncargnamesCompatAttr: | ||
""" helper class so that Metafunc, Function and FixtureRequest | ||
don't need to each define the "funcargnames" compatibility attribute. | ||
""" | ||
|
||
@property | ||
def funcargnames(self): | ||
""" alias attribute for ``fixturenames`` for pre-2.3 compatibility""" | ||
import warnings | ||
from _pytest.deprecated import FUNCARGNAMES | ||
|
||
warnings.warn(FUNCARGNAMES, stacklevel=2) | ||
return self.fixturenames | ||
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm ok with inlining, but wondered how it made typing harder (from the commit message)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a mixin class, it uses There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed. |
||
if sys.version_info < (3, 5, 2): # pragma: no cover | ||
|
||
def overload(f): # noqa: F811 | ||
|
@@ -407,7 +404,9 @@ def __get__( | |
raise NotImplementedError() | ||
|
||
@overload # noqa: F811 | ||
def __get__(self, instance: _S, owner: Optional["Type[_S]"] = ...) -> _T: | ||
def __get__( # noqa: F811 | ||
self, instance: _S, owner: Optional["Type[_S]"] = ... | ||
) -> _T: | ||
raise NotImplementedError() | ||
|
||
def __get__(self, instance, owner=None): # noqa: F811 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There seems to be a discrepancy between where CI flake8 and my flake8 report this - on the decorator or the
def
. Maybe because of Python 3.8? So for now I add it to both.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very likely, yes.
Isn't there a flake8 plugin to handle this automatically maybe?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There seems to be some discussion regarding this here, I'm too lazy to read through it though...