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
Code coverage report seems wrong on 3.8 #409
Comments
A reproducer is a a code sample that produces the problem, not a statement that the problem does produce every time. How would a mere statement help me figure it out :) |
Sorry I don't have a minimal example that I can share with you a the moment. But by the looks of it, any piece of code is likely to reproduce the problem |
Well at least characterize your project. Does it use subprocesses? Multiprocessing? Anything out of the ordinary. |
Sure. This is part of the pyproject.toml file that could be of interest [tool.poetry.dependencies]
python = ">=3.6"
dataclasses = "*"
psutil = ">=5.7.0"
[tool.poetry.dev-dependencies]
coverage = {extras = ["toml"], version = "*"}
pytest = ">=5.4.2"
pytest-cov = ">=2.8.1"
sphinx = "^3.0.4"
sphinx-autodoc-typehints = "^1.10.3"
nox = "^2020.5.24"
mypy = "^0.770"
codecov = "^2.1.3"
[tool.poetry.urls]
issues = "https://github.com/P403n1x87/austin-python/issues"
[tool.coverage.run]
branch = true
source = ["austin"]
[tool.coverage.report]
show_missing = true The project uses subprocesses together with asyncio and threading. The content of from argparse import ArgumentParser, Namespace, REMAINDER
from typing import Any, List, NoReturn
from austin import AustinError
class AustinCommandLineError(AustinError):
"""Invalid Austin command line."""
pass
class AustinArgumentParser(ArgumentParser):
"""Austin Command Line parser.
This command line parser is based on :class:`argparse.ArgumentParser` and
provides a minimal implementation for parsing the standard Austin command
line. The boolean arguments of the constructor are used to specify whether
the corresponding Austin option should be parsed or not. For example, if
your application doesn't need the possiblity of switching to the
alternative format, you can exclude this option with ``alt_format=False``.
Note that al least one between ``pid`` and ``command`` is required, but
they cannot be used together when invoking Austin.
"""
def __init__(
self,
name: str = "austin",
alt_format: bool = True,
children: bool = True,
exclude_empty: bool = True,
full: bool = True,
interval: bool = True,
memory: bool = True,
pid: bool = True,
sleepless: bool = True,
timeout: bool = True,
command: bool = True,
**kwargs: Any,
) -> None:
super().__init__(prog=name, **kwargs)
if not (pid and command):
raise AustinCommandLineError(
"Austin command line parser must have at least one between pid "
"and command."
)
if alt_format:
self.add_argument(
"-a",
"--alt-format",
help="Alternative collapsed stack sample format.",
action="store_true",
)
if children:
self.add_argument(
"-C",
"--children",
help="Attach to child processes.",
action="store_true",
)
if exclude_empty:
self.add_argument(
"-e",
"--exclude-empty",
help="Do not output samples of threads with no frame stacks.",
action="store_true",
)
if full:
self.add_argument(
"-f",
"--full",
help="Produce the full set of metrics (time +mem -mem).",
action="store_true",
)
if interval:
self.add_argument(
"-i",
"--interval",
help="Sampling interval (default is 100us).",
type=int,
)
if memory:
self.add_argument(
"-m", "--memory", help="Profile memory usage.", action="store_true"
)
if pid:
self.add_argument(
"-p",
"--pid",
help="The the ID of the process to which Austin should attach.",
type=int,
)
if sleepless:
self.add_argument(
"-s", "--sleepless", help="Suppress idle samples.", action="store_true"
)
if timeout:
self.add_argument(
"-t",
"--timeout",
help="Approximate start up wait time. Increase on slow machines "
"(default is 100ms).",
type=int,
)
if command:
self.add_argument(
"command",
type=str,
nargs=REMAINDER,
help="The command to execute if no PID is provided, followed by "
"its arguments.",
)
def parse_args(
self, args: List[str] = None, namespace: Namespace = None
) -> Namespace:
"""Parse the list of arguments.
Return a :class:`argparse.Namespace` with the parsed result. If no PID
nor a command are passed, an instance of the
:class:`AustinCommandLineError` exception is thrown.
"""
parsed_austin_args, unparsed = super().parse_known_args(args, namespace)
if unparsed:
raise AustinCommandLineError(
f"Some arguments were left unparsed: {unparsed}"
)
if not parsed_austin_args.pid and not parsed_austin_args.command:
raise AustinCommandLineError("No PID or command given.")
return parsed_austin_args
def exit(self, status: int = 0, message: str = None) -> NoReturn:
"""Raise exception on error."""
raise AustinCommandLineError(message, status)
@staticmethod
def to_list(args: Namespace) -> List[str]:
"""Convert a :class:`argparse.Namespace` to a list of arguments.
This is the opposite of the parsing of the command line. This static
method is intended to filter and reconstruct the command line arguments
that need to be passed to lower level APIs to start the actual Austin
process.
"""
arg_list = []
if getattr(args, "alt_format", None):
arg_list.append("-a")
if getattr(args, "children", None):
arg_list.append("-C")
if getattr(args, "exclude_empty", None):
arg_list.append("-e")
if getattr(args, "full", None):
arg_list.append("-f")
if getattr(args, "interval", None):
arg_list += ["-i", str(args.interval)]
if getattr(args, "memory", None):
arg_list.append("-m")
if getattr(args, "pid", None):
arg_list += ["-p", str(args.pid)]
if getattr(args, "sleepless", None):
arg_list.append("-s")
if getattr(args, "timeout", None):
arg_list += ["-t", str(args.timeout)]
if getattr(args, "command", None):
arg_list.append(args.command)
return arg_list As you can see, this is about 200 lines of code, but the report shows
This is the test that exercises the code above from austin.cli import AustinArgumentParser, AustinCommandLineError
from pytest import raises
class Bunch:
def __getattr__(self, name):
return self.__dict__.get(name)
def test_missing_command_and_pid():
with raises(AustinCommandLineError):
AustinArgumentParser().parse_args([])
with raises(AustinCommandLineError):
AustinArgumentParser(pid=False, command=False)
def test_command_with_options():
args = AustinArgumentParser().parse_args(
["-i", "1000", "python3", "-c", 'print("Test")']
)
assert args.command == ["python3", "-c", 'print("Test")']
def test_command_with_options_and_arguments():
args = AustinArgumentParser().parse_args(
["-i", "1000", "python3", "my_app.py", "-c", 'print("Test")']
)
assert args.command == ["python3", "my_app.py", "-c", 'print("Test")']
def test_command_with_austin_args():
args = AustinArgumentParser().parse_args(
["-i", "1000", "python3", "my_app.py", "-i", "100"]
)
assert args.interval == 1000
assert args.command == ["python3", "my_app.py", "-i", "100"]
def test_pid_only():
args = AustinArgumentParser().parse_args(["-i", "1000", "-p", "1086"])
assert args.pid == 1086
def test_args_list():
args = Bunch()
args.alt_format = True
args.children = True
args.exclude_empty = True
args.full = True
args.interval = 1000
args.memory = True
args.pid = 42
args.sleepless = True
args.timeout = 50
args.command = "python3"
args.foo = "bar"
assert AustinArgumentParser.to_list(args) == [
"-a",
"-C",
"-e",
"-f",
"-i",
"1000",
"-m",
"-p",
"42",
"-s",
"-t",
"50",
"python3",
] |
Well if the CLI is enough to reproduce the problem put it in a public repo? |
Same problem here, with a project that uses subprocess, and asyncio in python 3.8. Unfortunately It's on a private repo for now but at least it seems not to be an isolated issue. |
I had similar problems and updating |
Summary
Running the same tests with nox against Python 3.6, 3.7 and 3.8 shows wrong report being generated for Python 3.8. This seems to be caused by the fact that the number of reported lines for some sources is wrong.
Expected vs actual result
The report generated with Python 3.6 seems fine:
This is what the same tests produce with Python 3.8
Note how some sources in the 3.8 case are reported to be ridiculously larger than in the 3.6 case.
Reproducer
It seems to happen with every test run.
Versions
pytest (5.4.2)
pytest-cov (2.9.0)
nox (2020.5.24)
Config
Please ask me in a comment if you need anything in particular.
Code
Currently from a private repository
The text was updated successfully, but these errors were encountered: