[pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci
This commit is contained in:
pre-commit-ci[bot] 2024-04-13 00:00:18 +00:00
parent 72ad6dc953
commit f4cd1ba0d6
813 changed files with 66015 additions and 58839 deletions

View file

@ -1,10 +1,9 @@
# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
"""Control of and utilities for debugging."""
from __future__ import annotations
import _thread
import atexit
import contextlib
import functools
@ -17,15 +16,18 @@ import reprlib
import sys
import traceback
import types
import _thread
from typing import Any
from typing import Callable
from typing import IO
from typing import Iterable
from typing import Iterator
from typing import Mapping
from typing import overload
from typing import (
overload,
Any, Callable, IO, Iterable, Iterator, Mapping,
)
from coverage.misc import human_sorted_items, isolate_module
from coverage.types import AnyCallable, TWritable
from coverage.misc import human_sorted_items
from coverage.misc import isolate_module
from coverage.types import AnyCallable
from coverage.types import TWritable
os = isolate_module(os)
@ -53,12 +55,12 @@ class DebugControl:
self.suppress_callers = False
filters = []
if self.should("process"):
if self.should('process'):
filters.append(CwdTracker().filter)
filters.append(ProcessTracker().filter)
if self.should("pytest"):
if self.should('pytest'):
filters.append(PytestTracker().filter)
if self.should("pid"):
if self.should('pid'):
filters.append(add_pid_and_tid)
self.output = DebugOutputFile.get_one(
@ -69,11 +71,11 @@ class DebugControl:
self.raw_output = self.output.outfile
def __repr__(self) -> str:
return f"<DebugControl options={self.options!r} raw_output={self.raw_output!r}>"
return f'<DebugControl options={self.options!r} raw_output={self.raw_output!r}>'
def should(self, option: str) -> bool:
"""Decide whether to output debug information in category `option`."""
if option == "callers" and self.suppress_callers:
if option == 'callers' and self.suppress_callers:
return False
return (option in self.options)
@ -96,20 +98,21 @@ class DebugControl:
after the message.
"""
self.output.write(msg + "\n")
self.output.write(msg + '\n')
if exc is not None:
self.output.write("".join(traceback.format_exception(None, exc, exc.__traceback__)))
if self.should("self"):
caller_self = inspect.stack()[1][0].f_locals.get("self")
self.output.write(''.join(traceback.format_exception(None, exc, exc.__traceback__)))
if self.should('self'):
caller_self = inspect.stack()[1][0].f_locals.get('self')
if caller_self is not None:
self.output.write(f"self: {caller_self!r}\n")
if self.should("callers"):
self.output.write(f'self: {caller_self!r}\n')
if self.should('callers'):
dump_stack_frames(out=self.output, skip=1)
self.output.flush()
class NoDebugging(DebugControl):
"""A replacement for DebugControl that will never try to do anything."""
def __init__(self) -> None:
# pylint: disable=super-init-not-called
...
@ -120,12 +123,12 @@ class NoDebugging(DebugControl):
def write(self, msg: str, *, exc: BaseException | None = None) -> None:
"""This will never be called."""
raise AssertionError("NoDebugging.write should never be called.")
raise AssertionError('NoDebugging.write should never be called.')
def info_header(label: str) -> str:
"""Make a nice header string."""
return "--{:-<60s}".format(" "+label+" ")
return '--{:-<60s}'.format(' ' + label + ' ')
def info_formatter(info: Iterable[tuple[str, Any]]) -> Iterator[str]:
@ -142,17 +145,17 @@ def info_formatter(info: Iterable[tuple[str, Any]]) -> Iterator[str]:
assert all(len(l) < label_len for l, _ in info)
for label, data in info:
if data == []:
data = "-none-"
data = '-none-'
if isinstance(data, tuple) and len(repr(tuple(data))) < 30:
# Convert to tuple to scrub namedtuples.
yield "%*s: %r" % (label_len, label, tuple(data))
yield '%*s: %r' % (label_len, label, tuple(data))
elif isinstance(data, (list, set, tuple)):
prefix = "%*s:" % (label_len, label)
prefix = '%*s:' % (label_len, label)
for e in data:
yield "%*s %s" % (label_len+1, prefix, e)
prefix = ""
yield '%*s %s' % (label_len + 1, prefix, e)
prefix = ''
else:
yield "%*s: %s" % (label_len, label, data)
yield '%*s: %s' % (label_len, label, data)
def write_formatted_info(
@ -170,35 +173,38 @@ def write_formatted_info(
"""
write(info_header(header))
for line in info_formatter(info):
write(f" {line}")
write(f' {line}')
def exc_one_line(exc: Exception) -> str:
"""Get a one-line summary of an exception, including class name and message."""
lines = traceback.format_exception_only(type(exc), exc)
return "|".join(l.rstrip() for l in lines)
return '|'.join(l.rstrip() for l in lines)
_FILENAME_REGEXES: list[tuple[str, str]] = [
(r".*[/\\]pytest-of-.*[/\\]pytest-\d+([/\\]popen-gw\d+)?", "tmp:"),
(r'.*[/\\]pytest-of-.*[/\\]pytest-\d+([/\\]popen-gw\d+)?', 'tmp:'),
]
_FILENAME_SUBS: list[tuple[str, str]] = []
@overload
def short_filename(filename: str) -> str:
pass
@overload
def short_filename(filename: None) -> None:
pass
def short_filename(filename: str | None) -> str | None:
"""Shorten a file name. Directories are replaced by prefixes like 'syspath:'"""
if not _FILENAME_SUBS:
for pathdir in sys.path:
_FILENAME_SUBS.append((pathdir, "syspath:"))
_FILENAME_SUBS.append((pathdir, 'syspath:'))
import coverage
_FILENAME_SUBS.append((os.path.dirname(coverage.__file__), "cov:"))
_FILENAME_SUBS.append((os.path.dirname(coverage.__file__), 'cov:'))
_FILENAME_SUBS.sort(key=(lambda pair: len(pair[0])), reverse=True)
if filename is not None:
for pat, sub in _FILENAME_REGEXES:
@ -237,9 +243,9 @@ def short_stack(
"""
# Regexes in initial frames that we don't care about.
BORING_PRELUDE = [
"<string>", # pytest-xdist has string execution.
r"\bigor.py$", # Our test runner.
r"\bsite-packages\b", # pytest etc getting to our tests.
'<string>', # pytest-xdist has string execution.
r'\bigor.py$', # Our test runner.
r'\bsite-packages\b', # pytest etc getting to our tests.
]
stack: Iterable[inspect.FrameInfo] = inspect.stack()[:skip:-1]
@ -251,20 +257,20 @@ def short_stack(
)
lines = []
for frame_info in stack:
line = f"{frame_info.function:>30s} : "
line = f'{frame_info.function:>30s} : '
if frame_ids:
line += f"{id(frame_info.frame):#x} "
line += f'{id(frame_info.frame):#x} '
filename = frame_info.filename
if short_filenames:
filename = short_filename(filename)
line += f"{filename}:{frame_info.lineno}"
line += f'{filename}:{frame_info.lineno}'
lines.append(line)
return "\n".join(lines)
return '\n'.join(lines)
def dump_stack_frames(out: TWritable, skip: int = 0) -> None:
"""Print a summary of the stack to `out`."""
out.write(short_stack(skip=skip+1) + "\n")
out.write(short_stack(skip=skip + 1) + '\n')
def clipped_repr(text: str, numchars: int = 50) -> str:
@ -285,36 +291,37 @@ def short_id(id64: int) -> int:
def add_pid_and_tid(text: str) -> str:
"""A filter to add pid and tid to debug messages."""
# Thread ids are useful, but too long. Make a shorter one.
tid = f"{short_id(_thread.get_ident()):04x}"
text = f"{os.getpid():5d}.{tid}: {text}"
tid = f'{short_id(_thread.get_ident()):04x}'
text = f'{os.getpid():5d}.{tid}: {text}'
return text
AUTO_REPR_IGNORE = {"$coverage.object_id"}
AUTO_REPR_IGNORE = {'$coverage.object_id'}
def auto_repr(self: Any) -> str:
"""A function implementing an automatic __repr__ for debugging."""
show_attrs = (
(k, v) for k, v in self.__dict__.items()
if getattr(v, "show_repr_attr", True)
and not inspect.ismethod(v)
and k not in AUTO_REPR_IGNORE
if getattr(v, 'show_repr_attr', True) and
not inspect.ismethod(v) and
k not in AUTO_REPR_IGNORE
)
return "<{klass} @{id:#x}{attrs}>".format(
return '<{klass} @{id:#x}{attrs}>'.format(
klass=self.__class__.__name__,
id=id(self),
attrs="".join(f" {k}={v!r}" for k, v in show_attrs),
attrs=''.join(f' {k}={v!r}' for k, v in show_attrs),
)
def simplify(v: Any) -> Any: # pragma: debugging
"""Turn things which are nearly dict/list/etc into dict/list/etc."""
if isinstance(v, dict):
return {k:simplify(vv) for k, vv in v.items()}
return {k: simplify(vv) for k, vv in v.items()}
elif isinstance(v, (list, tuple)):
return type(v)(simplify(vv) for vv in v)
elif hasattr(v, "__dict__"):
return simplify({"."+k: v for k, v in v.__dict__.items()})
elif hasattr(v, '__dict__'):
return simplify({'.' + k: v for k, v in v.__dict__.items()})
else:
return v
@ -343,12 +350,13 @@ def filter_text(text: str, filters: Iterable[Callable[[str], str]]) -> str:
lines = []
for line in text.splitlines():
lines.extend(filter_fn(line).splitlines())
text = "\n".join(lines)
text = '\n'.join(lines)
return text + ending
class CwdTracker:
"""A class to add cwd info to debug messages."""
def __init__(self) -> None:
self.cwd: str | None = None
@ -356,32 +364,33 @@ class CwdTracker:
"""Add a cwd message for each new cwd."""
cwd = os.getcwd()
if cwd != self.cwd:
text = f"cwd is now {cwd!r}\n" + text
text = f'cwd is now {cwd!r}\n' + text
self.cwd = cwd
return text
class ProcessTracker:
"""Track process creation for debug logging."""
def __init__(self) -> None:
self.pid: int = os.getpid()
self.did_welcome = False
def filter(self, text: str) -> str:
"""Add a message about how new processes came to be."""
welcome = ""
welcome = ''
pid = os.getpid()
if self.pid != pid:
welcome = f"New process: forked {self.pid} -> {pid}\n"
welcome = f'New process: forked {self.pid} -> {pid}\n'
self.pid = pid
elif not self.did_welcome:
argv = getattr(sys, "argv", None)
argv = getattr(sys, 'argv', None)
welcome = (
f"New process: {pid=}, executable: {sys.executable!r}\n"
+ f"New process: cmd: {argv!r}\n"
f'New process: {pid=}, executable: {sys.executable!r}\n' +
f'New process: cmd: {argv!r}\n'
)
if hasattr(os, "getppid"):
welcome += f"New process parent pid: {os.getppid()!r}\n"
if hasattr(os, 'getppid'):
welcome += f'New process parent pid: {os.getppid()!r}\n'
if welcome:
self.did_welcome = True
@ -392,20 +401,22 @@ class ProcessTracker:
class PytestTracker:
"""Track the current pytest test name to add to debug messages."""
def __init__(self) -> None:
self.test_name: str | None = None
def filter(self, text: str) -> str:
"""Add a message when the pytest test changes."""
test_name = os.getenv("PYTEST_CURRENT_TEST")
test_name = os.getenv('PYTEST_CURRENT_TEST')
if test_name != self.test_name:
text = f"Pytest context: {test_name}\n" + text
text = f'Pytest context: {test_name}\n' + text
self.test_name = test_name
return text
class DebugOutputFile:
"""A file-like object that includes pid and cwd information."""
def __init__(
self,
outfile: IO[str] | None,
@ -444,21 +455,21 @@ class DebugOutputFile:
the_one, is_interim = cls._get_singleton_data()
if the_one is None or is_interim:
if file_name is not None:
fileobj = open(file_name, "a", encoding="utf-8")
fileobj = open(file_name, 'a', encoding='utf-8')
else:
# $set_env.py: COVERAGE_DEBUG_FILE - Where to write debug output
file_name = os.getenv("COVERAGE_DEBUG_FILE", FORCED_DEBUG_FILE)
if file_name in ("stdout", "stderr"):
file_name = os.getenv('COVERAGE_DEBUG_FILE', FORCED_DEBUG_FILE)
if file_name in ('stdout', 'stderr'):
fileobj = getattr(sys, file_name)
elif file_name:
fileobj = open(file_name, "a", encoding="utf-8")
fileobj = open(file_name, 'a', encoding='utf-8')
atexit.register(fileobj.close)
else:
fileobj = sys.stderr
the_one = cls(fileobj, filters)
cls._set_singleton_data(the_one, interim)
if not(the_one.filters):
if not (the_one.filters):
the_one.filters = list(filters)
return the_one
@ -467,8 +478,8 @@ class DebugOutputFile:
# a process-wide singleton. So stash it in sys.modules instead of
# on a class attribute. Yes, this is aggressively gross.
SYS_MOD_NAME = "$coverage.debug.DebugOutputFile.the_one"
SINGLETON_ATTR = "the_one_and_is_interim"
SYS_MOD_NAME = '$coverage.debug.DebugOutputFile.the_one'
SINGLETON_ATTR = 'the_one_and_is_interim'
@classmethod
def _set_singleton_data(cls, the_one: DebugOutputFile, interim: bool) -> None:
@ -504,7 +515,7 @@ class DebugOutputFile:
def log(msg: str, stack: bool = False) -> None: # pragma: debugging
"""Write a log message as forcefully as possible."""
out = DebugOutputFile.get_one(interim=True)
out.write(msg+"\n")
out.write(msg + '\n')
if stack:
dump_stack_frames(out=out, skip=1)
@ -519,8 +530,8 @@ def decorate_methods(
for name, meth in inspect.getmembers(cls, inspect.isroutine):
if name not in cls.__dict__:
continue
if name != "__init__":
if not private and name.startswith("_"):
if name != '__init__':
if not private and name.startswith('_'):
continue
if name in butnot:
continue
@ -542,7 +553,8 @@ def break_in_pudb(func: AnyCallable) -> AnyCallable: # pragma: debugging
OBJ_IDS = itertools.count()
CALLS = itertools.count()
OBJ_ID_ATTR = "$coverage.object_id"
OBJ_ID_ATTR = '$coverage.object_id'
def show_calls(
show_args: bool = True,
@ -555,27 +567,27 @@ def show_calls(
def _wrapper(self: Any, *args: Any, **kwargs: Any) -> Any:
oid = getattr(self, OBJ_ID_ATTR, None)
if oid is None:
oid = f"{os.getpid():08d} {next(OBJ_IDS):04d}"
oid = f'{os.getpid():08d} {next(OBJ_IDS):04d}'
setattr(self, OBJ_ID_ATTR, oid)
extra = ""
extra = ''
if show_args:
eargs = ", ".join(map(repr, args))
ekwargs = ", ".join("{}={!r}".format(*item) for item in kwargs.items())
extra += "("
eargs = ', '.join(map(repr, args))
ekwargs = ', '.join('{}={!r}'.format(*item) for item in kwargs.items())
extra += '('
extra += eargs
if eargs and ekwargs:
extra += ", "
extra += ', '
extra += ekwargs
extra += ")"
extra += ')'
if show_stack:
extra += " @ "
extra += "; ".join(short_stack(short_filenames=True).splitlines())
extra += ' @ '
extra += '; '.join(short_stack(short_filenames=True).splitlines())
callid = next(CALLS)
msg = f"{oid} {callid:04d} {func.__name__}{extra}\n"
msg = f'{oid} {callid:04d} {func.__name__}{extra}\n'
DebugOutputFile.get_one(interim=True).write(msg)
ret = func(self, *args, **kwargs)
if show_return:
msg = f"{oid} {callid:04d} {func.__name__} return {ret!r}\n"
msg = f'{oid} {callid:04d} {func.__name__} return {ret!r}\n'
DebugOutputFile.get_one(interim=True).write(msg)
return ret
return _wrapper
@ -595,9 +607,9 @@ def relevant_environment_display(env: Mapping[str, str]) -> list[tuple[str, str]
A list of pairs (name, value) to show.
"""
slugs = {"COV", "PY"}
include = {"HOME", "TEMP", "TMP"}
cloak = {"API", "TOKEN", "KEY", "SECRET", "PASS", "SIGNATURE"}
slugs = {'COV', 'PY'}
include = {'HOME', 'TEMP', 'TMP'}
cloak = {'API', 'TOKEN', 'KEY', 'SECRET', 'PASS', 'SIGNATURE'}
to_show = []
for name, val in env.items():
@ -608,6 +620,6 @@ def relevant_environment_display(env: Mapping[str, str]) -> list[tuple[str, str]
keep = True
if keep:
if any(slug in name for slug in cloak):
val = re.sub(r"\w", "*", val)
val = re.sub(r'\w', '*', val)
to_show.append((name, val))
return human_sorted_items(to_show)