Skip to content

Difference between multiple context managers in Windows and Linux #734

@MartinBonner

Description

@MartinBonner

Summary

I have some code which looks like:

    with (                                  # Line 320
        fn1(args1) as thing1,
        fn2(args2) as thing2,
    ):
        ...

I have some unit tests which run under Linux and (on the CI) under Windows. The command-line is python3 -m pytest --cov --cov-report term-missing.

Expected vs actual result

The expected result is that the coverage report should be the same on both platforms. Specifically that 100% branch coverage is achieved (despite that nothing mocks fn1 or fn2 raising an exception).

The actual result is that Linux achieves 100% coverage, but Windows reports that 320->exit is not covered.

Reproducer

Apologies, but I am not confident that this section is going to be entirely accurate. I don't have a Windows machine myself, so I can't readily obtain the required information for Windows. (It should be the same on both Windows and Linux, but obviously if one is looking for an explanation of different behaviour on the two platforms, different versions of something are the first place to look.)

Versions

$ pip list
Package                               Version                  Editable project location
------------------------------------- ------------------------ ------------------------------------------------------------------------------------
appdirs                               1.4.4
asn1                                  2.7.0
asn1crypto                            1.5.1
astroid                               3.0.1
async-timeout                         4.0.3
attrs                                 23.1.0
bcrypt                                4.0.1
build                                 1.2.1
certifi                               2024.8.30
certvalidator                         1.0.0
cffi                                  1.16.0
chardet                               5.2.0
charset-normalizer                    3.3.2
click                                 8.1.7
configparser                          5.3.0
coverage                              7.10.7
coverage-conditional-plugin           0.9.0
dbus-next                             0.2.3
dill                                  0.3.7
ecdsa                                 0.18.0
enum-compat                           0.0.3
exceptiongroup                        1.1.3
fdt                                   0.3.3
filelock                              3.13.1
gitdb                                 4.0.10
GitPython                             3.1.31
idna                                  3.10
ifaddr                                0.2.0
iniconfig                             2.0.0
isort                                 5.12.0
jedi                                  0.19.1
lazy-object-proxy                     1.9.0
mccabe                                0.7.0
mock                                  5.1.0
mypy                                  1.15.0
mypy-extensions                       1.0.0
<redacted>
numpy                                 1.26.0
packaging                             24.0
paramiko                              3.4.1
parso                                 0.8.3
pbr                                   6.0.0
pefile                                2023.2.7
pep517                                0.13.0
pexpect                               4.9.0
pip                                   23.3.2
platformdirs                          4.3.6
pluggy                                1.3.0
prompt-toolkit                        3.0.41
psutil                                5.9.4                    /home/mbonner/git/main/build-host-linux/psutil
ptpython                              3.0.23
ptyprocess                            0.7.0
py                                    1.11.0
pycparser                             2.22
pycryptodomex                         3.20.0
pydantic                              1.10.21
pyelftools                            0.31
Pygments                              2.16.1
pylint                                3.0.2
PyNaCl                                1.5.0
pyparsing                             3.1.1
pyproject_hooks                       1.1.0
pyscard                               2.0.7
pyserial                              3.5
pytest                                7.4.3
pytest-asyncio                        0.23.8
pytest-cov                            4.1.0
pytest-mock                           3.12.0
pytest-timeout                        2.1.0
pytestdiv                             0.3.1
PyYAML                                6.0.1
requests                              2.32.5
setuptools                            79.0.1
six                                   1.17.0
smmap                                 5.0.0
toml                                  0.10.2
tomli                                 2.0.1
tomlkit                               0.12.3
types-mock                            5.1.0.2
types-PyYAML                          6.0.12.12
types-requests                        2.31.0.10
types-urllib3                         1.26.25.14
typing_extensions                     4.8.0
urllib3                               2.5.0
wcwidth                               0.2.10
wheel                                 0.45.1
wrapt                                 1.16.0
wxPython                              4.2.2
zeroconf                              0.147.0

[notice] A new release of pip is available: 23.3.2 -> 25.3
[notice] To update, run: pip install --upgrade pip

Python version: Python 3.11.14 (this is the same on Windows)

On windows:

platform win32 -- Python 3.11.14, pytest-7.4.3, pluggy-1.3.0
plugins: pytestdiv-0.3.1, asyncio-0.23.8, cov-4.1.0, mock-3.12.0, timeout-2.1.0, internal_pytest-0.1.1

On linux:

platform linux -- Python 3.11.14, pytest-7.4.3, pluggy-1.3.0
plugins: asyncio-0.23.8, mock-3.12.0, pytestdiv-0.3.1, timeout-2.1.0, cov-4.1.0, internal_pytest-0.1.1

Config

pytest.ini

[pytest]
log_cli_level = INFO
filterwarnings =
    error

.coveragerc-linux

# see https://coverage.readthedocs.io/en/coverage-5.1/config.html
[run]
branch = True

# include top level namespace modules/packages we ship
source =
    our.package

omit =
    */windows/*.py

[report]
fail_under = 100

# Regexes for lines to exclude from consideration
exclude_lines =

    # exclude # pragma: no cover
    pragma: no cover

    # Don't complain about missing debug-only code:
    def __repr__

    # Don't complain if tests don't hit defensive assertion code:
    raise AssertionError
    raise NotImplementedError

    # Don't complain about not running Windows-only code:
    if sys.platform == 'win32':

.coveragerc-windows

# see https://coverage.readthedocs.io/en/coverage-5.1/config.html
[run]
branch = True

# include top level namespace modules/packages we ship
source = our.package

omit =
    */linux/*.py

[report]
fail_under = 100

# Regexes for lines to exclude from consideration
exclude_lines =
    # exclude # pragma: no cover
    pragma: no cover

    # A pragma for code which simply isn't relevant on Windows.
    pragma: no-coverage-windows

    # Don't complain about missing debug-only code:
    def __repr__

    # Don't complain if tests don't hit defensive assertion code:
    raise AssertionError
    raise NotImplementedError

    # Don't complain about not running Linux-only code:
    if sys.platform.startswith\('linux'\):

Code

See above.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions