diff --git a/.coveragerc b/.coveragerc index 97934dc3b..b5cfb9e7b 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,9 +1,18 @@ [run] -source = pytest,_pytest,testing/ +include = + */src/* + testing/* + */lib/python*/site-packages/_pytest/* + */lib/python*/site-packages/pytest.py + */pypy*/site-packages/_pytest/* + */pypy*/site-packages/pytest.py + *\Lib\site-packages\_pytest\* + *\Lib\site-packages\pytest.py parallel = 1 branch = 1 [paths] source = src/ - .tox/*/lib/python*/site-packages/ - .tox\*\Lib\site-packages\ + */lib/python*/site-packages/ + */pypy*/site-packages/ + *\Lib\site-packages\ diff --git a/.travis.yml b/.travis.yml index ba2ba37f2..03b502c80 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,54 +9,68 @@ stages: python: - '3.7' install: - - pip install --upgrade --pre tox -env: - matrix: - - TOXENV=py27 - # Specialized factors for py27. - - TOXENV=py27-nobyte - - TOXENV=py27-xdist - - TOXENV=py27-pluggymaster - # Specialized factors for py37. - - TOXENV=py37-pexpect,py37-trial,py37-numpy - - TOXENV=py37-pluggymaster - - TOXENV=py37-freeze PYTEST_NO_COVERAGE=1 - -matrix: - allow_failures: - - python: '3.8-dev' - env: TOXENV=py38-xdist + - python -m pip install --upgrade --pre tox jobs: include: + # OSX tests - first (in test stage), since they are the slower ones. + - &test-macos + # NOTE: (tests with) pexpect appear to be buggy on Travis, + # at least with coverage. + # Log: https://travis-ci.org/pytest-dev/pytest/jobs/500358864 + os: osx + osx_image: xcode10.1 + language: generic + # Coverage for: + # - py2 with symlink in test_cmdline_python_package_symlink. + env: TOXENV=py27-xdist PYTEST_COVERAGE=1 + before_install: + - python -V + - test $(python -c 'import sys; print("%d%d" % sys.version_info[0:2])') = 27 + - <<: *test-macos + env: TOXENV=py37-xdist + before_install: + - which python3 + - python3 -V + - ln -sfn "$(which python3)" /usr/local/bin/python + - python -V + - test $(python -c 'import sys; print("%d%d" % sys.version_info[0:2])') = 37 + + # Full run of latest (major) supported versions, without xdist. + - env: TOXENV=py27 + python: '2.7' + - env: TOXENV=py37 + python: '3.7' + # Coverage tracking is slow with pypy, skip it. - - env: TOXENV=pypy-xdist PYTEST_NO_COVERAGE=1 + - env: TOXENV=pypy-xdist python: 'pypy2.7-6.0' - - env: TOXENV=pypy3-xdist PYTEST_NO_COVERAGE=1 + - env: TOXENV=pypy3-xdist python: 'pypy3.5-6.0' - env: TOXENV=py34-xdist python: '3.4' - env: TOXENV=py35-xdist python: '3.5' - - env: TOXENV=py36-xdist - python: '3.6' - - env: TOXENV=py37 - - &test-macos - language: generic - os: osx - osx_image: xcode9.4 - sudo: required - install: - - python -m pip install --pre tox - env: TOXENV=py27-xdist - - <<: *test-macos - env: TOXENV=py37-xdist - before_install: - - brew update - - brew upgrade python - - brew unlink python - - brew link python + + # Coverage for: + # - pytester's LsofFdLeakChecker + # - TestArgComplete (linux only) + # - numpy + - env: TOXENV=py37-lsof-numpy-xdist PYTEST_COVERAGE=1 + + # Specialized factors for py27. + - env: TOXENV=py27-nobyte-numpy-xdist + python: '2.7' + - env: TOXENV=py27-pluggymaster-xdist + python: '2.7' + + # Specialized factors for py37. + # Coverage for: + # - test_sys_breakpoint_interception (via pexpect). + - env: TOXENV=py37-pexpect,py37-trial PYTEST_COVERAGE=1 + - env: TOXENV=py37-pluggymaster-xdist + - env: TOXENV=py37-freeze # Jobs only run via Travis cron jobs (currently daily). - env: TOXENV=py38-xdist @@ -64,14 +78,17 @@ jobs: if: type = cron - stage: baseline - env: TOXENV=py27-pexpect,py27-trial,py27-numpy - - env: TOXENV=py37-xdist - - env: TOXENV=linting,docs,doctesting - python: '3.7' + # Coverage for: + # - _pytest.unittest._handle_skip (via pexpect). + env: TOXENV=py27-pexpect,py27-trial PYTEST_COVERAGE=1 + python: '2.7' + # Use py36 here for faster baseline. + - env: TOXENV=py36-xdist + python: '3.6' + - env: TOXENV=linting,docs,doctesting PYTEST_COVERAGE=1 - stage: deploy python: '3.6' - env: PYTEST_NO_COVERAGE=1 install: pip install -U setuptools setuptools_scm script: skip deploy: @@ -85,9 +102,19 @@ jobs: tags: true repo: pytest-dev/pytest +matrix: + allow_failures: + - python: '3.8-dev' + env: TOXENV=py38-xdist + before_script: - | - if [[ "$PYTEST_NO_COVERAGE" != 1 ]]; then + # Do not (re-)upload coverage with cron runs. + if [[ "$TRAVIS_EVENT_TYPE" = cron ]]; then + PYTEST_COVERAGE=0 + fi + - | + if [[ "$PYTEST_COVERAGE" = 1 ]]; then export COVERAGE_FILE="$PWD/.coverage" export COVERAGE_PROCESS_START="$PWD/.coveragerc" export _PYTEST_TOX_COVERAGE_RUN="coverage run -m" @@ -98,14 +125,14 @@ script: tox --recreate after_success: - | - if [[ "$PYTEST_NO_COVERAGE" != 1 ]]; then + if [[ "$PYTEST_COVERAGE" = 1 ]]; then set -e # Add last TOXENV to $PATH. PATH="$PWD/.tox/${TOXENV##*,}/bin:$PATH" coverage combine - coverage xml --ignore-errors - coverage report -m --ignore-errors - bash <(curl -s https://codecov.io/bash) -Z -X gcov -X coveragepy -X search -X xcode -X gcovout -X fix -f coverage.xml -F $TRAVIS_OS_NAME + coverage xml + coverage report -m + bash <(curl -s https://codecov.io/bash) -Z -X gcov -X coveragepy -X search -X xcode -X gcovout -X fix -f coverage.xml -n $TOXENV-$TRAVIS_OS_NAME fi notifications: diff --git a/README.rst b/README.rst index bd7f1daa6..1955d3503 100644 --- a/README.rst +++ b/README.rst @@ -22,8 +22,8 @@ .. image:: https://travis-ci.org/pytest-dev/pytest.svg?branch=master :target: https://travis-ci.org/pytest-dev/pytest -.. image:: https://ci.appveyor.com/api/projects/status/mrgbjaua7t33pg6b?svg=true - :target: https://ci.appveyor.com/project/pytestbot/pytest +.. image:: https://dev.azure.com/pytest-dev/pytest/_apis/build/status/pytest-CI?branchName=master + :target: https://dev.azure.com/pytest-dev/pytest .. image:: https://img.shields.io/badge/code%20style-black-000000.svg :target: https://github.com/ambv/black diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index da4def8f1..000000000 --- a/appveyor.yml +++ /dev/null @@ -1,48 +0,0 @@ -environment: - matrix: - - TOXENV: "py37-xdist" - - TOXENV: "py27-xdist" - - TOXENV: "linting,docs,doctesting" - - TOXENV: "py34-xdist" - - TOXENV: "py35-xdist" - - TOXENV: "py36-xdist" - # NOTE: pypy-xdist is buggy currently (https://github.com/pytest-dev/pytest-xdist/issues/142). - - TOXENV: "pypy" - PYTEST_NO_COVERAGE: "1" - # Specialized factors for py27. - - TOXENV: "py27-trial,py27-numpy,py27-nobyte" - # Specialized factors for py37. - - TOXENV: "py37-trial,py37-numpy" - - TOXENV: "py37-freeze" - PYTEST_NO_COVERAGE: "1" - -matrix: - fast_finish: true - -install: - - echo Installed Pythons - - dir c:\Python* - - - if "%TOXENV%" == "pypy" call scripts\install-pypy.bat - - - C:\Python36\python -m pip install --upgrade pip - - C:\Python36\python -m pip install --upgrade --pre tox - -build: false # Not a C# project, build stuff at the test step instead. - -before_test: - - call scripts\prepare-coverage.bat - -test_script: - - C:\Python36\python -m tox - -on_success: - - call scripts\upload-coverage.bat - -cache: - - '%LOCALAPPDATA%\pip\cache' - - '%USERPROFILE%\.cache\pre-commit' - -# We don't deploy anything on tags with AppVeyor, we use Travis instead, so we -# might as well save resources -skip_tags: true diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 9f375bbc9..712106c94 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -6,6 +6,9 @@ variables: PYTEST_ADDOPTS: "--junitxml=build/test-results/$(tox.env).xml" python.needs_vc: False python.exe: "python" + COVERAGE_FILE: "$(Build.Repository.LocalPath)/.coverage" + COVERAGE_PROCESS_START: "$(Build.Repository.LocalPath)/.coveragerc" + PYTEST_COVERAGE: '0' jobs: @@ -17,47 +20,68 @@ jobs: py27: python.version: '2.7' tox.env: 'py27' - py27-xdist: + py27-nobyte-lsof-numpy: python.version: '2.7' - tox.env: 'py27-xdist' - py27-numpy/nobyte: - python.version: '2.7' - tox.env: 'py27-numpy,py27-nobyte' + tox.env: 'py27-lsof-nobyte-numpy' + # Coverage for: + # - test_supports_breakpoint_module_global + # - test_terminal_reporter_writer_attr (without xdist) + # - "if write" branch in _pytest.assertion.rewrite + # - numpy + # - pytester's LsofFdLeakChecker (being skipped) + PYTEST_COVERAGE: '1' py27-trial: python.version: '2.7' tox.env: 'py27-trial' python.needs_vc: True - py27-pluggymaster: + py27-pluggymaster-xdist: python.version: '2.7' - tox.env: 'pluggymaster' + tox.env: 'py27-pluggymaster-xdist' + # Coverage for: + # - except-IOError in _attempt_to_close_capture_file for py2. + # Also seen with py27-nobyte (using xdist), and py27-xdist. + # But no exception with py27-pexpect,py27-trial,py27-numpy. + PYTEST_COVERAGE: '1' pypy: python.version: 'pypy' tox.env: 'pypy' python.exe: 'pypy' - py34: + # NOTE: pypy3 fails to install pip currently due to an interal error. + # pypy3: + # python.version: 'pypy3' + # tox.env: 'pypy3' + # python.exe: 'pypy3' + py34-xdist: python.version: '3.4' - tox.env: 'py34' - py35: + tox.env: 'py34-xdist' + # Coverage for: + # - _pytest.compat._bytes_to_ascii + PYTEST_COVERAGE: '1' + py35-xdist: python.version: '3.5' - tox.env: 'py35' - py36: + tox.env: 'py35-xdist' + # Coverage for: + # - test_supports_breakpoint_module_global + PYTEST_COVERAGE: '1' + py36-xdist: python.version: '3.6' - tox.env: 'py36' + tox.env: 'py36-xdist' py37: python.version: '3.7' tox.env: 'py37' + # Coverage for: + # - _py36_windowsconsoleio_workaround (with py36+) + # - test_request_garbage (no xdist) + PYTEST_COVERAGE: '1' py37-linting/docs/doctesting: python.version: '3.7' tox.env: 'linting,docs,doctesting' - py37-xdist: - python.version: '3.7' - tox.env: 'py37-xdist' py37-trial/numpy: python.version: '3.7' tox.env: 'py37-trial,py37-numpy' - py37-pluggymaster: + py37-pluggymaster-xdist: python.version: '3.7' - tox.env: 'py37-pluggymaster' + tox.env: 'py37-pluggymaster-xdist' maxParallel: 10 steps: @@ -91,7 +115,9 @@ jobs: - script: $(python.exe) -m pip install --upgrade pip && $(python.exe) -m pip install tox displayName: 'Install tox' - - script: $(python.exe) -m tox -e $(tox.env) + - script: | + call scripts/setup-coverage-vars.bat || goto :eof + $(python.exe) -m tox -e $(tox.env) displayName: 'Run tests' - task: PublishTestResults@2 @@ -99,3 +125,11 @@ jobs: testResultsFiles: 'build/test-results/$(tox.env).xml' testRunTitle: '$(tox.env)' condition: succeededOrFailed() + + - script: call scripts\upload-coverage.bat + displayName: 'Report and upload coverage' + condition: eq(variables['PYTEST_COVERAGE'], '1') + env: + PYTHON: $(python.exe) + CODECOV_TOKEN: $(CODECOV_TOKEN) + PYTEST_CODECOV_NAME: $(tox.env) diff --git a/bench/skip.py b/bench/skip.py index 25559cc27..2fc8240e5 100644 --- a/bench/skip.py +++ b/bench/skip.py @@ -2,7 +2,6 @@ from six.moves import range import pytest - SKIP = True diff --git a/changelog/4861.bugfix.rst b/changelog/4861.bugfix.rst new file mode 100644 index 000000000..b4bf125d1 --- /dev/null +++ b/changelog/4861.bugfix.rst @@ -0,0 +1 @@ +Improve validation of contents written to captured output so it behaves the same as when capture is disabled. diff --git a/doc/en/assert.rst b/doc/en/assert.rst index b119adcf0..e7e78601b 100644 --- a/doc/en/assert.rst +++ b/doc/en/assert.rst @@ -252,8 +252,8 @@ the conftest file: .. _assert-details: .. _`assert introspection`: -Advanced assertion introspection ----------------------------------- +Assertion introspection details +------------------------------- .. versionadded:: 2.1 @@ -266,28 +266,46 @@ supporting modules which are not themselves test modules will not be rewritten** You can manually enable assertion rewriting for an imported module by calling `register_assert_rewrite `_ -before you import it (a good place to do that is in ``conftest.py``). - -.. note:: - - ``pytest`` rewrites test modules on import by using an import - hook to write new ``pyc`` files. Most of the time this works transparently. - However, if you are messing with import yourself, the import hook may - interfere. - - If this is the case you have two options: - - * Disable rewriting for a specific module by adding the string - ``PYTEST_DONT_REWRITE`` to its docstring. - - * Disable rewriting for all modules by using ``--assert=plain``. - - Additionally, rewriting will fail silently if it cannot write new ``.pyc`` files, - i.e. in a read-only filesystem or a zipfile. - +before you import it (a good place to do that is in your root ``conftest.py``). For further information, Benjamin Peterson wrote up `Behind the scenes of pytest's new assertion rewriting `_. +Assertion rewriting caches files on disk +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``pytest`` will write back the rewritten modules to disk for caching. You can disable +this behavior (for example to avoid leaving stale ``.pyc`` files around in projects that +move files around a lot) by adding this to the top of your ``conftest.py`` file: + +.. code-block:: python + + import sys + + sys.dont_write_bytecode = True + +Note that you still get the benefits of assertion introspection, the only change is that +the ``.pyc`` files won't be cached on disk. + +Additionally, rewriting will silently skip caching if it cannot write new ``.pyc`` files, +i.e. in a read-only filesystem or a zipfile. + + +Disabling assert rewriting +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``pytest`` rewrites test modules on import by using an import +hook to write new ``pyc`` files. Most of the time this works transparently. +However, if you are working with the import machinery yourself, the import hook may +interfere. + +If this is the case you have two options: + +* Disable rewriting for a specific module by adding the string + ``PYTEST_DONT_REWRITE`` to its docstring. + +* Disable rewriting for all modules by using ``--assert=plain``. + + .. versionadded:: 2.1 Add assert rewriting as an alternate introspection technique. diff --git a/doc/en/reference.rst b/doc/en/reference.rst index 1ed5d91b1..7ab734075 100644 --- a/doc/en/reference.rst +++ b/doc/en/reference.rst @@ -499,6 +499,32 @@ Each recorded warning is an instance of :class:`warnings.WarningMessage`. differently; see :ref:`ensuring_function_triggers`. +tmp_path +~~~~~~~~ + +**Tutorial**: :doc:`tmpdir` + +.. currentmodule:: _pytest.tmpdir + +.. autofunction:: tmp_path() + :no-auto-options: + + +tmp_path_factory +~~~~~~~~~~~~~~~~ + +**Tutorial**: :ref:`tmp_path_factory example` + +.. _`tmp_path_factory factory api`: + +``tmp_path_factory`` instances have the following methods: + +.. currentmodule:: _pytest.tmpdir + +.. automethod:: TempPathFactory.mktemp +.. automethod:: TempPathFactory.getbasetemp + + tmpdir ~~~~~~ diff --git a/doc/en/tmpdir.rst b/doc/en/tmpdir.rst index df490d7fb..e2b8fc32b 100644 --- a/doc/en/tmpdir.rst +++ b/doc/en/tmpdir.rst @@ -66,6 +66,9 @@ Running this would result in a passed test except for the last test_tmp_path.py:13: AssertionError ========================= 1 failed in 0.12 seconds ========================= + +.. _`tmp_path_factory example`: + The ``tmp_path_factory`` fixture -------------------------------- @@ -77,6 +80,8 @@ to create arbitrary temporary directories from any other fixture or test. It is intended to replace ``tmpdir_factory``, and returns :class:`pathlib.Path` instances. +See :ref:`tmp_path_factory API ` for details. + The 'tmpdir' fixture -------------------- diff --git a/scripts/install-pypy.bat b/scripts/install-pypy.bat deleted file mode 100644 index 8012ea46a..000000000 --- a/scripts/install-pypy.bat +++ /dev/null @@ -1,6 +0,0 @@ -REM install pypy using choco -REM redirect to a file because choco install python.pypy is too noisy. If the command fails, write output to console -choco install python.pypy > pypy-inst.log 2>&1 || (type pypy-inst.log & exit /b 1) -set PATH=C:\tools\pypy\pypy;%PATH% # so tox can find pypy -echo PyPy installed -pypy --version diff --git a/scripts/prepare-coverage.bat b/scripts/prepare-coverage.bat deleted file mode 100644 index bff1e62b0..000000000 --- a/scripts/prepare-coverage.bat +++ /dev/null @@ -1,10 +0,0 @@ -REM scripts called by AppVeyor to setup the environment variables to enable coverage -if not defined PYTEST_NO_COVERAGE ( - set "COVERAGE_FILE=%CD%\.coverage" - set "COVERAGE_PROCESS_START=%CD%\.coveragerc" - set "_PYTEST_TOX_COVERAGE_RUN=coverage run -m" - set "_PYTEST_TOX_EXTRA_DEP=coverage-enable-subprocess" - echo Coverage setup completed -) else ( - echo Skipping coverage setup, PYTEST_NO_COVERAGE is set -) diff --git a/scripts/appveyor-retry.cmd b/scripts/retry.cmd similarity index 100% rename from scripts/appveyor-retry.cmd rename to scripts/retry.cmd diff --git a/scripts/setup-coverage-vars.bat b/scripts/setup-coverage-vars.bat new file mode 100644 index 000000000..7a4a6d4de --- /dev/null +++ b/scripts/setup-coverage-vars.bat @@ -0,0 +1,7 @@ +if "%PYTEST_COVERAGE%" == "1" ( + set "_PYTEST_TOX_COVERAGE_RUN=coverage run -m" + set "_PYTEST_TOX_EXTRA_DEP=coverage-enable-subprocess" + echo Coverage vars configured, PYTEST_COVERAGE=%PYTEST_COVERAGE% +) else ( + echo Skipping coverage vars setup, PYTEST_COVERAGE=%PYTEST_COVERAGE% +) diff --git a/scripts/upload-coverage.bat b/scripts/upload-coverage.bat index 7391f817c..b21e0126e 100644 --- a/scripts/upload-coverage.bat +++ b/scripts/upload-coverage.bat @@ -1,11 +1,16 @@ -REM script called by AppVeyor to combine and upload coverage information to codecov -if not defined PYTEST_NO_COVERAGE ( +REM script called by Azure to combine and upload coverage information to codecov +if "%PYTEST_COVERAGE%" == "1" ( echo Prepare to upload coverage information - C:\Python36\Scripts\pip install codecov - C:\Python36\Scripts\coverage combine - C:\Python36\Scripts\coverage xml --ignore-errors - C:\Python36\Scripts\coverage report -m --ignore-errors - scripts\appveyor-retry C:\Python36\Scripts\codecov --required -X gcov pycov search -f coverage.xml --flags windows + if defined CODECOV_TOKEN ( + echo CODECOV_TOKEN defined + ) else ( + echo CODECOV_TOKEN NOT defined + ) + %PYTHON% -m pip install codecov + %PYTHON% -m coverage combine + %PYTHON% -m coverage xml + %PYTHON% -m coverage report -m + scripts\retry %PYTHON% -m codecov --required -X gcov pycov search -f coverage.xml --name %PYTEST_CODECOV_NAME% ) else ( - echo Skipping coverage upload, PYTEST_NO_COVERAGE is set + echo Skipping coverage upload, PYTEST_COVERAGE=%PYTEST_COVERAGE% ) diff --git a/setup.py b/setup.py index 6fca21070..a924d4aba 100644 --- a/setup.py +++ b/setup.py @@ -1,8 +1,5 @@ -import os - from setuptools import setup - # TODO: if py gets upgrade to >=1.6, # remove _width_of_current_line in terminal.py INSTALL_REQUIRES = [ @@ -16,15 +13,10 @@ INSTALL_REQUIRES = [ 'funcsigs>=1.0;python_version<"3.0"', 'pathlib2>=2.2.0;python_version<"3.6"', 'colorama;sys_platform=="win32"', + "pluggy>=0.9", ] -# if _PYTEST_SETUP_SKIP_PLUGGY_DEP is set, skip installing pluggy; -# used by tox.ini to test with pluggy master -if "_PYTEST_SETUP_SKIP_PLUGGY_DEP" not in os.environ: - INSTALL_REQUIRES.append("pluggy>=0.9") - - def main(): setup( use_scm_version={"write_to": "src/_pytest/_version.py"}, @@ -33,6 +25,7 @@ def main(): # fmt: off extras_require={ "testing": [ + "argcomplete", "hypothesis>=3.56", "nose", "requests", diff --git a/src/_pytest/assertion/truncate.py b/src/_pytest/assertion/truncate.py index d19c8b61e..69d6acdef 100644 --- a/src/_pytest/assertion/truncate.py +++ b/src/_pytest/assertion/truncate.py @@ -12,7 +12,6 @@ import os import six - DEFAULT_MAX_LINES = 8 DEFAULT_MAX_CHARS = 8 * 80 USAGE_MSG = "use '-vv' to show" diff --git a/src/_pytest/capture.py b/src/_pytest/capture.py index 533690949..7bd319b1a 100644 --- a/src/_pytest/capture.py +++ b/src/_pytest/capture.py @@ -17,6 +17,7 @@ from tempfile import TemporaryFile import six import pytest +from _pytest.compat import _PY3 from _pytest.compat import CaptureIO patchsysdict = {0: "stdin", 1: "stdout", 2: "stderr"} @@ -412,6 +413,10 @@ class EncodedFile(object): def write(self, obj): if isinstance(obj, six.text_type): obj = obj.encode(self.encoding, "replace") + elif _PY3: + raise TypeError( + "write() argument must be str, not {}".format(type(obj).__name__) + ) self.buffer.write(obj) def writelines(self, linelist): diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index 4afde6902..6134ca77b 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -16,7 +16,6 @@ from _pytest.warning_types import PytestDeprecationWarning from _pytest.warning_types import RemovedInPytest4Warning from _pytest.warning_types import UnformattedWarning - YIELD_TESTS = "yield tests were removed in pytest 4.0 - {name} will be ignored" diff --git a/src/_pytest/hookspec.py b/src/_pytest/hookspec.py index 3b6c9a7d1..0641e3bc5 100644 --- a/src/_pytest/hookspec.py +++ b/src/_pytest/hookspec.py @@ -3,7 +3,6 @@ from pluggy import HookspecMarker from _pytest.deprecated import PYTEST_LOGWARNING - hookspec = HookspecMarker("pytest") # ------------------------------------------------------------------------- diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index 7e906deab..22db44301 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -15,7 +15,6 @@ from _pytest.compat import dummy_context_manager from _pytest.config import create_terminal_writer from _pytest.pathlib import Path - DEFAULT_LOG_FORMAT = "%(filename)-25s %(lineno)4d %(levelname)-8s %(message)s" DEFAULT_LOG_DATE_FORMAT = "%H:%M:%S" diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 6e639d872..d8478d4fc 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -24,7 +24,6 @@ from _pytest.deprecated import PYTEST_CONFIG_GLOBAL from _pytest.outcomes import exit from _pytest.runner import collect_one_node - # exitcodes for the command line EXIT_OK = 0 EXIT_TESTSFAILED = 1 diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index c8a25d156..d65d8b9d8 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -12,7 +12,6 @@ from ..compat import MappingMixin from ..compat import NOTSET from _pytest.outcomes import fail - EMPTY_PARAMETERSET_OPTION = "empty_parameter_set_mark" diff --git a/src/_pytest/pathlib.py b/src/_pytest/pathlib.py index 5ecdd6026..5dd00e74f 100644 --- a/src/_pytest/pathlib.py +++ b/src/_pytest/pathlib.py @@ -19,7 +19,6 @@ from six.moves import map from .compat import PY36 - if PY36: from pathlib import Path, PurePath else: diff --git a/testing/code/test_source.py b/testing/code/test_source.py index fc5eaed04..965838dae 100644 --- a/testing/code/test_source.py +++ b/testing/code/test_source.py @@ -16,7 +16,6 @@ import _pytest._code import pytest from _pytest._code import Source - astonly = pytest.mark.nothing failsonjython = pytest.mark.xfail("sys.platform.startswith('java')") diff --git a/testing/example_scripts/perf_examples/collect_stats/generate_folders.py b/testing/example_scripts/perf_examples/collect_stats/generate_folders.py index 94b3f013f..ff1eaf7d6 100644 --- a/testing/example_scripts/perf_examples/collect_stats/generate_folders.py +++ b/testing/example_scripts/perf_examples/collect_stats/generate_folders.py @@ -1,7 +1,6 @@ import argparse import pathlib - HERE = pathlib.Path(__file__).parent TEST_CONTENT = (HERE / "template_test.py").read_bytes() diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index bfb6acfab..098e106f9 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -1313,8 +1313,7 @@ class TestEarlyRewriteBailout(object): # always (previously triggered via xdist only). # Ref: https://github.com/pytest-dev/py/pull/207 monkeypatch.setattr(sys, "path", [""] + sys.path) - if "pathlib" in sys.modules: - del sys.modules["pathlib"] + monkeypatch.delitem(sys.modules, "pathlib", raising=False) testdir.makepyfile( **{ diff --git a/testing/test_capture.py b/testing/test_capture.py index 81ab4e8a8..91cf8d8cf 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -18,6 +18,7 @@ from six import text_type import pytest from _pytest import capture from _pytest.capture import CaptureManager +from _pytest.compat import _PY3 from _pytest.main import EXIT_NOTESTSCOLLECTED # note: py.io capture tests where copied from @@ -1402,28 +1403,36 @@ def test_dontreadfrominput_has_encoding(testdir): def test_crash_on_closing_tmpfile_py27(testdir): - testdir.makepyfile( + p = testdir.makepyfile( """ from __future__ import print_function - import time import threading import sys + printing = threading.Event() + def spam(): f = sys.stderr - while True: - print('.', end='', file=f) + print('SPAMBEFORE', end='', file=f) + printing.set() - def test_silly(): + while True: + try: + f.flush() + except (OSError, ValueError): + break + + def test_spam_in_thread(): t = threading.Thread(target=spam) t.daemon = True t.start() - time.sleep(0.5) + printing.wait() """ ) - result = testdir.runpytest_subprocess() + result = testdir.runpytest_subprocess(str(p)) assert result.ret == 0 + assert result.stderr.str() == "" assert "IOError" not in result.stdout.str() @@ -1526,3 +1535,26 @@ def test_capture_with_live_logging(testdir, capture_fixture): result = testdir.runpytest_subprocess("--log-cli-level=INFO") assert result.ret == 0 + + +def test_typeerror_encodedfile_write(testdir): + """It should behave the same with and without output capturing (#4861).""" + p = testdir.makepyfile( + """ + def test_fails(): + import sys + sys.stdout.write(b"foo") + """ + ) + result_without_capture = testdir.runpytest("-s", str(p)) + + result_with_capture = testdir.runpytest(str(p)) + + assert result_with_capture.ret == result_without_capture.ret + + if _PY3: + result_with_capture.stdout.fnmatch_lines( + ["E TypeError: write() argument must be str, not bytes"] + ) + else: + assert result_with_capture.ret == 0 diff --git a/testing/test_parseopt.py b/testing/test_parseopt.py index e25705d00..91f9a7d2b 100644 --- a/testing/test_parseopt.py +++ b/testing/test_parseopt.py @@ -299,15 +299,12 @@ def test_argcomplete(testdir, monkeypatch): if not distutils.spawn.find_executable("bash"): pytest.skip("bash not available") script = str(testdir.tmpdir.join("test_argcomplete")) - pytest_bin = sys.argv[0] - if "pytest" not in os.path.basename(pytest_bin): - pytest.skip("need to be run with pytest executable, not {}".format(pytest_bin)) with open(str(script), "w") as fp: # redirect output from argcomplete to stdin and stderr is not trivial # http://stackoverflow.com/q/12589419/1307905 # so we use bash - fp.write('COMP_WORDBREAKS="$COMP_WORDBREAKS" %s 8>&1 9>&2' % pytest_bin) + fp.write('COMP_WORDBREAKS="$COMP_WORDBREAKS" python -m pytest 8>&1 9>&2') # alternative would be exteneded Testdir.{run(),_run(),popen()} to be able # to handle a keyword argument env that replaces os.environ in popen or # extends the copy, advantage: could not forget to restore @@ -323,7 +320,11 @@ def test_argcomplete(testdir, monkeypatch): # argcomplete not found pytest.skip("argcomplete not available") elif not result.stdout.str(): - pytest.skip("bash provided no output, argcomplete not available?") + pytest.skip( + "bash provided no output on stdout, argcomplete not available? (stderr={!r})".format( + result.stderr.str() + ) + ) else: result.stdout.fnmatch_lines(["--funcargs", "--fulltrace"]) os.mkdir("test_argcomplete.d") diff --git a/testing/test_resultlog.py b/testing/test_resultlog.py index 2c5175246..aba5713af 100644 --- a/testing/test_resultlog.py +++ b/testing/test_resultlog.py @@ -12,7 +12,6 @@ from _pytest.resultlog import pytest_configure from _pytest.resultlog import pytest_unconfigure from _pytest.resultlog import ResultLog - pytestmark = pytest.mark.filterwarnings("ignore:--result-log is deprecated") diff --git a/testing/test_warnings.py b/testing/test_warnings.py index 984aae027..1b4231edc 100644 --- a/testing/test_warnings.py +++ b/testing/test_warnings.py @@ -8,7 +8,6 @@ import six import pytest - WARNINGS_SUMMARY_HEADER = "warnings summary" diff --git a/tox.ini b/tox.ini index 87a56f842..97cac6f6b 100644 --- a/tox.ini +++ b/tox.ini @@ -14,30 +14,43 @@ envlist = pypy pypy3 {py27,py37}-{pexpect,xdist,trial,numpy,pluggymaster} - py27-nobyte + py27-nobyte-xdist doctesting py37-freeze docs [testenv] commands = - {env:_PYTEST_TOX_COVERAGE_RUN:} pytest {env:_PYTEST_TOX_ARGS:} {posargs} + {env:_PYTEST_TOX_COVERAGE_RUN:} pytest {posargs:{env:_PYTEST_TOX_DEFAULT_POSARGS:}} coverage: coverage combine coverage: coverage report passenv = USER USERNAME COVERAGE_* TRAVIS PYTEST_ADDOPTS setenv = - _PYTEST_TOX_ARGS=--lsof + _PYTEST_TOX_DEFAULT_POSARGS={env:_PYTEST_TOX_POSARGS_LSOF:} {env:_PYTEST_TOX_POSARGS_PEXPECT:} {env:_PYTEST_TOX_POSARGS_XDIST:} + # Configuration to run with coverage similar to Travis/Appveyor, e.g. # "tox -e py37-coverage". coverage: _PYTEST_TOX_COVERAGE_RUN=coverage run -m coverage: _PYTEST_TOX_EXTRA_DEP=coverage-enable-subprocess coverage: COVERAGE_FILE={toxinidir}/.coverage coverage: COVERAGE_PROCESS_START={toxinidir}/.coveragerc - xdist: _PYTEST_TOX_ARGS={env:_PYTEST_TOX_ARGS:-n auto} + + nobyte: PYTHONDONTWRITEBYTECODE=1 + + lsof: _PYTEST_TOX_POSARGS_LSOF=--lsof + + pexpect: _PYTEST_TOX_PLATFORM=linux|darwin + pexpect: _PYTEST_TOX_POSARGS_PEXPECT=testing/test_pdb.py testing/test_terminal.py testing/test_unittest.py + + xdist: _PYTEST_TOX_POSARGS_XDIST=-n auto extras = testing deps = + numpy: numpy + pexpect: pexpect + pluggymaster: git+https://github.com/pytest-dev/pluggy.git@master xdist: pytest-xdist>=1.13 {env:_PYTEST_TOX_EXTRA_DEP:} +platform = {env:_PYTEST_TOX_PLATFORM:.*} [testenv:py27-subprocess] deps = @@ -54,31 +67,6 @@ basepython = python3 deps = pre-commit>=1.11.0 commands = pre-commit run --all-files --show-diff-on-failure -[testenv:py27-pexpect] -platform = linux|darwin -deps = - {[testenv]deps} - pexpect -commands = - {env:_PYTEST_TOX_COVERAGE_RUN:} pytest {posargs:testing/test_pdb.py testing/test_terminal.py testing/test_unittest.py} - -[testenv:py37-pexpect] -platform = {[testenv:py27-pexpect]platform} -deps = {[testenv:py27-pexpect]deps} -commands = {[testenv:py27-pexpect]commands} - -[testenv:py27-nobyte] -extras = testing -deps = - {[testenv]deps} - pytest-xdist>=1.13 -distribute = true -setenv = - {[testenv]setenv} - PYTHONDONTWRITEBYTECODE=1 -commands = - {env:_PYTEST_TOX_COVERAGE_RUN:} pytest -n auto {posargs} - [testenv:py27-trial] deps = {[testenv]deps} @@ -91,29 +79,6 @@ commands = deps = {[testenv:py27-trial]deps} commands = {[testenv:py27-trial]commands} -[testenv:py27-numpy] -deps = - {[testenv]deps} - numpy -commands= - {env:_PYTEST_TOX_COVERAGE_RUN:} pytest {posargs:testing/python/approx.py} - -[testenv:py37-numpy] -deps = {[testenv:py27-numpy]deps} -commands = {[testenv:py27-numpy]commands} - -[testenv:py27-pluggymaster] -setenv= - {[testenv]setenv} - _PYTEST_SETUP_SKIP_PLUGGY_DEP=1 -deps = - {[testenv]deps} - git+https://github.com/pytest-dev/pluggy.git@master - -[testenv:py37-pluggymaster] -setenv = {[testenv:py27-pluggymaster]setenv} -deps = {[testenv:py27-pluggymaster]deps} - [testenv:docs] basepython = python3 skipsdist = True @@ -211,6 +176,14 @@ filterwarnings = # Do not cause SyntaxError for invalid escape sequences in py37. default:invalid escape sequence:DeprecationWarning pytester_example_dir = testing/example_scripts + [flake8] max-line-length = 120 ignore = E203,W503 + +[isort] +; This config mimics what reorder-python-imports does. +force_single_line = 1 +known_localfolder = pytest,_pytest +known_third_party = test_source,test_excinfo +force_alphabetical_sort_within_sections = 1