Skip to content
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

Coverage command_line run fails with AttributeError: 'NoDebugging' object has no attribute 'suppress_callers' #1904

Open
Krisscut opened this issue Dec 10, 2024 · 3 comments
Labels
bug Something isn't working question Further information is requested

Comments

@Krisscut
Copy link

Describe the bug
Coverage command_line run fails with AttributeError: 'NoDebugging' object has no attribute 'suppress_callers'

To Reproduce
How can we reproduce the problem? It's hard to reproduce, this is a sporadic issue that we are seeing running some automated tests in endurance.

  1. What version of Python are you using? Python3.9
  2. What version of coverage.py shows the problem?

bash-5.1$ coverage debug sys
-- sys -------------------------------------------------------
coverage_version: 7.3.0
coverage_module: /usr/local/lib64/python3.9/site-packages/coverage/init.py
tracer: -none-
CTracer: available
plugins.file_tracers: -none-
plugins.configurers: -none-
plugins.context_switchers: -none-
configs_attempted: .coveragerc
configs_read: /workspace/.coveragerc
config_file: /workspace/.coveragerc
config_contents: b'[run]\nbranch = True\nconcurrency = multiprocessing,thread\nparallel=True\n'
data_file: -none-
python: 3.9.18 (main, Jan 4 2024, 00:00:00) [GCC 11.4.1 20230605 (Red Hat 11.4.1-2)]
platform: Linux-5.15.35-1.NSN.el7.x86_64-x86_64-with-glibc2.34
implementation: CPython
executable: /usr/bin/python3
def_encoding: utf-8
fs_encoding: utf-8
pid: 70
cwd: /workspace
path: /usr/local/bin
/opt/UE_APP_OAM/python_lib
/opt/UE_APP_OAM/lib/python3.9/site-packages
/opt/UE_APP_OAM/lib64/python3.9/site-packages
/opt/hwmt/python
/opt/hwmt/build/python
/workspace/Services/CommonLibs
/workspace
/usr/lib64/python39.zip
/usr/lib64/python3.9
/usr/lib64/python3.9/lib-dynload
/usr/local/lib64/python3.9/site-packages
/usr/local/lib/python3.9/site-packages
/usr/lib64/python3.9/site-packages
/usr/lib/python3.9/site-packages
environment: HOME = /tmp
NATIVE_PYTHON3 = /usr/bin/python3
PYTHON3 = /usr/bin/python3
PYTHONPATH = /opt/UE_APP_OAM/python_lib:/opt/UE_APP_OAM/lib/python3.9/site-packages/:/opt/UE_APP_OAM/lib64/python3.9/site-packages/:/opt/hwmt/python:/opt/hwmt/build/python:/workspace/Services/CommonLibs:
command_line: /usr/local/bin/coverage debug sys
sqlite3_sqlite_version: 3.34.1
sqlite3_temp_store: 0
sqlite3_compile_options: COMPILER=gcc-11.3.1 20220421 (Red Hat 11.3.1-2), DISABLE_DIRSYNC,
ENABLE_COLUMN_METADATA, ENABLE_DBSTAT_VTAB, ENABLE_FTS3,
ENABLE_FTS3_PARENTHESIS, ENABLE_FTS4, ENABLE_FTS5, ENABLE_JSON1,
ENABLE_RTREE, ENABLE_UNLOCK_NOTIFY, HAVE_ISNAN, SECURE_DELETE,
TEMP_STORE=1, THREADSAFE=1

  1. What versions of what packages do you have installed? The output of pip freeze is helpful.

bash-5.1$ pip freeze
aiohappyeyeballs==2.4.3
aiohttp==3.10.10
aiohttp-wsgi==0.10.0
aiosignal==1.3.1
asn1crypto==1.3.0
astroid==2.11.7
async-timeout==4.0.3
attrs==21.4.0
bcrypt==4.2.0
certifi==2024.8.30
cffi==1.15.0
charset-normalizer==3.4.0
coverage==7.3.0
crossenv==1.3.0
cryptography==43.0.3
Cython==0.29.33
dbus-python==1.2.18
defusedxml==0.7.1
dill==0.3.5.1
docstring_parser==0.13
ecdsa==0.13
elementpath==4.4.0
exceptiongroup==1.2.2
frozenlist==1.5.0
future==0.18.2
gcovr==6.0
getmac==0.8.3
gpg==1.15.1
h11==0.14.0
hypothesis==6.84.2
idna==3.10
ifaddr==0.2.0
importlib-metadata==4.12.0
importlib-resources==0.0.0
iniconfig==1.1.1
ipaddress==1.0.22
isort==5.10.1
Jinja2==3.1.4
json5==0.9.25
jsonschema==0.0.0
junitparser==2.8.0
lazy-object-proxy==1.7.1
libcomps==0.1.18
libyang==2.8.4
lxml==4.9.1
MarkupSafe==1.1.1
mccabe==0.7.0
multidict==6.1.0
ncclient==0.6.16
netconf-client==3.1.1
netifaces==0.11.0
numpy==1.20.1
outcome==1.3.0.post0
packaging==21.3
paramiko==3.5.0
passlib==1.7.4
pexpect==4.8.0
platformdirs==2.5.2
pluggy==1.0.0
prompt-toolkit==3.0.24
propcache==0.2.0
protobuf==3.14.0
psutil==5.9.8
ptyprocess==0.7.0
py==1.11.0
pyasn1==0.4.4
pycparser==2.22
pycrypto==2.6.1
Pygments==2.18.0
pylint==2.14.4
PyNaCl==1.5.0
pyparsing==3.0.9
pyroute2 @ file:///workspace/Services/3rdParty/pyroute2-0.7.3.tar.gz
pyrsistent==0.18.1
PySocks==1.7.1
pytest==7.1.2
pytest-base-url==2.1.0
pytest-cov==3.0.0
pytest-html==4.1.1
pytest-metadata==3.1.1
pytest-mock==3.7.0
pytest-random-order==1.0.4
pytest-rerunfailures==10.2
pytest-selenium==4.1.0
pytest-timeout==2.3.1
pytest-variables==3.1.0
python-dateutil==2.8.1
python-docx==1.1.0
python-dotenv==1.0.1
PyYAML==6.0.2
redis==4.6.0
requests==2.31.0
rpm==4.16.1.3
scapy==2.4.5
selenium==4.16.0
six==1.16.0
sniffio==1.3.1
sortedcontainers==2.4.0
supervisor==4.2.2
sysrepo==1.5.0
systemd-python==234
tabulate==0.9.0
tenacity==9.0.0
tomli==2.0.1
tomlkit==0.11.4
trio==0.24.0
trio-websocket==0.11.1
typed-ast==1.5.4
typing_extensions==4.12.2
urllib3==2.2.3
watchdog==2.1.9
wcwidth==0.2.5
webdriver-manager==4.0.2
websocket-client==1.8.0
websockets==9.1
wrapt==1.14.1
WsgiDAV==4.3.3
wsproto==1.2.0
xmlschema==3.3.1
xmltodict==0.11.0
yarl==1.16.0
zipp==0.0.0
bash-5.1$

  1. What code shows the problem?

Full stack trace:

2024-12-09 12:05:36.738 [ ERR>] [SUT.py:114] - process_fd_to_read() - Traceback (most recent call last):
2024-12-09 12:05:36.739 [ ERR>] [SUT.py:114] - process_fd_to_read() - File "/workspace/tools/run_pytest_coverage", line 51, in
2024-12-09 12:05:36.740 [ ERR>] [SUT.py:114] - process_fd_to_read() - CoverageScriptCustom(sut_id).command_line(command_to_run)
2024-12-09 12:05:36.741 [ ERR>] [SUT.py:114] - process_fd_to_read() - File "/usr/local/lib64/python3.9/site-packages/coverage/cmdline.py", line 684, in command_line
2024-12-09 12:05:36.742 [ ERR>] [SUT.py:114] - process_fd_to_read() - return self.do_run(options, args)
2024-12-09 12:05:36.743 [ ERR>] [SUT.py:114] - process_fd_to_read() - File "/workspace/tools/run_pytest_coverage", line 45, in do_run
2024-12-09 12:05:36.743 [ ERR>] [SUT.py:114] - process_fd_to_read() - super().do_run(*args)
2024-12-09 12:05:36.744 [ ERR>] [SUT.py:114] - process_fd_to_read() - File "/usr/local/lib64/python3.9/site-packages/coverage/cmdline.py", line 858, in do_run
2024-12-09 12:05:36.745 [ ERR>] [SUT.py:114] - process_fd_to_read() - self.coverage.start()
2024-12-09 12:05:36.745 [ ERR>] [SUT.py:114] - process_fd_to_read() - File "/usr/local/lib64/python3.9/site-packages/coverage/control.py", line 632, in start
2024-12-09 12:05:36.746 [ ERR>] [SUT.py:114] - process_fd_to_read() - self._post_init()
2024-12-09 12:05:36.747 [ ERR>] [SUT.py:114] - process_fd_to_read() - File "/usr/local/lib64/python3.9/site-packages/coverage/control.py", line 353, in _post_init
2024-12-09 12:05:36.747 [ ERR>] [SUT.py:114] - process_fd_to_read() - self._write_startup_debug()
2024-12-09 12:05:36.748 [ ERR>] [SUT.py:114] - process_fd_to_read() - File "/usr/local/lib64/python3.9/site-packages/coverage/control.py", line 363, in _write_startup_debug
2024-12-09 12:05:36.748 [ ERR>] [SUT.py:114] - process_fd_to_read() - with self._debug.without_callers():
2024-12-09 12:05:36.748 [ ERR>] [SUT.py:114] - process_fd_to_read() - File "/usr/lib64/python3.9/contextlib.py", line 119, in enter
2024-12-09 12:05:36.749 [ ERR>] [SUT.py:114] - process_fd_to_read() - return next(self.gen)
2024-12-09 12:05:36.750 [ ERR>] [SUT.py:114] - process_fd_to_read() - File "/usr/local/lib64/python3.9/site-packages/coverage/debug.py", line 78, in without_callers
2024-12-09 12:05:36.750 [ ERR>] [SUT.py:114] - process_fd_to_read() - old = self.suppress_callers
2024-12-09 12:05:36.751 [ ERR>] [SUT.py:114] - process_fd_to_read() - AttributeError: 'NoDebugging' object has no attribute 'suppress_callers'

Seems like issue is here:

@contextlib.contextmanager
def without_callers(self) -> Iterator[None]:
"""A context manager to prevent call stacks from being logged."""
old = self.suppress_callers
self.suppress_callers = True
try:
yield
finally:
self.suppress_callers = old

Which should be defined here:

self.suppress_callers = False

But in our case it isn't because it seems the underlying code uses the NoDebugging class which doesn't call the init of its parent:

class NoDebugging(DebugControl):
"""A replacement for DebugControl that will never try to do anything."""
def __init__(self) -> None:
# pylint: disable=super-init-not-called
...
def should(self, option: str) -> bool:

  1. What commands should we run to reproduce the problem?
    Hard to give full repro steps since this is seen on some of unit test, but we basically run our software with the following coverage script which wraps the python code we want to test/check the coverage for. It's mostly a wrapper so that we can trigger the dump of the coverage periodically or on demand through a redis message
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import re
import sys
import time
import threading
from coverage.cmdline import CoverageScript
from message import Message
from multiprocessing import Lock


class CoverageScriptCustom(CoverageScript):

    def __init__(self, sut_id):
        super().__init__()
        self.sut_id = sut_id
        self.message = Message(db=0)
        self.message.MsgSubscribe({
            f"TEST/COVERAGE/DUMP/{self.sut_id}": self.dump_coverage_message_handler,
            })

        self.coverage_mutex = Lock()

    def dump_coverage_message_handler(self, dn, value):
        print(f"Received message: {dn} {value}, dumping coverage")
        self.save_coverage()
        self.message.MsgReply(dn, "Done")

    def save_coverage(self):
        with self.coverage_mutex:
            self.coverage.save()

    def do_run(self, *args):
        def thread_cov_saver():
            self.coverage.load()
            while True:
                self.save_coverage()
                time.sleep(1)
        saver_thread = threading.Thread(target=thread_cov_saver, name="coverage saver")
        saver_thread.start()

        super().do_run(*args)


if __name__ == '__main__':
    sut_id = sys.argv[1]
    command_to_run = sys.argv[2:]
    CoverageScriptCustom(sut_id).command_line(command_to_run)
    sys.exit()

the issue only occurs sporadically, reproduction is not 100%.

Expected behavior
Running a software with the CoverageScript.command_line should have deterministic behavior and not fail sporadically.

@Krisscut Krisscut added the bug Something isn't working label Dec 10, 2024
@nedbat
Copy link
Owner

nedbat commented Dec 10, 2024

CoverageScript isn't a supported public interface. Is there a way to do what you need to do using the documented API?

@nedbat nedbat added the question Further information is requested label Dec 24, 2024
@Krisscut
Copy link
Author

Krisscut commented Jan 7, 2025

I'm looking in using an alternative.
From what I know, this implementation was done because the program that got started didn't stop gracefully, so the coverage had to be dumped periodically or on demand right before we kill it to fetch the coverage.

I'm working on cleaning this up at the moment.

But don't you think what I mentioned in the original post is not weird as well ? With the usage of an undefined variable.

@nedbat
Copy link
Owner

nedbat commented Jan 7, 2025

I see your point. I fixed NoDebugging. Can you try installing this and see if it fixes the problem?

python3 -m pip install git+https://github.com/nedbat/coveragepy@nedbat/fix-1904#egg=coverage==0.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants