From 15e0e951e825a57711006247d4d750ba396c937e Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Thu, 7 Dec 2023 02:07:09 +0100 Subject: [PATCH] Make use of `-I` in all Python invocations This helps prevent testing the non-installed source. Additionally, the patch updates the docs to demonstrate a more predictable runpy-style invocation method. --- .github/workflows/deploy.yml | 2 +- .github/workflows/prepare-release-pr.yml | 2 +- .github/workflows/test.yml | 4 ++-- .github/workflows/update-plugin-list.yml | 2 +- CONTRIBUTING.rst | 2 +- doc/en/explanation/goodpractices.rst | 2 +- doc/en/explanation/pythonpath.rst | 12 ++++++------ doc/en/how-to/usage.rst | 11 +++++++---- src/_pytest/pytester.py | 2 +- testing/acceptance_test.py | 6 +++--- testing/test_assertion.py | 6 +++--- testing/test_parseopt.py | 2 +- testing/test_setuponly.py | 2 +- testing/test_warnings.py | 14 +++++--------- tox.ini | 2 +- 15 files changed, 35 insertions(+), 36 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index e1b95efa719..e3f4661dfb0 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -79,7 +79,7 @@ jobs: - name: Install tox run: | - python -m pip install --upgrade pip + python -Im pip install --upgrade pip pip install --upgrade tox - name: Publish GitHub release notes diff --git a/.github/workflows/prepare-release-pr.yml b/.github/workflows/prepare-release-pr.yml index ce8130c86a2..d6534bdc0db 100644 --- a/.github/workflows/prepare-release-pr.yml +++ b/.github/workflows/prepare-release-pr.yml @@ -38,7 +38,7 @@ jobs: - name: Install dependencies run: | - python -m pip install --upgrade pip + python -Im pip install --upgrade pip pip install --upgrade setuptools tox - name: Prepare release PR (minor/patch release) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5f5832475d3..d6df03ad9dc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -186,7 +186,7 @@ jobs: - name: Install dependencies run: | - python -m pip install --upgrade pip + python -Im pip install --upgrade pip pip install tox coverage - name: Test without coverage @@ -201,7 +201,7 @@ jobs: - name: Generate coverage report if: "matrix.use_coverage" - run: python -m coverage xml + run: python -Im coverage xml - name: Upload coverage to Codecov if: "matrix.use_coverage" diff --git a/.github/workflows/update-plugin-list.yml b/.github/workflows/update-plugin-list.yml index ff148e10891..5cd97b39cfa 100644 --- a/.github/workflows/update-plugin-list.yml +++ b/.github/workflows/update-plugin-list.yml @@ -38,7 +38,7 @@ jobs: - name: Install dependencies run: | - python -m pip install --upgrade pip + python -Im pip install --upgrade pip pip install packaging requests tabulate[widechars] tqdm requests-cache platformdirs diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 6f55c230c92..34ea7ce6b2d 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -299,7 +299,7 @@ Here is a simple overview, with pytest-specific bits: #. If instead of using ``tox`` you prefer to run the tests directly, then we suggest to create a virtual environment and use an editable install with the ``testing`` extra:: - $ python3 -m venv .venv + $ python3 -Im venv .venv $ source .venv/bin/activate # Linux $ .venv/Scripts/activate.bat # Windows $ pip install -e ".[testing]" diff --git a/doc/en/explanation/goodpractices.rst b/doc/en/explanation/goodpractices.rst index efde420cd8f..030653b0594 100644 --- a/doc/en/explanation/goodpractices.rst +++ b/doc/en/explanation/goodpractices.rst @@ -137,7 +137,7 @@ which are better explained in this excellent `blog post`_ by Ionel Cristian Măr directory) you can rely on the fact that Python by default puts the current directory in ``sys.path`` to import your package and run ``python -m pytest`` to execute the tests against the local copy directly. - See :ref:`pytest vs python -m pytest` for more information about the difference between calling ``pytest`` and + See :ref:`pytest vs python -Im pytest` for more information about the difference between calling ``pytest`` and ``python -m pytest``. Tests as part of application code diff --git a/doc/en/explanation/pythonpath.rst b/doc/en/explanation/pythonpath.rst index 5b533f47fdc..0d3c516157a 100644 --- a/doc/en/explanation/pythonpath.rst +++ b/doc/en/explanation/pythonpath.rst @@ -130,13 +130,13 @@ imported in the global import namespace. This is also discussed in details in :ref:`test discovery`. -.. _`pytest vs python -m pytest`: +.. _`pytest vs python -Im pytest`: -Invoking ``pytest`` versus ``python -m pytest`` ------------------------------------------------ +Invoking ``pytest`` versus ``python -Im pytest`` +------------------------------------------------ -Running pytest with ``pytest [...]`` instead of ``python -m pytest [...]`` yields nearly -equivalent behaviour, except that the latter will add the current directory to ``sys.path``, which -is standard ``python`` behavior. +Running pytest with ``pytest [...]`` instead of ``python -Im pytest [...]`` yields nearly +equivalent behaviour, except that the latter will add the current directory to ``sys.path`` +in case when ``-I`` is omitted, which is standard ``python`` behavior. See also :ref:`invoke-python`. diff --git a/doc/en/how-to/usage.rst b/doc/en/how-to/usage.rst index 65f9debd852..0929e336d85 100644 --- a/doc/en/how-to/usage.rst +++ b/doc/en/how-to/usage.rst @@ -158,17 +158,20 @@ Other ways of calling pytest .. _invoke-python: -Calling pytest through ``python -m pytest`` +Calling pytest through ``python -Im pytest`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can invoke testing through the Python interpreter from the command line: .. code-block:: text - python -m pytest [...] + python -Im pytest [...] -This is almost equivalent to invoking the command line script ``pytest [...]`` -directly, except that calling via ``python`` will also add the current directory to ``sys.path``. +This is equivalent to invoking the command line script ``pytest [...]`` +directly, for as long as ``-I`` is used. Dropping it will also add the current +directory to ``sys.path``, which may have a side effect of not `testing your +project as installed +`__. .. _`pytest.main-usage`: diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index f93e9c94a00..37829030f10 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -97,7 +97,7 @@ def pytest_addoption(parser: Parser) -> None: choices=("inprocess", "subprocess"), help=( "Run pytest sub runs in tests using an 'inprocess' " - "or 'subprocess' (python -m main) method" + "or 'subprocess' (python -Im main) method" ), ) diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index d597311ae38..3bfcb858310 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -589,17 +589,17 @@ def test_hello(): def test_python_minus_m_invocation_ok(self, pytester: Pytester) -> None: p1 = pytester.makepyfile("def test_hello(): pass") - res = pytester.run(sys.executable, "-m", "pytest", str(p1)) + res = pytester.run(sys.executable, "-Im", "pytest", str(p1)) assert res.ret == 0 def test_python_minus_m_invocation_fail(self, pytester: Pytester) -> None: p1 = pytester.makepyfile("def test_fail(): 0/0") - res = pytester.run(sys.executable, "-m", "pytest", str(p1)) + res = pytester.run(sys.executable, "-Im", "pytest", str(p1)) assert res.ret == 1 def test_python_pytest_package(self, pytester: Pytester) -> None: p1 = pytester.makepyfile("def test_pass(): pass") - res = pytester.run(sys.executable, "-m", "pytest", str(p1)) + res = pytester.run(sys.executable, "-Im", "pytest", str(p1)) assert res.ret == 0 res.stdout.fnmatch_lines(["*1 passed*"]) diff --git a/testing/test_assertion.py b/testing/test_assertion.py index 4d751f8dbfc..98d263053e0 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -1702,18 +1702,18 @@ def test_multitask_job(): "cmdline_args, warning_output", [ ( - ["-OO", "-m", "pytest", "-h"], + ["-OO", "-Im", "pytest", "-h"], ["warning :*PytestConfigWarning:*assert statements are not executed*"], ), ( - ["-OO", "-m", "pytest"], + ["-OO", "-Im", "pytest"], [ "=*= warnings summary =*=", "*PytestConfigWarning:*assert statements are not executed*", ], ), ( - ["-OO", "-m", "pytest", "--assert=plain"], + ["-OO", "-Im", "pytest", "--assert=plain"], [ "=*= warnings summary =*=", "*PytestConfigWarning: ASSERTIONS ARE NOT EXECUTED and FAILING TESTS WILL PASS. " diff --git a/testing/test_parseopt.py b/testing/test_parseopt.py index 1b80883ee0f..8edec93608c 100644 --- a/testing/test_parseopt.py +++ b/testing/test_parseopt.py @@ -317,7 +317,7 @@ def test_argcomplete(pytester: Pytester, monkeypatch: MonkeyPatch) -> None: # http://stackoverflow.com/q/12589419/1307905 # so we use bash fp.write( - 'COMP_WORDBREAKS="$COMP_WORDBREAKS" {} -m pytest 8>&1 9>&2'.format( + 'COMP_WORDBREAKS="$COMP_WORDBREAKS" {} -Im pytest 8>&1 9>&2'.format( shlex.quote(sys.executable) ) ) diff --git a/testing/test_setuponly.py b/testing/test_setuponly.py index fe4bdc514eb..cb6311677e9 100644 --- a/testing/test_setuponly.py +++ b/testing/test_setuponly.py @@ -313,6 +313,6 @@ def test_data(data): """ ) result = pytester.run( - sys.executable, "-bb", "-m", "pytest", "--setup-show", str(test_file) + sys.executable, "-bb", "-Im", "pytest", "--setup-show", str(test_file) ) assert result.ret == 0 diff --git a/testing/test_warnings.py b/testing/test_warnings.py index 96ecad6f647..719ae290b97 100644 --- a/testing/test_warnings.py +++ b/testing/test_warnings.py @@ -792,7 +792,7 @@ def test_warning_on_testpaths_not_found(pytester: Pytester) -> None: ) -def test_resource_warning(pytester: Pytester, monkeypatch: pytest.MonkeyPatch) -> None: +def test_resource_warning(pytester: Pytester) -> None: # Some platforms (notably PyPy) don't have tracemalloc. # We choose to explicitly not skip this in case tracemalloc is not # available, using `importorskip("tracemalloc")` for example, @@ -804,10 +804,6 @@ def test_resource_warning(pytester: Pytester, monkeypatch: pytest.MonkeyPatch) - except ImportError: has_tracemalloc = False - # Explicitly disable PYTHONTRACEMALLOC in case pytest's test suite is running - # with it enabled. - monkeypatch.delenv("PYTHONTRACEMALLOC", raising=False) - pytester.makepyfile( """ def open_file(p): @@ -820,7 +816,9 @@ def test_resource_warning(tmp_path): open_file(p) """ ) - result = pytester.run(sys.executable, "-Xdev", "-m", "pytest") + # Explicitly disable PYTHONTRACEMALLOC in case pytest's test suite is running + # with it enabled. + result = pytester.run(sys.executable, "-Xdev", "-Xtracemalloc=0", "-Im", "pytest") expected_extra = ( [ "*ResourceWarning* unclosed file*", @@ -832,9 +830,7 @@ def test_resource_warning(tmp_path): ) result.stdout.fnmatch_lines([*expected_extra, "*1 passed*"]) - monkeypatch.setenv("PYTHONTRACEMALLOC", "20") - - result = pytester.run(sys.executable, "-Xdev", "-m", "pytest") + result = pytester.run(sys.executable, "-Xdev", "-Xtracemalloc=20", "-Im", "pytest") expected_extra = ( [ "*ResourceWarning* unclosed file*", diff --git a/tox.ini b/tox.ini index c52a43fd7a7..2359f13537c 100644 --- a/tox.ini +++ b/tox.ini @@ -129,7 +129,7 @@ setenv = pip_pre=true # use latest pip to get new dependency resolver (#7783) download=true -install_command=python -m pip install {opts} {packages} +install_command=python -Im pip install {opts} {packages} changedir = testing/plugins_integration deps = -rtesting/plugins_integration/requirements.txt setenv =