Skip to content

pypy3 + coverage: unclosed file <_io.FileIO fd=3 mode='rb' closefd=True> #807

@asottile

Description

@asottile

Describe the bug

Occasionally while running coverage with pypy3 on a specific testsuite it causes "unclosed file" warnings in random parts of code only when instrumented by coverage.

Apologies, I haven't narrowed down the root cause yet but I do have a pretty-consistent reproduction using docker.

To Reproduce

Here's a dockerfile:

FROM ubuntu:xenial

RUN : \
    && apt-get update \
    && apt-get install -y --no-install-recommends \
        bzip2 \
        curl \
        git \
        virtualenv \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

RUN : \
    && curl --silent --location --output /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.2/dumb-init_1.2.2_amd64 \
    && echo '37f2c1f0372a45554f1b89924fbb134fc24c3756efaedf11e07f599494e0eff9 /usr/local/bin/dumb-init' | sha256sum --check \
    && chmod +x /usr/local/bin/dumb-init

RUN : \
    && curl --silent --location --output pypy.tar.bz2 https://bitbucket.org/pypy/pypy/downloads/pypy3.5-v7.0.0-linux64.tar.bz2 \
    && echo '729e3c54325969c98bd3658c6342b9f5987b96bad1d6def04250a08401b54c4b  pypy.tar.bz2' | sha256sum --check \
    && tar -xf pypy.tar.bz2 \
    && rm pypy.tar.bz2

RUN : \
    && git clone https://github.com/pre-commit/pre-commit \
    && git -C pre-commit checkout fff3ad51

WORKDIR pre-commit

RUN : \
    && virtualenv /venv -p ../pypy3.5-v7.0.0-linux64/bin/pypy3 \
    && /venv/bin/pip install -r requirements-dev.txt

CMD [ \
    "dumb-init", \
    "/venv/bin/coverage", "run", "-m", "pytest", "tests/clientlib_test.py" \
]

Build this with

docker build -t pypy-coverage-bug .

You can run this with the following command, however it only reproduces about 1 every 10 times and it's a bit slow to stop and start docker repeatedly:

docker run --rm pypy-coverage-bug

The easier way that I've been reproducing is by doing this

docker run --rm -ti pypy-coverage-bug bash
# and then once inside the docker container (adjust the `-n` for the number of iterations)
yes | head -n 10 | xargs --replace /venv/bin/coverage run -m pytest tests/clientlib_test.py

When it passes it looks like this (which happens most of the time):

============================= test session starts ==============================
platform linux -- Python 3.5.3[pypy-7.0.0-final], pytest-4.5.0, py-1.8.0, pluggy-0.12.0
rootdir: /pre-commit, inifile: tox.ini
plugins: env-0.6.2
collected 37 items                                                             

tests/clientlib_test.py .....................................            [100%]

========================== 37 passed in 1.46 seconds ===========================

When it fails it looks like this:

============================= test session starts ==============================
platform linux -- Python 3.5.3[pypy-7.0.0-final], pytest-4.5.0, py-1.8.0, pluggy-0.12.0
rootdir: /pre-commit, inifile: tox.ini
plugins: env-0.6.2
collected 37 items                                                             

tests/clientlib_test.py .............................E........           [100%]

==================================== ERRORS ====================================
__________ ERROR at teardown of test_meta_hook_invalid[config_repo0] ___________

tp = <class 'AssertionError'>, value = None, tb = None

    def reraise(tp, value, tb=None):
        try:
            if value is None:
                value = tp()
            if value.__traceback__ is not tb:
                raise value.with_traceback(tb)
>           raise value

/venv/site-packages/six.py:693: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/venv/site-packages/six.py:693: in reraise
    raise value
/pypy3.5-v7.0.0-linux64/lib_pypy/_functools.py:80: in __call__
    return self._func(*(self._args + fargs), **fkeywords)
/venv/site-packages/six.py:693: in reraise
    raise value
/pypy3.5-v7.0.0-linux64/lib_pypy/_functools.py:80: in __call__
    return self._func(*(self._args + fargs), **fkeywords)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

recwarn = WarningsRecorder(record=True)

    @pytest.fixture(autouse=True)
    def no_warnings(recwarn):
        yield
        warnings = []
        for warning in recwarn:  # pragma: no cover
            message = str(warning.message)
            # ImportWarning: Not importing directory '...' missing __init__(.py)
            if not (
                isinstance(warning.message, ImportWarning) and
                message.startswith('Not importing directory ') and
                ' missing __init__' in message
            ):
                warnings.append(
                    '{}:{} {}'.format(warning.filename, warning.lineno, message),
                )
>       assert not warnings
E       assert not ["/venv/site-packages/coverage/pytracer.py:107 unclosed file <_io.FileIO fd=3 mode='rb' closefd=True>"]

tests/conftest.py:40: AssertionError
====================== 37 passed, 1 error in 2.74 seconds ======================
  1. What version of Python are you running? Python 3.5.3[pypy-7.0.0-final]
  2. What versions of what packages do you have installed? The output of pip freeze is very helpful.
root@34a9d341816b:/pre-commit# /venv/bin/pip freeze --all
aspy.yaml==1.3.0
atomicwrites==1.3.0
attrs==19.1.0
cffi==1.12.0
cfgv==2.0.0
coverage==4.5.3
entrypoints==0.3
flake8==3.7.7
greenlet==0.4.13
identify==1.4.3
importlib-metadata==0.17
importlib-resources==1.0.2
mccabe==0.6.1
mock==3.0.5
more-itertools==7.0.0
nodeenv==1.3.3
pathlib2==2.3.3
pip==19.1.1
pkg-resources==0.0.0
pluggy==0.12.0
-e git+https://github.com/pre-commit/pre-commit@fff3ad518cb9a9bf169346aa4727fb70a67a4e30#egg=pre_commit
py==1.8.0
pycodestyle==2.5.0
pyflakes==2.1.1
pytest==4.5.0
pytest-env==0.6.2
PyYAML==5.1
readline==6.2.4.1
setuptools==41.0.1
six==1.12.0
toml==0.10.0
virtualenv==16.6.0
wcwidth==0.1.7
wheel==0.33.4
zipp==0.5.1
  1. What code are you running? Give us a specific commit of a specific repo that we can check out. (shown above, it's pre-commit
  2. What commands did you run? (see above)

Expected behavior
Not to crash (see above)

Additional context

Thanks again for writing coverage, it has been instrumental to all of my python testing! Sorry I haven't narrowed down this bug enough yet, but figured I'd post this in case you have any ideas on where I should start looking!

This is currently flaking a lot for me in azure pipelines :(

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions