diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 34895e1a30f..12fa0d343f1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,17 +7,18 @@ repos: args: [--safe, --quiet] language_version: python3 - repo: https://github.com/asottile/blacken-docs - rev: v0.5.0 + rev: v1.0.0 hooks: - id: blacken-docs additional_dependencies: [black==19.3b0] language_version: python3 - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v2.2.2 + rev: v2.2.3 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: fix-encoding-pragma + args: [--remove] - id: check-yaml - id: debug-statements exclude: _pytest/debugging.py @@ -31,14 +32,14 @@ repos: rev: v1.4.0 hooks: - id: reorder-python-imports - args: ['--application-directories=.:src'] + args: ['--application-directories=.:src', --py3-plus] - repo: https://github.com/asottile/pyupgrade - rev: v1.15.0 + rev: v1.18.0 hooks: - id: pyupgrade - args: [--keep-percent-format] + args: [--py3-plus] - repo: https://github.com/pre-commit/pygrep-hooks - rev: v1.3.0 + rev: v1.4.0 hooks: - id: rst-backticks - repo: local diff --git a/.travis.yml b/.travis.yml index 9914800f808..2f6df4d9ad0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,18 +19,13 @@ install: jobs: include: # OSX tests - first (in test stage), since they are the slower ones. - - &test-macos - os: osx + - os: osx + # NOTE: (tests with) pexpect appear to be buggy on Travis, + # at least with coverage. + # Log: https://travis-ci.org/pytest-dev/pytest/jobs/500358864 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-pexpect,py37-xdist PYTEST_COVERAGE=1 + env: TOXENV=py37-xdist PYTEST_COVERAGE=1 before_install: - which python3 - python3 -V @@ -38,20 +33,14 @@ jobs: - 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 + # Full run of latest supported version, without xdist. + - env: TOXENV=py37 PYTEST_COVERAGE=1 python: '3.7' # Coverage tracking is slow with pypy, skip it. - - env: TOXENV=pypy-xdist - python: 'pypy' - env: TOXENV=pypy3-xdist python: 'pypy3' - - env: TOXENV=py34-xdist - python: '3.4' - env: TOXENV=py35-xdist python: '3.5' @@ -62,12 +51,6 @@ jobs: # Empty PYTEST_ADDOPTS to run this non-verbose. - env: TOXENV=py37-lsof-numpy-xdist PYTEST_COVERAGE=1 PYTEST_ADDOPTS= - # 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). @@ -81,12 +64,7 @@ jobs: if: type = cron - stage: baseline - # Coverage for: - # - _pytest.unittest._handle_skip (via pexpect). - env: TOXENV=py27-pexpect,py27-twisted PYTEST_COVERAGE=1 - python: '2.7' - # Use py36 here for faster baseline. - - env: TOXENV=py36-xdist + env: TOXENV=py36-xdist python: '3.6' - env: TOXENV=linting,docs,doctesting PYTEST_COVERAGE=1 cache: @@ -112,9 +90,6 @@ matrix: allow_failures: - python: '3.8-dev' env: TOXENV=py38-xdist - # Temporary (https://github.com/pytest-dev/pytest/pull/5334). - - env: TOXENV=pypy3-xdist - python: 'pypy3' before_script: - | diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a1bcd19178e..a2bf12d7ea2 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -18,6 +18,96 @@ with advance notice in the **Deprecations** section of releases. .. towncrier release notes start +pytest 4.6.1 (2019-06-02) +========================= + +Bug Fixes +--------- + +- `#5354 `_: Fix ``pytest.mark.parametrize`` when the argvalues is an iterator. + + +- `#5358 `_: Fix assertion rewriting of ``all()`` calls to deal with non-generators. + + +pytest 4.6.0 (2019-05-31) +========================= + +Important +--------- + +The ``4.6.X`` series will be the last series to support **Python 2 and Python 3.4**. + +For more details, see our `Python 2.7 and 3.4 support plan `__. + + +Features +-------- + +- `#4559 `_: Added the ``junit_log_passing_tests`` ini value which can be used to enable or disable logging of passing test output in the Junit XML file. + + +- `#4956 `_: pytester's ``testdir.spawn`` uses ``tmpdir`` as HOME/USERPROFILE directory. + + +- `#5062 `_: Unroll calls to ``all`` to full for-loops with assertion rewriting for better failure messages, especially when using Generator Expressions. + + +- `#5063 `_: Switch from ``pkg_resources`` to ``importlib-metadata`` for entrypoint detection for improved performance and import time. + + +- `#5091 `_: The output for ini options in ``--help`` has been improved. + + +- `#5269 `_: ``pytest.importorskip`` includes the ``ImportError`` now in the default ``reason``. + + +- `#5311 `_: Captured logs that are output for each failing test are formatted using the + ColoredLevelFormatter. + + +- `#5312 `_: Improved formatting of multiline log messages in Python 3. + + + +Bug Fixes +--------- + +- `#2064 `_: The debugging plugin imports the wrapped ``Pdb`` class (``--pdbcls``) on-demand now. + + +- `#4908 `_: The ``pytest_enter_pdb`` hook gets called with post-mortem (``--pdb``). + + +- `#5036 `_: Fix issue where fixtures dependent on other parametrized fixtures would be erroneously parametrized. + + +- `#5256 `_: Handle internal error due to a lone surrogate unicode character not being representable in Jython. + + +- `#5257 `_: Ensure that ``sys.stdout.mode`` does not include ``'b'`` as it is a text stream. + + +- `#5278 `_: Pytest's internal python plugin can be disabled using ``-p no:python`` again. + + +- `#5286 `_: Fix issue with ``disable_test_id_escaping_and_forfeit_all_rights_to_community_support`` option not working when using a list of test IDs in parametrized tests. + + +- `#5330 `_: Show the test module being collected when emitting ``PytestCollectionWarning`` messages for + test classes with ``__init__`` and ``__new__`` methods to make it easier to pin down the problem. + + +- `#5333 `_: Fix regression in 4.5.0 with ``--lf`` not re-running all tests with known failures from non-selected tests. + + + +Improved Documentation +---------------------- + +- `#5250 `_: Expand docs on use of ``setenv`` and ``delenv`` with ``monkeypatch``. + + pytest 4.5.0 (2019-05-11) ========================= diff --git a/HOWTORELEASE.rst b/HOWTORELEASE.rst index b37a245c0be..d0704b17279 100644 --- a/HOWTORELEASE.rst +++ b/HOWTORELEASE.rst @@ -12,6 +12,8 @@ taking a lot of time to make a new one. #. Create a branch ``release-X.Y.Z`` with the version for the release. + * **maintenance releases**: from ``4.6-maintenance``; + * **patch releases**: from the latest ``master``; * **minor releases**: from the latest ``features``; then merge with the latest ``master``; @@ -24,7 +26,8 @@ taking a lot of time to make a new one. This will generate a commit with all the changes ready for pushing. -#. Open a PR for this branch targeting ``master``. +#. Open a PR for this branch targeting ``master`` (or ``4.6-maintenance`` for + maintenance releases). #. After all tests pass and the PR has been approved, publish to PyPI by pushing the tag:: @@ -33,7 +36,16 @@ taking a lot of time to make a new one. Wait for the deploy to complete, then make sure it is `available on PyPI `_. -#. Merge the PR into ``master``. +#. Merge the PR. + +#. If this is a maintenance release, cherry-pick the CHANGELOG / announce + files to the ``master`` branch:: + + git fetch --all --prune + git checkout origin/master -b cherry-pick-maintenance-release + git cherry-pick --no-commit -m1 origin/4.6-maintenance + git checkout origin/master -- changelog + git commit # no arguments #. Send an email announcement with the contents from:: diff --git a/README.rst b/README.rst index 44fa8ac728d..9739a1bda68 100644 --- a/README.rst +++ b/README.rst @@ -85,7 +85,7 @@ Features - Can run `unittest `_ (or trial), `nose `_ test suites out of the box; -- Python 2.7, Python 3.4+, PyPy 2.3, Jython 2.5 (untested); +- Python 3.5+ and PyPy3; - Rich plugin architecture, with over 315+ `external plugins `_ and thriving community; diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 8e50486de55..72e4af732fe 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -4,7 +4,6 @@ trigger: variables: PYTEST_ADDOPTS: "--junitxml=build/test-results/$(tox.env).xml -vv" - python.needs_vc: False COVERAGE_FILE: "$(Build.Repository.LocalPath)/.coverage" COVERAGE_PROCESS_START: "$(Build.Repository.LocalPath)/.coveragerc" PYTEST_COVERAGE: '0' @@ -16,44 +15,10 @@ jobs: vmImage: "vs2017-win2016" strategy: matrix: - py27: - python.version: '2.7' - tox.env: 'py27' - py27-nobyte-lsof-numpy: - python.version: '2.7' - 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-twisted: - python.version: '2.7' - tox.env: 'py27-twisted' - python.needs_vc: True - py27-pluggymaster-xdist: - python.version: '2.7' - 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-twisted,py27-numpy. - PYTEST_COVERAGE: '1' - # -- pypy2 and pypy3 are disabled for now: #5279 -- - # pypy: - # python.version: 'pypy2' - # tox.env: 'pypy' + # -- pypy3 disabled for now: #5279 -- # pypy3: # python.version: 'pypy3' # tox.env: 'pypy3' - py34-xdist: - python.version: '3.4' - tox.env: 'py34-xdist' - # Coverage for: - # - _pytest.compat._bytes_to_ascii - PYTEST_COVERAGE: '1' py35-xdist: python.version: '3.5' tox.env: 'py35-xdist' @@ -87,10 +52,6 @@ jobs: versionSpec: '$(python.version)' architecture: 'x64' - - script: choco install vcpython27 - condition: eq(variables['python.needs_vc'], True) - displayName: 'Install VC for py27' - - script: python -m pip install --upgrade pip && python -m pip install tox displayName: 'Install tox' diff --git a/bench/bench.py b/bench/bench.py index 468ef521957..31cc7ac137c 100644 --- a/bench/bench.py +++ b/bench/bench.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import sys if __name__ == "__main__": diff --git a/bench/bench_argcomplete.py b/bench/bench_argcomplete.py index 297637bad0d..335733df72b 100644 --- a/bench/bench_argcomplete.py +++ b/bench/bench_argcomplete.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # 10000 iterations, just for relative comparison # 2.7.5 3.3.2 # FilesCompleter 75.1109 69.2116 diff --git a/bench/empty.py b/bench/empty.py index bfda88235de..4e7371b6f80 100644 --- a/bench/empty.py +++ b/bench/empty.py @@ -1,3 +1,2 @@ -# -*- coding: utf-8 -*- for i in range(1000): exec("def test_func_%d(): pass" % i) diff --git a/bench/manyparam.py b/bench/manyparam.py index c47e25f5182..1226c73bd9c 100644 --- a/bench/manyparam.py +++ b/bench/manyparam.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest diff --git a/bench/skip.py b/bench/skip.py index 29e03e54fc6..f0c9d1ddbef 100644 --- a/bench/skip.py +++ b/bench/skip.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from six.moves import range - import pytest SKIP = True diff --git a/changelog/2064.bugfix.rst b/changelog/2064.bugfix.rst deleted file mode 100644 index eba593fc568..00000000000 --- a/changelog/2064.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -The debugging plugin imports the wrapped ``Pdb`` class (``--pdbcls``) on-demand now. diff --git a/changelog/4559.feature.rst b/changelog/4559.feature.rst deleted file mode 100644 index ff076aff573..00000000000 --- a/changelog/4559.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Added the ``junit_log_passing_tests`` ini value which can be used to enable and disable logging passing test output in the Junit XML file. diff --git a/changelog/4908.bugfix.rst b/changelog/4908.bugfix.rst deleted file mode 100644 index 2513618a1d9..00000000000 --- a/changelog/4908.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -The ``pytest_enter_pdb`` hook gets called with post-mortem (``--pdb``). diff --git a/changelog/4956.feature.rst b/changelog/4956.feature.rst deleted file mode 100644 index ab8357cdafe..00000000000 --- a/changelog/4956.feature.rst +++ /dev/null @@ -1 +0,0 @@ -pytester's ``testdir.spawn`` uses ``tmpdir`` as HOME/USERPROFILE directory. diff --git a/changelog/5036.bugfix.rst b/changelog/5036.bugfix.rst deleted file mode 100644 index a9c266538ef..00000000000 --- a/changelog/5036.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fix issue where fixtures dependent on other parametrized fixtures would be erroneously parametrized. diff --git a/changelog/5062.feature.rst b/changelog/5062.feature.rst deleted file mode 100644 index e311d161d52..00000000000 --- a/changelog/5062.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Unroll calls to ``all`` to full for-loops for better failure messages, especially when using Generator Expressions. diff --git a/changelog/5063.feature.rst b/changelog/5063.feature.rst deleted file mode 100644 index 21379ef0aa0..00000000000 --- a/changelog/5063.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Switch from ``pkg_resources`` to ``importlib-metadata`` for entrypoint detection for improved performance and import time. diff --git a/changelog/5091.feature.rst b/changelog/5091.feature.rst deleted file mode 100644 index 122c91f533a..00000000000 --- a/changelog/5091.feature.rst +++ /dev/null @@ -1 +0,0 @@ -The output for ini options in ``--help`` has been improved. diff --git a/changelog/5250.doc.rst b/changelog/5250.doc.rst deleted file mode 100644 index 1b4c564a0a4..00000000000 --- a/changelog/5250.doc.rst +++ /dev/null @@ -1 +0,0 @@ -Expand docs on use of ``setenv`` and ``delenv`` with ``monkeypatch``. diff --git a/changelog/5256.bugfix.rst b/changelog/5256.bugfix.rst deleted file mode 100644 index 4df83454e03..00000000000 --- a/changelog/5256.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Handle internal error due to a lone surrogate unicode character not being representable in Jython. diff --git a/changelog/5257.bugfix.rst b/changelog/5257.bugfix.rst deleted file mode 100644 index 5205190882d..00000000000 --- a/changelog/5257.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Ensure that ``sys.stdout.mode`` does not include ``'b'`` as it is a text stream. diff --git a/changelog/5269.feature.rst b/changelog/5269.feature.rst deleted file mode 100644 index 6247bfd5cc1..00000000000 --- a/changelog/5269.feature.rst +++ /dev/null @@ -1 +0,0 @@ -``pytest.importorskip`` includes the ``ImportError`` now in the default ``reason``. diff --git a/changelog/5278.bugfix.rst b/changelog/5278.bugfix.rst deleted file mode 100644 index 4fe70c7fd0a..00000000000 --- a/changelog/5278.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Pytest's internal python plugin can be disabled using ``-p no:python`` again. diff --git a/changelog/5286.bugfix.rst b/changelog/5286.bugfix.rst deleted file mode 100644 index 1d6974b8964..00000000000 --- a/changelog/5286.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fix issue with ``disable_test_id_escaping_and_forfeit_all_rights_to_community_support`` option doesn't work when using a list of test IDs in parametrized tests. diff --git a/changelog/5311.feature.rst b/changelog/5311.feature.rst deleted file mode 100644 index ae7ef99ac8f..00000000000 --- a/changelog/5311.feature.rst +++ /dev/null @@ -1,2 +0,0 @@ -Captured logs that are output for each failing test are formatted using the -ColoredLevelFormatter. diff --git a/changelog/5312.feature.rst b/changelog/5312.feature.rst deleted file mode 100644 index bcd5a736b58..00000000000 --- a/changelog/5312.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Improved formatting of multiline log messages in python3. diff --git a/changelog/5330.bugfix.rst b/changelog/5330.bugfix.rst deleted file mode 100644 index 61c71555207..00000000000 --- a/changelog/5330.bugfix.rst +++ /dev/null @@ -1,2 +0,0 @@ -Show the test module being collected when emitting ``PytestCollectionWarning`` messages for -test classes with ``__init__`` and ``__new__`` methods to make it easier to pin down the problem. diff --git a/changelog/5333.bugfix.rst b/changelog/5333.bugfix.rst deleted file mode 100644 index ac1d79585f5..00000000000 --- a/changelog/5333.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fix regression with ``--lf`` not re-running all tests with known failures from non-selected tests. diff --git a/changelog/5335.bugfix.rst b/changelog/5335.bugfix.rst new file mode 100644 index 00000000000..0a2e99dc9f1 --- /dev/null +++ b/changelog/5335.bugfix.rst @@ -0,0 +1,2 @@ +Colorize level names when the level in the logging format is formatted using +'%(levelname).Xs' (truncated fixed width alignment), where X is an integer. diff --git a/changelog/5354.bugfix.rst b/changelog/5354.bugfix.rst new file mode 100644 index 00000000000..812ea8364aa --- /dev/null +++ b/changelog/5354.bugfix.rst @@ -0,0 +1 @@ +Fix ``pytest.mark.parametrize`` when the argvalues is an iterator. diff --git a/changelog/5358.bugfix.rst b/changelog/5358.bugfix.rst new file mode 100644 index 00000000000..181da1e0ec2 --- /dev/null +++ b/changelog/5358.bugfix.rst @@ -0,0 +1 @@ +Fix assertion rewriting of ``all()`` calls to deal with non-generators. diff --git a/doc/en/_templates/globaltoc.html b/doc/en/_templates/globaltoc.html index 0e088d67ef3..f4d06a99f68 100644 --- a/doc/en/_templates/globaltoc.html +++ b/doc/en/_templates/globaltoc.html @@ -10,6 +10,7 @@

{{ _('Table Of Contents') }}

  • Changelog
  • Contributing
  • Backwards Compatibility
  • +
  • Python 2.7 and 3.4 Support
  • License
  • Contact Channels
  • diff --git a/doc/en/_themes/flask_theme_support.py b/doc/en/_themes/flask_theme_support.py index c5dcdbe2737..b107f2c892e 100644 --- a/doc/en/_themes/flask_theme_support.py +++ b/doc/en/_themes/flask_theme_support.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # flasky extensions. flasky pygments style based on tango style from pygments.style import Style from pygments.token import Comment diff --git a/doc/en/announce/index.rst b/doc/en/announce/index.rst index 8b5ca7b0a04..2c05e2e59cb 100644 --- a/doc/en/announce/index.rst +++ b/doc/en/announce/index.rst @@ -6,6 +6,8 @@ Release announcements :maxdepth: 2 + release-4.6.1 + release-4.6.0 release-4.5.0 release-4.4.2 release-4.4.1 diff --git a/doc/en/announce/release-4.6.0.rst b/doc/en/announce/release-4.6.0.rst new file mode 100644 index 00000000000..373f5d66eb7 --- /dev/null +++ b/doc/en/announce/release-4.6.0.rst @@ -0,0 +1,43 @@ +pytest-4.6.0 +======================================= + +The pytest team is proud to announce the 4.6.0 release! + +pytest is a mature Python testing tool with more than a 2000 tests +against itself, passing on many different interpreters and platforms. + +This release contains a number of bugs fixes and improvements, so users are encouraged +to take a look at the CHANGELOG: + + https://docs.pytest.org/en/latest/changelog.html + +For complete documentation, please visit: + + https://docs.pytest.org/en/latest/ + +As usual, you can upgrade from pypi via: + + pip install -U pytest + +Thanks to all who contributed to this release, among them: + +* Akiomi Kamakura +* Anthony Sottile +* Bruno Oliveira +* Daniel Hahler +* David Röthlisberger +* Evan Kepner +* Jeffrey Rackauckas +* MyComputer +* Nikita Krokosh +* Raul Tambre +* Thomas Hisch +* Tim Hoffmann +* Tomer Keren +* Victor Maryama +* danielx123 +* oleg-yegorov + + +Happy testing, +The Pytest Development Team diff --git a/doc/en/announce/release-4.6.1.rst b/doc/en/announce/release-4.6.1.rst new file mode 100644 index 00000000000..78d017544d2 --- /dev/null +++ b/doc/en/announce/release-4.6.1.rst @@ -0,0 +1,19 @@ +pytest-4.6.1 +======================================= + +pytest 4.6.1 has just been released to PyPI. + +This is a bug-fix release, being a drop-in replacement. To upgrade:: + + pip install --upgrade pytest + +The full changelog is available at https://docs.pytest.org/en/latest/changelog.html. + +Thanks to all who contributed to this release, among them: + +* Anthony Sottile +* Bruno Oliveira + + +Happy testing, +The pytest Development Team diff --git a/doc/en/conf.py b/doc/en/conf.py index 5daa15a069e..42dc18fd8cf 100644 --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # pytest documentation build configuration file, created by # sphinx-quickstart on Fri Oct 8 17:54:28 2010. @@ -63,9 +62,9 @@ master_doc = "contents" # General information about the project. -project = u"pytest" +project = "pytest" year = datetime.datetime.utcnow().year -copyright = u"2015–2019 , holger krekel and pytest-dev team" +copyright = "2015–2019 , holger krekel and pytest-dev team" # The language for content autogenerated by Sphinx. Refer to documentation @@ -233,8 +232,8 @@ ( "contents", "pytest.tex", - u"pytest Documentation", - u"holger krekel, trainer and consultant, http://merlinux.eu", + "pytest Documentation", + "holger krekel, trainer and consultant, http://merlinux.eu", "manual", ) ] @@ -266,16 +265,16 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [("usage", "pytest", u"pytest usage", [u"holger krekel at merlinux eu"], 1)] +man_pages = [("usage", "pytest", "pytest usage", ["holger krekel at merlinux eu"], 1)] # -- Options for Epub output --------------------------------------------------- # Bibliographic Dublin Core info. -epub_title = u"pytest" -epub_author = u"holger krekel at merlinux eu" -epub_publisher = u"holger krekel at merlinux eu" -epub_copyright = u"2013, holger krekel et alii" +epub_title = "pytest" +epub_author = "holger krekel at merlinux eu" +epub_publisher = "holger krekel at merlinux eu" +epub_copyright = "2013, holger krekel et alii" # The language of the text. It defaults to the language option # or en if the language is not set. diff --git a/doc/en/conftest.py b/doc/en/conftest.py index b51aae5b685..1a62e1b5df5 100644 --- a/doc/en/conftest.py +++ b/doc/en/conftest.py @@ -1,2 +1 @@ -# -*- coding: utf-8 -*- collect_ignore = ["conf.py"] diff --git a/doc/en/example/assertion/failure_demo.py b/doc/en/example/assertion/failure_demo.py index ba910ef6a88..3a307816f00 100644 --- a/doc/en/example/assertion/failure_demo.py +++ b/doc/en/example/assertion/failure_demo.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import _pytest._code import pytest from pytest import raises @@ -21,7 +20,7 @@ def test_generative(param1, param2): assert param1 * 2 < param2 -class TestFailing(object): +class TestFailing: def test_simple(self): def f(): return 42 @@ -41,7 +40,7 @@ def f(): assert not f() -class TestSpecialisedExplanations(object): +class TestSpecialisedExplanations: def test_eq_text(self): assert "spam" == "eggs" @@ -101,7 +100,7 @@ def test_eq_dataclass(self): from dataclasses import dataclass @dataclass - class Foo(object): + class Foo: a: int b: str @@ -113,7 +112,7 @@ def test_eq_attrs(self): import attr @attr.s - class Foo(object): + class Foo: a = attr.ib() b = attr.ib() @@ -123,7 +122,7 @@ class Foo(object): def test_attribute(): - class Foo(object): + class Foo: b = 1 i = Foo() @@ -131,14 +130,14 @@ class Foo(object): def test_attribute_instance(): - class Foo(object): + class Foo: b = 1 assert Foo().b == 2 def test_attribute_failure(): - class Foo(object): + class Foo: def _get_b(self): raise Exception("Failed to get attrib") @@ -149,10 +148,10 @@ def _get_b(self): def test_attribute_multiple(): - class Foo(object): + class Foo: b = 1 - class Bar(object): + class Bar: b = 2 assert Foo().b == Bar().b @@ -162,7 +161,7 @@ def globf(x): return x + 1 -class TestRaises(object): +class TestRaises: def test_raises(self): s = "qwe" raises(TypeError, int, s) @@ -203,7 +202,7 @@ def test_dynamic_compile_shows_nicely(): module.foo() -class TestMoreErrors(object): +class TestMoreErrors: def test_complex_error(self): def f(): return 44 @@ -253,16 +252,16 @@ def test_try_finally(self): x = 0 -class TestCustomAssertMsg(object): +class TestCustomAssertMsg: def test_single_line(self): - class A(object): + class A: a = 1 b = 2 assert A.a == b, "A.a appears not to be b" def test_multiline(self): - class A(object): + class A: a = 1 b = 2 @@ -271,7 +270,7 @@ class A(object): ), "A.a appears not to be b\nor does not appear to be b\none of those" def test_custom_repr(self): - class JSON(object): + class JSON: a = 1 def __repr__(self): diff --git a/doc/en/example/assertion/global_testmodule_config/conftest.py b/doc/en/example/assertion/global_testmodule_config/conftest.py index 8e04ac2ac46..da89047fe09 100644 --- a/doc/en/example/assertion/global_testmodule_config/conftest.py +++ b/doc/en/example/assertion/global_testmodule_config/conftest.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import py import pytest diff --git a/doc/en/example/assertion/global_testmodule_config/test_hello_world.py b/doc/en/example/assertion/global_testmodule_config/test_hello_world.py index bfccc94f712..a31a601a1ce 100644 --- a/doc/en/example/assertion/global_testmodule_config/test_hello_world.py +++ b/doc/en/example/assertion/global_testmodule_config/test_hello_world.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- hello = "world" diff --git a/doc/en/example/assertion/test_failures.py b/doc/en/example/assertion/test_failures.py index 60b5e213118..30ebc72dc37 100644 --- a/doc/en/example/assertion/test_failures.py +++ b/doc/en/example/assertion/test_failures.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import py failure_demo = py.path.local(__file__).dirpath("failure_demo.py") diff --git a/doc/en/example/assertion/test_setup_flow_example.py b/doc/en/example/assertion/test_setup_flow_example.py index f49d6d8ed31..0e7eded06b6 100644 --- a/doc/en/example/assertion/test_setup_flow_example.py +++ b/doc/en/example/assertion/test_setup_flow_example.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- def setup_module(module): module.TestStateFullThing.classcount = 0 -class TestStateFullThing(object): +class TestStateFullThing: def setup_class(cls): cls.classcount += 1 diff --git a/doc/en/example/conftest.py b/doc/en/example/conftest.py index 1f5a961ada0..f905738c4f6 100644 --- a/doc/en/example/conftest.py +++ b/doc/en/example/conftest.py @@ -1,2 +1 @@ -# -*- coding: utf-8 -*- collect_ignore = ["nonpython"] diff --git a/doc/en/example/costlysetup/conftest.py b/doc/en/example/costlysetup/conftest.py index b90c774733b..8fa9c9fe475 100644 --- a/doc/en/example/costlysetup/conftest.py +++ b/doc/en/example/costlysetup/conftest.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest @@ -9,7 +8,7 @@ def setup(request): setup.finalize() -class CostlySetup(object): +class CostlySetup: def __init__(self): import time diff --git a/doc/en/example/costlysetup/sub_a/__init__.py b/doc/en/example/costlysetup/sub_a/__init__.py index ec51c5a2b9d..792d6005489 100644 --- a/doc/en/example/costlysetup/sub_a/__init__.py +++ b/doc/en/example/costlysetup/sub_a/__init__.py @@ -1,2 +1 @@ -# -*- coding: utf-8 -*- # diff --git a/doc/en/example/costlysetup/sub_a/test_quick.py b/doc/en/example/costlysetup/sub_a/test_quick.py index 4f7b9f1ac7d..38dda2660d2 100644 --- a/doc/en/example/costlysetup/sub_a/test_quick.py +++ b/doc/en/example/costlysetup/sub_a/test_quick.py @@ -1,3 +1,2 @@ -# -*- coding: utf-8 -*- def test_quick(setup): pass diff --git a/doc/en/example/costlysetup/sub_b/__init__.py b/doc/en/example/costlysetup/sub_b/__init__.py index ec51c5a2b9d..792d6005489 100644 --- a/doc/en/example/costlysetup/sub_b/__init__.py +++ b/doc/en/example/costlysetup/sub_b/__init__.py @@ -1,2 +1 @@ -# -*- coding: utf-8 -*- # diff --git a/doc/en/example/costlysetup/sub_b/test_two.py b/doc/en/example/costlysetup/sub_b/test_two.py index 4e08bc2b69e..b1653aaab88 100644 --- a/doc/en/example/costlysetup/sub_b/test_two.py +++ b/doc/en/example/costlysetup/sub_b/test_two.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- def test_something(setup): assert setup.timecostly == 1 diff --git a/doc/en/example/markers.rst b/doc/en/example/markers.rst index b8d8bef0cb1..c3381600fb7 100644 --- a/doc/en/example/markers.rst +++ b/doc/en/example/markers.rst @@ -288,8 +288,7 @@ its test methods: This is equivalent to directly applying the decorator to the two test functions. -To remain backward-compatible with Python 2.4 you can also set a -``pytestmark`` attribute on a TestClass like this: +Due to legacy reasons, it is possible to set the ``pytestmark`` attribute on a TestClass like this: .. code-block:: python diff --git a/doc/en/example/multipython.py b/doc/en/example/multipython.py index dc722aac91d..c06e452df26 100644 --- a/doc/en/example/multipython.py +++ b/doc/en/example/multipython.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ module containing a parametrized tests testing cross-python serialization via the pickle module. @@ -9,7 +8,7 @@ import pytest -pythonlist = ["python2.7", "python3.4", "python3.5"] +pythonlist = ["python3.5", "python3.6", "python3.7"] @pytest.fixture(params=pythonlist) @@ -23,7 +22,7 @@ def python2(request, python1): return Python(request.param, python1.picklefile) -class Python(object): +class Python: def __init__(self, version, picklefile): self.pythonpath = distutils.spawn.find_executable(version) if not self.pythonpath: diff --git a/doc/en/example/nonpython/conftest.py b/doc/en/example/nonpython/conftest.py index 306aa250379..a1210320ea3 100644 --- a/doc/en/example/nonpython/conftest.py +++ b/doc/en/example/nonpython/conftest.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # content of conftest.py import pytest @@ -19,7 +18,7 @@ def collect(self): class YamlItem(pytest.Item): def __init__(self, name, parent, spec): - super(YamlItem, self).__init__(name, parent) + super().__init__(name, parent) self.spec = spec def runtest(self): diff --git a/doc/en/example/parametrize.rst b/doc/en/example/parametrize.rst index c3f8252978e..d324fc106cb 100644 --- a/doc/en/example/parametrize.rst +++ b/doc/en/example/parametrize.rst @@ -429,14 +429,14 @@ is to be run with different sets of arguments for its three arguments: .. literalinclude:: multipython.py -Running it results in some skips if we don't have all the python interpreters installed and otherwise runs all combinations (5 interpreters times 5 interpreters times 3 objects to serialize/deserialize): +Running it results in some skips if we don't have all the python interpreters installed and otherwise runs all combinations (3 interpreters times 3 interpreters times 3 objects to serialize/deserialize): .. code-block:: pytest . $ pytest -rs -q multipython.py ...sss...sssssssss...sss... [100%] ========================= short test summary info ========================== - SKIPPED [15] $REGENDOC_TMPDIR/CWD/multipython.py:30: 'python3.4' not found + SKIPPED [15] $REGENDOC_TMPDIR/CWD/multipython.py:31: 'python3.4' not found 12 passed, 15 skipped in 0.12 seconds Indirect parametrization of optional implementations/imports @@ -494,7 +494,7 @@ If you run this with reporting for skips enabled: test_module.py .s [100%] ========================= short test summary info ========================== - SKIPPED [1] $REGENDOC_TMPDIR/conftest.py:11: could not import 'opt2' + SKIPPED [1] $REGENDOC_TMPDIR/conftest.py:11: could not import 'opt2': No module named 'opt2' =================== 1 passed, 1 skipped in 0.12 seconds ==================== You'll see that we don't have an ``opt2`` module and thus the second test run diff --git a/doc/en/example/py2py3/conftest.py b/doc/en/example/py2py3/conftest.py index a6b9af7a860..844510a25a3 100644 --- a/doc/en/example/py2py3/conftest.py +++ b/doc/en/example/py2py3/conftest.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import sys import pytest diff --git a/doc/en/example/py2py3/test_py3.py b/doc/en/example/py2py3/test_py3.py index 30151f914a8..d95702a5348 100644 --- a/doc/en/example/py2py3/test_py3.py +++ b/doc/en/example/py2py3/test_py3.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- def test_exception_syntax(): try: 0 / 0 diff --git a/doc/en/example/pythoncollection.py b/doc/en/example/pythoncollection.py index bbc3fe868ea..8742526a191 100644 --- a/doc/en/example/pythoncollection.py +++ b/doc/en/example/pythoncollection.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # run this with $ pytest --collect-only test_collectonly.py # @@ -7,7 +6,7 @@ def test_function(): pass -class TestClass(object): +class TestClass: def test_method(self): pass diff --git a/doc/en/example/reportingdemo.rst b/doc/en/example/reportingdemo.rst index 1371d9cfa68..928c365cafc 100644 --- a/doc/en/example/reportingdemo.rst +++ b/doc/en/example/reportingdemo.rst @@ -26,7 +26,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: > assert param1 * 2 < param2 E assert (3 * 2) < 6 - failure_demo.py:20: AssertionError + failure_demo.py:21: AssertionError _________________________ TestFailing.test_simple __________________________ self = @@ -43,7 +43,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: E + where 42 = .f at 0xdeadbeef>() E + and 43 = .g at 0xdeadbeef>() - failure_demo.py:31: AssertionError + failure_demo.py:32: AssertionError ____________________ TestFailing.test_simple_multiline _____________________ self = @@ -51,7 +51,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: def test_simple_multiline(self): > otherfunc_multi(42, 6 * 9) - failure_demo.py:34: + failure_demo.py:35: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ a = 42, b = 54 @@ -60,7 +60,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: > assert a == b E assert 42 == 54 - failure_demo.py:15: AssertionError + failure_demo.py:16: AssertionError ___________________________ TestFailing.test_not ___________________________ self = @@ -73,7 +73,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: E assert not 42 E + where 42 = .f at 0xdeadbeef>() - failure_demo.py:40: AssertionError + failure_demo.py:41: AssertionError _________________ TestSpecialisedExplanations.test_eq_text _________________ self = @@ -84,7 +84,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: E - spam E + eggs - failure_demo.py:45: AssertionError + failure_demo.py:46: AssertionError _____________ TestSpecialisedExplanations.test_eq_similar_text _____________ self = @@ -97,7 +97,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: E + foo 2 bar E ? ^ - failure_demo.py:48: AssertionError + failure_demo.py:49: AssertionError ____________ TestSpecialisedExplanations.test_eq_multiline_text ____________ self = @@ -110,7 +110,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: E + eggs E bar - failure_demo.py:51: AssertionError + failure_demo.py:52: AssertionError ______________ TestSpecialisedExplanations.test_eq_long_text _______________ self = @@ -127,7 +127,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: E + 1111111111b222222222 E ? ^ - failure_demo.py:56: AssertionError + failure_demo.py:57: AssertionError _________ TestSpecialisedExplanations.test_eq_long_text_multiline __________ self = @@ -147,7 +147,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: E E ...Full output truncated (7 lines hidden), use '-vv' to show - failure_demo.py:61: AssertionError + failure_demo.py:62: AssertionError _________________ TestSpecialisedExplanations.test_eq_list _________________ self = @@ -158,7 +158,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: E At index 2 diff: 2 != 3 E Use -v to get the full diff - failure_demo.py:64: AssertionError + failure_demo.py:65: AssertionError ______________ TestSpecialisedExplanations.test_eq_list_long _______________ self = @@ -171,7 +171,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: E At index 100 diff: 1 != 2 E Use -v to get the full diff - failure_demo.py:69: AssertionError + failure_demo.py:70: AssertionError _________________ TestSpecialisedExplanations.test_eq_dict _________________ self = @@ -189,7 +189,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: E E ...Full output truncated (2 lines hidden), use '-vv' to show - failure_demo.py:72: AssertionError + failure_demo.py:73: AssertionError _________________ TestSpecialisedExplanations.test_eq_set __________________ self = @@ -207,7 +207,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: E E ...Full output truncated (2 lines hidden), use '-vv' to show - failure_demo.py:75: AssertionError + failure_demo.py:76: AssertionError _____________ TestSpecialisedExplanations.test_eq_longer_list ______________ self = @@ -218,7 +218,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: E Right contains one more item: 3 E Use -v to get the full diff - failure_demo.py:78: AssertionError + failure_demo.py:79: AssertionError _________________ TestSpecialisedExplanations.test_in_list _________________ self = @@ -227,7 +227,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: > assert 1 in [0, 2, 3, 4, 5] E assert 1 in [0, 2, 3, 4, 5] - failure_demo.py:81: AssertionError + failure_demo.py:82: AssertionError __________ TestSpecialisedExplanations.test_not_in_text_multiline __________ self = @@ -246,7 +246,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: E E ...Full output truncated (2 lines hidden), use '-vv' to show - failure_demo.py:85: AssertionError + failure_demo.py:86: AssertionError ___________ TestSpecialisedExplanations.test_not_in_text_single ____________ self = @@ -259,7 +259,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: E single foo line E ? +++ - failure_demo.py:89: AssertionError + failure_demo.py:90: AssertionError _________ TestSpecialisedExplanations.test_not_in_text_single_long _________ self = @@ -272,7 +272,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: E head head foo tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail E ? +++ - failure_demo.py:93: AssertionError + failure_demo.py:94: AssertionError ______ TestSpecialisedExplanations.test_not_in_text_single_long_term _______ self = @@ -285,7 +285,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: E head head fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffftail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail E ? ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - failure_demo.py:97: AssertionError + failure_demo.py:98: AssertionError ______________ TestSpecialisedExplanations.test_eq_dataclass _______________ self = @@ -306,7 +306,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: E Differing attributes: E b: 'b' != 'c' - failure_demo.py:109: AssertionError + failure_demo.py:110: AssertionError ________________ TestSpecialisedExplanations.test_eq_attrs _________________ self = @@ -327,7 +327,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: E Differing attributes: E b: 'b' != 'c' - failure_demo.py:121: AssertionError + failure_demo.py:122: AssertionError ______________________________ test_attribute ______________________________ def test_attribute(): @@ -339,7 +339,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: E assert 1 == 2 E + where 1 = .Foo object at 0xdeadbeef>.b - failure_demo.py:129: AssertionError + failure_demo.py:130: AssertionError _________________________ test_attribute_instance __________________________ def test_attribute_instance(): @@ -351,7 +351,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: E + where 1 = .Foo object at 0xdeadbeef>.b E + where .Foo object at 0xdeadbeef> = .Foo'>() - failure_demo.py:136: AssertionError + failure_demo.py:137: AssertionError __________________________ test_attribute_failure __________________________ def test_attribute_failure(): @@ -364,7 +364,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: i = Foo() > assert i.b == 2 - failure_demo.py:147: + failure_demo.py:148: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = .Foo object at 0xdeadbeef> @@ -373,7 +373,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: > raise Exception("Failed to get attrib") E Exception: Failed to get attrib - failure_demo.py:142: Exception + failure_demo.py:143: Exception _________________________ test_attribute_multiple __________________________ def test_attribute_multiple(): @@ -390,7 +390,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: E + and 2 = .Bar object at 0xdeadbeef>.b E + where .Bar object at 0xdeadbeef> = .Bar'>() - failure_demo.py:157: AssertionError + failure_demo.py:158: AssertionError __________________________ TestRaises.test_raises __________________________ self = @@ -400,7 +400,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: > raises(TypeError, int, s) E ValueError: invalid literal for int() with base 10: 'qwe' - failure_demo.py:167: ValueError + failure_demo.py:168: ValueError ______________________ TestRaises.test_raises_doesnt _______________________ self = @@ -409,7 +409,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: > raises(IOError, int, "3") E Failed: DID NOT RAISE - failure_demo.py:170: Failed + failure_demo.py:171: Failed __________________________ TestRaises.test_raise ___________________________ self = @@ -418,7 +418,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: > raise ValueError("demo error") E ValueError: demo error - failure_demo.py:173: ValueError + failure_demo.py:174: ValueError ________________________ TestRaises.test_tupleerror ________________________ self = @@ -427,7 +427,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: > a, b = [1] # NOQA E ValueError: not enough values to unpack (expected 2, got 1) - failure_demo.py:176: ValueError + failure_demo.py:177: ValueError ______ TestRaises.test_reinterpret_fails_with_print_for_the_fun_of_it ______ self = @@ -438,7 +438,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: > a, b = items.pop() E TypeError: 'int' object is not iterable - failure_demo.py:181: TypeError + failure_demo.py:182: TypeError --------------------------- Captured stdout call --------------------------- items is [1, 2, 3] ________________________ TestRaises.test_some_error ________________________ @@ -449,7 +449,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: > if namenotexi: # NOQA E NameError: name 'namenotexi' is not defined - failure_demo.py:184: NameError + failure_demo.py:185: NameError ____________________ test_dynamic_compile_shows_nicely _____________________ def test_dynamic_compile_shows_nicely(): @@ -464,14 +464,14 @@ Here is a nice run of several failures and how ``pytest`` presents things: sys.modules[name] = module > module.foo() - failure_demo.py:202: + failure_demo.py:203: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ def foo(): > assert 1 == 0 E AssertionError - <0-codegen 'abc-123' $REGENDOC_TMPDIR/assertion/failure_demo.py:199>:2: AssertionError + <0-codegen 'abc-123' $REGENDOC_TMPDIR/assertion/failure_demo.py:200>:2: AssertionError ____________________ TestMoreErrors.test_complex_error _____________________ self = @@ -485,9 +485,9 @@ Here is a nice run of several failures and how ``pytest`` presents things: > somefunc(f(), g()) - failure_demo.py:213: + failure_demo.py:214: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ - failure_demo.py:11: in somefunc + failure_demo.py:12: in somefunc otherfunc(x, y) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ @@ -497,7 +497,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: > assert a == b E assert 44 == 43 - failure_demo.py:7: AssertionError + failure_demo.py:8: AssertionError ___________________ TestMoreErrors.test_z1_unpack_error ____________________ self = @@ -507,7 +507,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: > a, b = items E ValueError: not enough values to unpack (expected 2, got 0) - failure_demo.py:217: ValueError + failure_demo.py:218: ValueError ____________________ TestMoreErrors.test_z2_type_error _____________________ self = @@ -517,7 +517,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: > a, b = items E TypeError: 'int' object is not iterable - failure_demo.py:221: TypeError + failure_demo.py:222: TypeError ______________________ TestMoreErrors.test_startswith ______________________ self = @@ -530,7 +530,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: E + where False = ('456') E + where = '123'.startswith - failure_demo.py:226: AssertionError + failure_demo.py:227: AssertionError __________________ TestMoreErrors.test_startswith_nested ___________________ self = @@ -549,7 +549,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: E + where '123' = .f at 0xdeadbeef>() E + and '456' = .g at 0xdeadbeef>() - failure_demo.py:235: AssertionError + failure_demo.py:236: AssertionError _____________________ TestMoreErrors.test_global_func ______________________ self = @@ -560,7 +560,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: E + where False = isinstance(43, float) E + where 43 = globf(42) - failure_demo.py:238: AssertionError + failure_demo.py:239: AssertionError _______________________ TestMoreErrors.test_instance _______________________ self = @@ -571,7 +571,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: E assert 42 != 42 E + where 42 = .x - failure_demo.py:242: AssertionError + failure_demo.py:243: AssertionError _______________________ TestMoreErrors.test_compare ________________________ self = @@ -581,7 +581,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: E assert 11 < 5 E + where 11 = globf(10) - failure_demo.py:245: AssertionError + failure_demo.py:246: AssertionError _____________________ TestMoreErrors.test_try_finally ______________________ self = @@ -592,7 +592,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: > assert x == 0 E assert 1 == 0 - failure_demo.py:250: AssertionError + failure_demo.py:251: AssertionError ___________________ TestCustomAssertMsg.test_single_line ___________________ self = @@ -607,7 +607,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: E assert 1 == 2 E + where 1 = .A'>.a - failure_demo.py:261: AssertionError + failure_demo.py:262: AssertionError ____________________ TestCustomAssertMsg.test_multiline ____________________ self = @@ -626,7 +626,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: E assert 1 == 2 E + where 1 = .A'>.a - failure_demo.py:268: AssertionError + failure_demo.py:269: AssertionError ___________________ TestCustomAssertMsg.test_custom_repr ___________________ self = @@ -648,5 +648,5 @@ Here is a nice run of several failures and how ``pytest`` presents things: E assert 1 == 2 E + where 1 = This is JSON\n{\n 'foo': 'bar'\n}.a - failure_demo.py:281: AssertionError + failure_demo.py:282: AssertionError ======================== 44 failed in 0.12 seconds ========================= diff --git a/doc/en/example/simple.rst b/doc/en/example/simple.rst index 140f4b840f1..cf8298ddc2a 100644 --- a/doc/en/example/simple.rst +++ b/doc/en/example/simple.rst @@ -441,7 +441,7 @@ Now we can profile which test functions execute the slowest: ========================= slowest 3 test durations ========================= 0.30s call test_some_are_slow.py::test_funcslow2 - 0.20s call test_some_are_slow.py::test_funcslow1 + 0.21s call test_some_are_slow.py::test_funcslow1 0.10s call test_some_are_slow.py::test_funcfast ========================= 3 passed in 0.12 seconds ========================= diff --git a/doc/en/example/xfail_demo.py b/doc/en/example/xfail_demo.py index 88384a1562a..01e6da1ad2e 100644 --- a/doc/en/example/xfail_demo.py +++ b/doc/en/example/xfail_demo.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest xfail = pytest.mark.xfail diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index d6c62cbe8c0..5a7b79ec3fa 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -1,9 +1,9 @@ Installation and Getting Started =================================== -**Pythons**: Python 2.7, 3.4, 3.5, 3.6, 3.7, Jython, PyPy-2.3 +**Pythons**: Python 3.5, 3.6, 3.7, PyPy3 -**Platforms**: Unix/Posix and Windows +**Platforms**: Linux and Windows **PyPI package name**: `pytest `_ diff --git a/doc/en/goodpractices.rst b/doc/en/goodpractices.rst index f08bd5c4078..2314c00660b 100644 --- a/doc/en/goodpractices.rst +++ b/doc/en/goodpractices.rst @@ -7,8 +7,7 @@ Good Integration Practices Install package with pip ------------------------------------------------- -For development, we recommend you use venv_ for virtual environments -(or virtualenv_ for Python 2.7) and +For development, we recommend you use venv_ for virtual environments and pip_ for installing your application and any dependencies, as well as the ``pytest`` package itself. This ensures your code and dependencies are isolated from your system Python installation. diff --git a/doc/en/index.rst b/doc/en/index.rst index 3ace95effe6..5439770fbde 100644 --- a/doc/en/index.rst +++ b/doc/en/index.rst @@ -61,7 +61,7 @@ Features - Can run :ref:`unittest ` (including trial) and :ref:`nose ` test suites out of the box; -- Python 2.7, Python 3.4+, PyPy 2.3, Jython 2.5 (untested); +- Python Python 3.5+ and PyPy 3; - Rich plugin architecture, with over 315+ `external plugins `_ and thriving community; diff --git a/doc/en/py27-py34-deprecation.rst b/doc/en/py27-py34-deprecation.rst index 95e96de0456..5c2f0610531 100644 --- a/doc/en/py27-py34-deprecation.rst +++ b/doc/en/py27-py34-deprecation.rst @@ -23,4 +23,4 @@ back-porting patches to the ``4.6-maintenance`` branch that affect Python 2 user branch will continue to exist so the community itself can contribute patches. The core team will be happy to accept those patches and make new ``4.6`` releases **until mid-2020**. -.. _`python_requires`: https://packaging.python.org/guides/distributing-packages-using-setuptools/#python-requires> +.. _`python_requires`: https://packaging.python.org/guides/distributing-packages-using-setuptools/#python-requires diff --git a/doc/en/warnings.rst b/doc/en/warnings.rst index dd10d94f41d..8f8d6f3e1c6 100644 --- a/doc/en/warnings.rst +++ b/doc/en/warnings.rst @@ -400,7 +400,7 @@ defines an ``__init__`` constructor, as this prevents the class from being insta ============================= warnings summary ============================= test_pytest_warnings.py:1 - $REGENDOC_TMPDIR/test_pytest_warnings.py:1: PytestCollectionWarning: cannot collect test class 'Test' because it has a __init__ constructor + $REGENDOC_TMPDIR/test_pytest_warnings.py:1: PytestCollectionWarning: cannot collect test class 'Test' because it has a __init__ constructor (from: test_pytest_warnings.py) class Test: -- Docs: https://docs.pytest.org/en/latest/warnings.html diff --git a/doc/en/writing_plugins.rst b/doc/en/writing_plugins.rst index 04dc68b642d..469ee57ca5c 100644 --- a/doc/en/writing_plugins.rst +++ b/doc/en/writing_plugins.rst @@ -335,8 +335,6 @@ string value of ``Hello World!`` if we do not supply a value or ``Hello .. code-block:: python - # -*- coding: utf-8 -*- - import pytest diff --git a/extra/get_issues.py b/extra/get_issues.py index 598a1af409f..9407aeded7d 100644 --- a/extra/get_issues.py +++ b/extra/get_issues.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import json import py diff --git a/extra/setup-py.test/setup.py b/extra/setup-py.test/setup.py index cfe18c28f52..d0560ce1f5f 100644 --- a/extra/setup-py.test/setup.py +++ b/extra/setup-py.test/setup.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import sys from distutils.core import setup diff --git a/scripts/release.py b/scripts/release.py index 32178fcf674..5009df359e6 100644 --- a/scripts/release.py +++ b/scripts/release.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Invoke development tasks. """ diff --git a/setup.cfg b/setup.cfg index 9d0aa332e94..2d6e5bee1e7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,4 @@ [metadata] - name = pytest description = pytest: simple powerful testing with Python long_description = file: README.rst @@ -23,13 +22,11 @@ classifiers = Topic :: Software Development :: Testing Topic :: Software Development :: Libraries Topic :: Utilities - Programming Language :: Python :: 2 - Programming Language :: Python :: 2.7 - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.4 + Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 platforms = unix, linux, osx, cygwin, win32 [options] @@ -43,8 +40,7 @@ packages = _pytest.mark py_modules = pytest -python_requires = >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* - +python_requires = >=3.5 [options.entry_points] console_scripts = @@ -59,13 +55,9 @@ all_files = 1 [upload_sphinx] upload-dir = doc/en/build/html -[bdist_wheel] -universal = 1 - [check-manifest] ignore = _pytest/_version.py - [devpi:upload] formats = sdist.tgz,bdist_wheel diff --git a/setup.py b/setup.py index 18d32201caf..4c87c6429bb 100644 --- a/setup.py +++ b/setup.py @@ -1,17 +1,13 @@ -# -*- coding: utf-8 -*- from setuptools import setup # TODO: if py gets upgrade to >=1.6, # remove _width_of_current_line in terminal.py INSTALL_REQUIRES = [ "py>=1.5.0", - "six>=1.10.0", "packaging", "attrs>=17.4.0", - 'more-itertools>=4.0.0,<6.0.0;python_version<="2.7"', - 'more-itertools>=4.0.0;python_version>"2.7"', + "more-itertools>=4.0.0", "atomicwrites>=1.0", - 'funcsigs>=1.0;python_version<"3.0"', 'pathlib2>=2.2.0;python_version<"3.6"', 'colorama;sys_platform=="win32"', "pluggy>=0.12,<1.0", @@ -30,9 +26,9 @@ def main(): "testing": [ "argcomplete", "hypothesis>=3.56", + "mock", "nose", "requests", - "mock;python_version=='2.7'", ], }, # fmt: on diff --git a/src/_pytest/__init__.py b/src/_pytest/__init__.py index 17cc20b615a..46c7827ed5e 100644 --- a/src/_pytest/__init__.py +++ b/src/_pytest/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- __all__ = ["__version__"] try: diff --git a/src/_pytest/_argcomplete.py b/src/_pytest/_argcomplete.py index c6cf1d8fddd..1ebf7432ca1 100644 --- a/src/_pytest/_argcomplete.py +++ b/src/_pytest/_argcomplete.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """allow bash-completion for argparse with argcomplete if installed needs argcomplete>=0.5.6 for python 3.2/3.3 (older versions fail to find the magic string, so _ARGCOMPLETE env. var is never set, and @@ -54,16 +53,12 @@ which should throw a KeyError: 'COMPLINE' (which is properly set by the global argcomplete script). """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import os import sys from glob import glob -class FastFilesCompleter(object): +class FastFilesCompleter: "Fast file completer class" def __init__(self, directories=True): diff --git a/src/_pytest/_code/__init__.py b/src/_pytest/_code/__init__.py index 1394b2b10e6..370e41dc9f3 100644 --- a/src/_pytest/_code/__init__.py +++ b/src/_pytest/_code/__init__.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- """ python inspection/code generation API """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - from .code import Code # noqa from .code import ExceptionInfo # noqa from .code import filter_traceback # noqa diff --git a/src/_pytest/_code/_py2traceback.py b/src/_pytest/_code/_py2traceback.py deleted file mode 100644 index faacc02166e..00000000000 --- a/src/_pytest/_code/_py2traceback.py +++ /dev/null @@ -1,95 +0,0 @@ -# -*- coding: utf-8 -*- -# copied from python-2.7.3's traceback.py -# CHANGES: -# - some_str is replaced, trying to create unicode strings -# -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals - -import types - -from six import text_type - - -def format_exception_only(etype, value): - """Format the exception part of a traceback. - - The arguments are the exception type and value such as given by - sys.last_type and sys.last_value. The return value is a list of - strings, each ending in a newline. - - Normally, the list contains a single string; however, for - SyntaxError exceptions, it contains several lines that (when - printed) display detailed information about where the syntax - error occurred. - - The message indicating which exception occurred is always the last - string in the list. - - """ - - # An instance should not have a meaningful value parameter, but - # sometimes does, particularly for string exceptions, such as - # >>> raise string1, string2 # deprecated - # - # Clear these out first because issubtype(string1, SyntaxError) - # would throw another exception and mask the original problem. - if ( - isinstance(etype, BaseException) - or isinstance(etype, types.InstanceType) - or etype is None - or type(etype) is str - ): - return [_format_final_exc_line(etype, value)] - - stype = etype.__name__ - - if not issubclass(etype, SyntaxError): - return [_format_final_exc_line(stype, value)] - - # It was a syntax error; show exactly where the problem was found. - lines = [] - try: - msg, (filename, lineno, offset, badline) = value.args - except Exception: - pass - else: - filename = filename or "" - lines.append(' File "{}", line {}\n'.format(filename, lineno)) - if badline is not None: - if isinstance(badline, bytes): # python 2 only - badline = badline.decode("utf-8", "replace") - lines.append(" {}\n".format(badline.strip())) - if offset is not None: - caretspace = badline.rstrip("\n")[:offset].lstrip() - # non-space whitespace (likes tabs) must be kept for alignment - caretspace = ((c.isspace() and c or " ") for c in caretspace) - # only three spaces to account for offset1 == pos 0 - lines.append(" {}^\n".format("".join(caretspace))) - value = msg - - lines.append(_format_final_exc_line(stype, value)) - return lines - - -def _format_final_exc_line(etype, value): - """Return a list of a single line -- normal case for format_exception_only""" - valuestr = _some_str(value) - if value is None or not valuestr: - line = "{}\n".format(etype) - else: - line = "{}: {}\n".format(etype, valuestr) - return line - - -def _some_str(value): - try: - return text_type(value) - except Exception: - try: - return bytes(value).decode("UTF-8", "replace") - except Exception: - pass - return "".format(type(value).__name__) diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index 8c73ccc6adc..c4ed961ace4 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -1,36 +1,22 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import inspect import re import sys import traceback from inspect import CO_VARARGS from inspect import CO_VARKEYWORDS +from traceback import format_exception_only from weakref import ref import attr import pluggy import py -from six import text_type import _pytest from _pytest._io.saferepr import safeformat from _pytest._io.saferepr import saferepr -from _pytest.compat import _PY2 -from _pytest.compat import _PY3 -from _pytest.compat import PY35 -from _pytest.compat import safe_str - -if _PY3: - from traceback import format_exception_only -else: - from ._py2traceback import format_exception_only -class Code(object): +class Code: """ wrapper around Python code objects """ def __init__(self, rawcode): @@ -41,7 +27,7 @@ def __init__(self, rawcode): self.firstlineno = rawcode.co_firstlineno - 1 self.name = rawcode.co_name except AttributeError: - raise TypeError("not a code object: %r" % (rawcode,)) + raise TypeError("not a code object: {!r}".format(rawcode)) self.raw = rawcode def __eq__(self, other): @@ -100,7 +86,7 @@ def getargs(self, var=False): return raw.co_varnames[:argcount] -class Frame(object): +class Frame: """Wrapper around a Python frame holding f_locals and f_globals in which expressions can be evaluated.""" @@ -163,7 +149,7 @@ def getargs(self, var=False): return retval -class TracebackEntry(object): +class TracebackEntry: """ a single entry in a traceback """ _repr_style = None @@ -208,8 +194,7 @@ def getlocals(self): locals = property(getlocals, None, None, "locals of underlaying frame") def getfirstlinesource(self): - # on Jython this firstlineno can be -1 apparently - return max(self.frame.code.firstlineno, 0) + return self.frame.code.firstlineno def getsource(self, astcache=None): """ return failing source code. """ @@ -324,7 +309,7 @@ def cut(self, path=None, lineno=None, firstlineno=None, excludepath=None): return self def __getitem__(self, key): - val = super(Traceback, self).__getitem__(key) + val = super().__getitem__(key) if isinstance(key, type(slice(0))): val = self.__class__(val) return val @@ -386,14 +371,12 @@ def recursionindex(self): @attr.s(repr=False) -class ExceptionInfo(object): +class ExceptionInfo: """ wraps sys.exc_info() objects and offers help for navigating the traceback. """ - _assert_start_repr = ( - "AssertionError(u'assert " if _PY2 else "AssertionError('assert " - ) + _assert_start_repr = "AssertionError('assert " _excinfo = attr.ib() _striptext = attr.ib(default="") @@ -558,11 +541,6 @@ def __str__(self): loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly()) return str(loc) - def __unicode__(self): - entry = self.traceback[-1] - loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly()) - return text_type(loc) - def match(self, regexp): """ Check whether the regular expression 'regexp' is found in the string @@ -578,7 +556,7 @@ def match(self, regexp): @attr.s -class FormattedExcinfo(object): +class FormattedExcinfo: """ presenting information about failing Functions and Generators. """ # for traceback entries @@ -677,7 +655,7 @@ def repr_locals(self, locals): str_repr = safeformat(value) # if len(str_repr) < 70 or not isinstance(value, # (list, tuple, dict)): - lines.append("%-10s = %s" % (name, str_repr)) + lines.append("{:<10} = {}".format(name, str_repr)) # else: # self._line("%-10s =\\" % (name,)) # # XXX @@ -692,8 +670,7 @@ def repr_traceback_entry(self, entry, excinfo=None): source = _pytest._code.Source("???") line_index = 0 else: - # entry.getfirstlinesource() can be -1, should be 0 on jython - line_index = entry.lineno - max(entry.getfirstlinesource(), 0) + line_index = entry.lineno - entry.getfirstlinesource() lines = [] style = entry._repr_style @@ -733,7 +710,7 @@ def repr_traceback(self, excinfo): if self.tbfilter: traceback = traceback.filter() - if is_recursion_error(excinfo): + if excinfo.errisinstance(RecursionError): traceback, extraline = self._truncate_recursive_traceback(traceback) else: extraline = None @@ -769,7 +746,7 @@ def _truncate_recursive_traceback(self, traceback): " Displaying first and last {max_frames} stack frames out of {total}." ).format( exc_type=type(e).__name__, - exc_msg=safe_str(e), + exc_msg=str(e), max_frames=max_frames, total=len(traceback), ) @@ -784,64 +761,51 @@ def _truncate_recursive_traceback(self, traceback): return traceback, extraline def repr_excinfo(self, excinfo): - if _PY2: - reprtraceback = self.repr_traceback(excinfo) - reprcrash = excinfo._getreprcrash() - return ReprExceptionInfo(reprtraceback, reprcrash) - else: - repr_chain = [] - e = excinfo.value - descr = None - seen = set() - while e is not None and id(e) not in seen: - seen.add(id(e)) - if excinfo: - reprtraceback = self.repr_traceback(excinfo) - reprcrash = excinfo._getreprcrash() - else: - # fallback to native repr if the exception doesn't have a traceback: - # ExceptionInfo objects require a full traceback to work - reprtraceback = ReprTracebackNative( - traceback.format_exception(type(e), e, None) - ) - reprcrash = None - - repr_chain += [(reprtraceback, reprcrash, descr)] - if e.__cause__ is not None and self.chain: - e = e.__cause__ - excinfo = ( - ExceptionInfo((type(e), e, e.__traceback__)) - if e.__traceback__ - else None - ) - descr = "The above exception was the direct cause of the following exception:" - elif ( - e.__context__ is not None - and not e.__suppress_context__ - and self.chain - ): - e = e.__context__ - excinfo = ( - ExceptionInfo((type(e), e, e.__traceback__)) - if e.__traceback__ - else None - ) - descr = "During handling of the above exception, another exception occurred:" - else: - e = None - repr_chain.reverse() - return ExceptionChainRepr(repr_chain) + repr_chain = [] + e = excinfo.value + descr = None + seen = set() + while e is not None and id(e) not in seen: + seen.add(id(e)) + if excinfo: + reprtraceback = self.repr_traceback(excinfo) + reprcrash = excinfo._getreprcrash() + else: + # fallback to native repr if the exception doesn't have a traceback: + # ExceptionInfo objects require a full traceback to work + reprtraceback = ReprTracebackNative( + traceback.format_exception(type(e), e, None) + ) + reprcrash = None + + repr_chain += [(reprtraceback, reprcrash, descr)] + if e.__cause__ is not None and self.chain: + e = e.__cause__ + excinfo = ( + ExceptionInfo((type(e), e, e.__traceback__)) + if e.__traceback__ + else None + ) + descr = "The above exception was the direct cause of the following exception:" + elif ( + e.__context__ is not None and not e.__suppress_context__ and self.chain + ): + e = e.__context__ + excinfo = ( + ExceptionInfo((type(e), e, e.__traceback__)) + if e.__traceback__ + else None + ) + descr = "During handling of the above exception, another exception occurred:" + else: + e = None + repr_chain.reverse() + return ExceptionChainRepr(repr_chain) -class TerminalRepr(object): +class TerminalRepr: def __str__(self): - s = self.__unicode__() - if _PY2: - s = s.encode("utf-8") - return s - - def __unicode__(self): # FYI this is called from pytest-xdist's serialization of exception # information. io = py.io.TextIO() @@ -850,7 +814,7 @@ def __unicode__(self): return io.getvalue().strip() def __repr__(self): - return "<%s instance at %0x>" % (self.__class__, id(self)) + return "<{} instance at {:0x}>".format(self.__class__, id(self)) class ExceptionRepr(TerminalRepr): @@ -868,7 +832,7 @@ def toterminal(self, tw): class ExceptionChainRepr(ExceptionRepr): def __init__(self, chain): - super(ExceptionChainRepr, self).__init__() + super().__init__() self.chain = chain # reprcrash and reprtraceback of the outermost (the newest) exception # in the chain @@ -881,18 +845,18 @@ def toterminal(self, tw): if element[2] is not None: tw.line("") tw.line(element[2], yellow=True) - super(ExceptionChainRepr, self).toterminal(tw) + super().toterminal(tw) class ReprExceptionInfo(ExceptionRepr): def __init__(self, reprtraceback, reprcrash): - super(ReprExceptionInfo, self).__init__() + super().__init__() self.reprtraceback = reprtraceback self.reprcrash = reprcrash def toterminal(self, tw): self.reprtraceback.toterminal(tw) - super(ReprExceptionInfo, self).toterminal(tw) + super().toterminal(tw) class ReprTraceback(TerminalRepr): @@ -969,7 +933,9 @@ def toterminal(self, tw): self.reprfileloc.toterminal(tw) def __str__(self): - return "%s\n%s\n%s" % ("\n".join(self.lines), self.reprlocals, self.reprfileloc) + return "{}\n{}\n{}".format( + "\n".join(self.lines), self.reprlocals, self.reprfileloc + ) class ReprFileLocation(TerminalRepr): @@ -986,7 +952,7 @@ def toterminal(self, tw): if i != -1: msg = msg[:i] tw.write(self.path, bold=True, red=True) - tw.line(":%s: %s" % (self.lineno, msg)) + tw.line(":{}: {}".format(self.lineno, msg)) class ReprLocals(TerminalRepr): @@ -1006,7 +972,7 @@ def toterminal(self, tw): if self.args: linesofar = "" for name, value in self.args: - ns = "%s = %s" % (safe_str(name), safe_str(value)) + ns = "{} = {}".format(name, value) if len(ns) + len(linesofar) + 2 > tw.fullwidth: if linesofar: tw.line(linesofar) @@ -1038,23 +1004,6 @@ def getrawcode(obj, trycall=True): return obj -if PY35: # RecursionError introduced in 3.5 - - def is_recursion_error(excinfo): - return excinfo.errisinstance(RecursionError) # noqa - - -else: - - def is_recursion_error(excinfo): - if not excinfo.errisinstance(RuntimeError): - return False - try: - return "maximum recursion depth exceeded" in str(excinfo.value) - except UnicodeError: - return False - - # relative paths that we use to filter traceback entries from appearing to the user; # see filter_traceback # note: if we need to add more paths than what we have now we should probably use a list diff --git a/src/_pytest/_code/source.py b/src/_pytest/_code/source.py index c8a4b6adf38..70d5f8fcdca 100644 --- a/src/_pytest/_code/source.py +++ b/src/_pytest/_code/source.py @@ -1,8 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import ast import inspect import linecache @@ -14,10 +9,9 @@ from bisect import bisect_right import py -import six -class Source(object): +class Source: """ an immutable object holding a source code fragment, possibly deindenting it. """ @@ -34,7 +28,7 @@ def __init__(self, *parts, **kwargs): partlines = part.lines elif isinstance(part, (tuple, list)): partlines = [x.rstrip("\n") for x in part] - elif isinstance(part, six.string_types): + elif isinstance(part, str): partlines = part.split("\n") else: partlines = getsource(part, deindent=de).lines diff --git a/src/_pytest/_io/saferepr.py b/src/_pytest/_io/saferepr.py index 9b412dccad9..74f75124f38 100644 --- a/src/_pytest/_io/saferepr.py +++ b/src/_pytest/_io/saferepr.py @@ -1,7 +1,5 @@ -# -*- coding: utf-8 -*- import pprint - -from six.moves import reprlib +import reprlib def _call_and_format_exception(call, x, *args): @@ -14,11 +12,8 @@ def _call_and_format_exception(call, x, *args): exc_info = str(exc) except Exception: exc_info = "unknown" - return '<[%s("%s") raised in repr()] %s object at 0x%x>' % ( - exc_name, - exc_info, - x.__class__.__name__, - id(x), + return '<[{}("{}") raised in repr()] {} object at 0x{:x}>'.format( + exc_name, exc_info, x.__class__.__name__, id(x) ) @@ -34,11 +29,11 @@ def repr_unicode(self, x, level): # Strictly speaking wrong on narrow builds def repr(u): if "'" not in u: - return u"'%s'" % u + return "'%s'" % u elif '"' not in u: - return u'"%s"' % u + return '"%s"' % u else: - return u"'%s'" % u.replace("'", r"\'") + return "'%s'" % u.replace("'", r"\'") s = repr(x[: self.maxstring]) if len(s) > self.maxstring: diff --git a/src/_pytest/assertion/__init__.py b/src/_pytest/assertion/__init__.py index 6b6abb863a3..e52101c9f16 100644 --- a/src/_pytest/assertion/__init__.py +++ b/src/_pytest/assertion/__init__.py @@ -1,15 +1,8 @@ -# -*- coding: utf-8 -*- """ support for presenting detailed information in failing assertions. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import sys -import six - from _pytest.assertion import rewrite from _pytest.assertion import truncate from _pytest.assertion import util @@ -56,14 +49,14 @@ def register_assert_rewrite(*names): importhook.mark_rewrite(*names) -class DummyRewriteHook(object): +class DummyRewriteHook: """A no-op import hook for when rewriting is disabled.""" def mark_rewrite(self, *names): pass -class AssertionState(object): +class AssertionState: """State for the assertion plugin.""" def __init__(self, config, mode): @@ -74,10 +67,6 @@ def __init__(self, config, mode): def install_importhook(config): """Try to install the rewrite hook, raise SystemError if it fails.""" - # Jython has an AST bug that make the assertion rewriting hook malfunction. - if sys.platform.startswith("java"): - raise SystemError("rewrite not supported") - config._assertstate = AssertionState(config, "rewrite") config._assertstate.hook = hook = rewrite.AssertionRewritingHook(config) sys.meta_path.insert(0, hook) @@ -133,7 +122,7 @@ def callbinrepr(op, left, right): if new_expl: new_expl = truncate.truncate_if_required(new_expl, item) new_expl = [line.replace("\n", "\\n") for line in new_expl] - res = six.text_type("\n~").join(new_expl) + res = "\n~".join(new_expl) if item.config.getvalue("assertmode") == "rewrite": res = res.replace("%", "%%") return res diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index 5e2c5397bb8..9b431b9849b 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- """Rewrite assertion AST to produce nice error messages""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import ast import errno import imp @@ -15,17 +10,16 @@ import struct import sys import types +from importlib.util import spec_from_file_location import atomicwrites import py -import six from _pytest._io.saferepr import saferepr from _pytest.assertion import util from _pytest.assertion.util import ( # noqa: F401 format_explanation as _format_explanation, ) -from _pytest.compat import spec_from_file_location from _pytest.pathlib import fnmatch_ex from _pytest.pathlib import PurePath @@ -35,28 +29,17 @@ else: if hasattr(sys, "pypy_version_info"): impl = "pypy" - elif sys.platform == "java": - impl = "jython" else: impl = "cpython" ver = sys.version_info - PYTEST_TAG = "%s-%s%s-PYTEST" % (impl, ver[0], ver[1]) + PYTEST_TAG = "{}-{}{}-PYTEST".format(impl, ver[0], ver[1]) del ver, impl PYC_EXT = ".py" + (__debug__ and "c" or "o") PYC_TAIL = "." + PYTEST_TAG + PYC_EXT -ASCII_IS_DEFAULT_ENCODING = sys.version_info[0] < 3 - -if sys.version_info >= (3, 5): - ast_Call = ast.Call -else: - - def ast_Call(a, b, c): - return ast.Call(a, b, c, None, None) - -class AssertionRewritingHook(object): +class AssertionRewritingHook: """PEP302 Import hook which rewrites asserts.""" def __init__(self, config): @@ -165,7 +148,7 @@ def find_module(self, name, path=None): # to check for a cached pyc. This may not be optimal... co = _read_pyc(fn_pypath, pyc, state.trace) if co is None: - state.trace("rewriting %r" % (fn,)) + state.trace("rewriting {!r}".format(fn)) source_stat, co = _rewrite_test(self.config, fn_pypath) if co is None: # Probably a SyntaxError in the test. @@ -177,7 +160,7 @@ def find_module(self, name, path=None): finally: self._writing_pyc = False else: - state.trace("found cached rewritten pyc for %r" % (fn,)) + state.trace("found cached rewritten pyc for {!r}".format(fn)) self.modules[name] = co, pyc return self @@ -216,26 +199,28 @@ def _early_rewrite_bailout(self, name, state): if self._is_marked_for_rewrite(name, state): return False - state.trace("early skip of rewriting module: %s" % (name,)) + state.trace("early skip of rewriting module: {}".format(name)) return True def _should_rewrite(self, name, fn_pypath, state): # always rewrite conftest files fn = str(fn_pypath) if fn_pypath.basename == "conftest.py": - state.trace("rewriting conftest file: %r" % (fn,)) + state.trace("rewriting conftest file: {!r}".format(fn)) return True if self.session is not None: if self.session.isinitpath(fn): - state.trace("matched test file (was specified on cmdline): %r" % (fn,)) + state.trace( + "matched test file (was specified on cmdline): {!r}".format(fn) + ) return True # modules not passed explicitly on the command line are only # rewritten if they match the naming convention for test files for pat in self.fnpats: if fn_pypath.fnmatch(pat): - state.trace("matched test file %r" % (fn,)) + state.trace("matched test file {!r}".format(fn)) return True return self._is_marked_for_rewrite(name, state) @@ -246,7 +231,9 @@ def _is_marked_for_rewrite(self, name, state): except KeyError: for marked in self._must_rewrite: if name == marked or name.startswith(marked + "."): - state.trace("matched marked file %r (from %r)" % (name, marked)) + state.trace( + "matched marked file {!r} (from {!r})".format(name, marked) + ) self._marked_for_rewrite_cache[name] = True return True @@ -341,7 +328,7 @@ def _write_pyc(state, co, source_stat, pyc): fp.write(struct.pack("= 3: - raise_ = ast.Raise(exc, None) - else: - raise_ = ast.Raise(exc, None, None) + exc = ast.Call(err_name, [fmt], []) + raise_ = ast.Raise(exc, None) + body.append(raise_) # Clear temporary variables by setting them to None. if self.variables: @@ -893,7 +847,7 @@ def warn_about_none_ast(self, node, module_path, lineno): def visit_Name(self, name): # Display the repr of the name if it's a local variable or # _should_repr_global_name() thinks it's acceptable. - locs = ast_Call(self.builtin("locals"), [], []) + locs = ast.Call(self.builtin("locals"), [], []) inlocs = ast.Compare(ast.Str(name.id), [ast.In()], [locs]) dorepr = self.helper("_should_repr_global_name", name) test = ast.BoolOp(ast.Or(), [inlocs, dorepr]) @@ -920,7 +874,7 @@ def visit_BoolOp(self, boolop): res, expl = self.visit(v) body.append(ast.Assign([ast.Name(res_var, ast.Store())], res)) expl_format = self.pop_format_context(ast.Str(expl)) - call = ast_Call(app, [expl_format], []) + call = ast.Call(app, [expl_format], []) self.on_failure.append(ast.Expr(call)) if i < levels: cond = res @@ -945,15 +899,25 @@ def visit_BinOp(self, binop): symbol = binop_map[binop.op.__class__] left_expr, left_expl = self.visit(binop.left) right_expr, right_expl = self.visit(binop.right) - explanation = "(%s %s %s)" % (left_expl, symbol, right_expl) + explanation = "({} {} {})".format(left_expl, symbol, right_expl) res = self.assign(ast.BinOp(left_expr, binop.op, right_expr)) return res, explanation - def visit_Call_35(self, call): + @staticmethod + def _is_any_call_with_generator_or_list_comprehension(call): + """Return True if the Call node is an 'any' call with a generator or list comprehension""" + return ( + isinstance(call.func, ast.Name) + and call.func.id == "all" + and len(call.args) == 1 + and isinstance(call.args[0], (ast.GeneratorExp, ast.ListComp)) + ) + + def visit_Call(self, call): """ - visit `ast.Call` nodes on Python3.5 and after + visit `ast.Call` nodes """ - if isinstance(call.func, ast.Name) and call.func.id == "all": + if self._is_any_call_with_generator_or_list_comprehension(call): return self._visit_all(call) new_func, func_expl = self.visit(call.func) arg_expls = [] @@ -971,17 +935,15 @@ def visit_Call_35(self, call): else: # **args have `arg` keywords with an .arg of None arg_expls.append("**" + expl) - expl = "%s(%s)" % (func_expl, ", ".join(arg_expls)) + expl = "{}({})".format(func_expl, ", ".join(arg_expls)) new_call = ast.Call(new_func, new_args, new_kwargs) res = self.assign(new_call) res_expl = self.explanation_param(self.display(res)) - outer_expl = "%s\n{%s = %s\n}" % (res_expl, res_expl, expl) + outer_expl = "{}\n{{{} = {}\n}}".format(res_expl, res_expl, expl) return res, outer_expl def _visit_all(self, call): """Special rewrite for the builtin all function, see #5062""" - if not isinstance(call.args[0], (ast.GeneratorExp, ast.ListComp)): - return gen_exp = call.args[0] assertion_module = ast.Module( body=[ast.Assert(test=gen_exp.elt, lineno=1, msg="", col_offset=1)] @@ -1005,46 +967,6 @@ def visit_Starred(self, starred): new_starred = ast.Starred(res, starred.ctx) return new_starred, "*" + expl - def visit_Call_legacy(self, call): - """ - visit `ast.Call nodes on 3.4 and below` - """ - if isinstance(call.func, ast.Name) and call.func.id == "all": - return self._visit_all(call) - new_func, func_expl = self.visit(call.func) - arg_expls = [] - new_args = [] - new_kwargs = [] - new_star = new_kwarg = None - for arg in call.args: - res, expl = self.visit(arg) - new_args.append(res) - arg_expls.append(expl) - for keyword in call.keywords: - res, expl = self.visit(keyword.value) - new_kwargs.append(ast.keyword(keyword.arg, res)) - arg_expls.append(keyword.arg + "=" + expl) - if call.starargs: - new_star, expl = self.visit(call.starargs) - arg_expls.append("*" + expl) - if call.kwargs: - new_kwarg, expl = self.visit(call.kwargs) - arg_expls.append("**" + expl) - expl = "%s(%s)" % (func_expl, ", ".join(arg_expls)) - new_call = ast.Call(new_func, new_args, new_kwargs, new_star, new_kwarg) - res = self.assign(new_call) - res_expl = self.explanation_param(self.display(res)) - outer_expl = "%s\n{%s = %s\n}" % (res_expl, res_expl, expl) - return res, outer_expl - - # ast.Call signature changed on 3.5, - # conditionally change which methods is named - # visit_Call depending on Python version - if sys.version_info >= (3, 5): - visit_Call = visit_Call_35 - else: - visit_Call = visit_Call_legacy - def visit_Attribute(self, attr): if not isinstance(attr.ctx, ast.Load): return self.generic_visit(attr) @@ -1074,7 +996,7 @@ def visit_Compare(self, comp): results.append(next_res) sym = binop_map[op.__class__] syms.append(ast.Str(sym)) - expl = "%s %s %s" % (left_expl, sym, next_expl) + expl = "{} {} {}".format(left_expl, sym, next_expl) expls.append(ast.Str(expl)) res_expr = ast.Compare(left_res, [op], [next_res]) self.statements.append(ast.Assign([store_names[i]], res_expr)) diff --git a/src/_pytest/assertion/truncate.py b/src/_pytest/assertion/truncate.py index 525896ea9ad..d97b05b441e 100644 --- a/src/_pytest/assertion/truncate.py +++ b/src/_pytest/assertion/truncate.py @@ -1,18 +1,11 @@ -# -*- coding: utf-8 -*- """ Utilities for truncating assertion output. Current default behaviour is to truncate assertion explanations at ~8 terminal lines, unless running in "-vv" mode or running on CI. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import os -import six - DEFAULT_MAX_LINES = 8 DEFAULT_MAX_CHARS = 8 * 80 USAGE_MSG = "use '-vv' to show" @@ -76,7 +69,7 @@ def _truncate_explanation(input_lines, max_lines=None, max_chars=None): else: msg += " ({} lines hidden)".format(truncated_line_count) msg += ", {}".format(USAGE_MSG) - truncated_explanation.extend([six.text_type(""), six.text_type(msg)]) + truncated_explanation.extend(["", str(msg)]) return truncated_explanation diff --git a/src/_pytest/assertion/util.py b/src/_pytest/assertion/util.py index 1fee64ce08d..2be759bac21 100644 --- a/src/_pytest/assertion/util.py +++ b/src/_pytest/assertion/util.py @@ -1,15 +1,8 @@ -# -*- coding: utf-8 -*- """Utilities for assertion debugging""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import pprint - -import six +from collections.abc import Sequence import _pytest._code -from ..compat import Sequence from _pytest import outcomes from _pytest._io.saferepr import saferepr @@ -42,7 +35,7 @@ def format_explanation(explanation): explanation = ecu(explanation) lines = _split_explanation(explanation) result = _format_lines(lines) - return u"\n".join(result) + return "\n".join(result) def _split_explanation(explanation): @@ -52,7 +45,7 @@ def _split_explanation(explanation): Any other newlines will be escaped and appear in the line as the literal '\n' characters. """ - raw_lines = (explanation or u"").split("\n") + raw_lines = (explanation or "").split("\n") lines = [raw_lines[0]] for values in raw_lines[1:]: if values and values[0] in ["{", "}", "~", ">"]: @@ -77,13 +70,13 @@ def _format_lines(lines): for line in lines[1:]: if line.startswith("{"): if stackcnt[-1]: - s = u"and " + s = "and " else: - s = u"where " + s = "where " stack.append(len(result)) stackcnt[-1] += 1 stackcnt.append(0) - result.append(u" +" + u" " * (len(stack) - 1) + s + line[1:]) + result.append(" +" + " " * (len(stack) - 1) + s + line[1:]) elif line.startswith("}"): stack.pop() stackcnt.pop() @@ -92,7 +85,7 @@ def _format_lines(lines): assert line[0] in ["~", ">"] stack[-1] += 1 indent = len(stack) if line.startswith("~") else len(stack) - 1 - result.append(u" " * indent + line[1:]) + result.append(" " * indent + line[1:]) assert len(stack) == 1 return result @@ -142,7 +135,7 @@ def assertrepr_compare(config, op, left, right): left_repr = saferepr(left, maxsize=int(width // 2)) right_repr = saferepr(right, maxsize=width - len(left_repr)) - summary = u"%s %s %s" % (ecu(left_repr), op, ecu(right_repr)) + summary = "{} {} {}".format(ecu(left_repr), op, ecu(right_repr)) verbose = config.getoption("verbose") explanation = None @@ -175,9 +168,9 @@ def assertrepr_compare(config, op, left, right): raise except Exception: explanation = [ - u"(pytest_assertion plugin: representation of details failed. " - u"Probably an object has a faulty __repr__.)", - six.text_type(_pytest._code.ExceptionInfo.from_current()), + "(pytest_assertion plugin: representation of details failed. " + "Probably an object has a faulty __repr__.)", + str(_pytest._code.ExceptionInfo.from_current()), ] if not explanation: @@ -204,7 +197,7 @@ def escape_for_readable_diff(binary_text): This is done using repr() which then needs post-processing to fix the encompassing quotes and un-escape newlines and carriage returns (#429). """ - r = six.text_type(repr(binary_text)[1:-1]) + r = str(repr(binary_text)[1:-1]) r = r.replace(r"\n", "\n") r = r.replace(r"\r", "\r") return r @@ -221,7 +214,7 @@ def escape_for_readable_diff(binary_text): if i > 42: i -= 10 # Provide some context explanation = [ - u"Skipping %s identical leading characters in diff, use -v to show" % i + "Skipping %s identical leading characters in diff, use -v to show" % i ] left = left[i:] right = right[i:] @@ -232,8 +225,8 @@ def escape_for_readable_diff(binary_text): if i > 42: i -= 10 # Provide some context explanation += [ - u"Skipping {} identical trailing " - u"characters in diff, use -v to show".format(i) + "Skipping {} identical trailing " + "characters in diff, use -v to show".format(i) ] left = left[:-i] right = right[:-i] @@ -241,7 +234,7 @@ def escape_for_readable_diff(binary_text): if left.isspace() or right.isspace(): left = repr(str(left)) right = repr(str(right)) - explanation += [u"Strings contain only whitespace, escaping them using repr()"] + explanation += ["Strings contain only whitespace, escaping them using repr()"] explanation += [ line.strip("\n") for line in ndiff(left.splitlines(keepends), right.splitlines(keepends)) @@ -255,29 +248,29 @@ def _compare_eq_verbose(left, right): right_lines = repr(right).splitlines(keepends) explanation = [] - explanation += [u"-" + line for line in left_lines] - explanation += [u"+" + line for line in right_lines] + explanation += ["-" + line for line in left_lines] + explanation += ["+" + line for line in right_lines] return explanation def _compare_eq_iterable(left, right, verbose=0): if not verbose: - return [u"Use -v to get the full diff"] + return ["Use -v to get the full diff"] # dynamic import to speedup pytest import difflib try: left_formatting = pprint.pformat(left).splitlines() right_formatting = pprint.pformat(right).splitlines() - explanation = [u"Full diff:"] + explanation = ["Full diff:"] except Exception: # hack: PrettyPrinter.pformat() in python 2 fails when formatting items that can't be sorted(), ie, calling # sorted() on a list would raise. See issue #718. # As a workaround, the full diff is generated by using the repr() string of each item of each container. left_formatting = sorted(repr(x) for x in left) right_formatting = sorted(repr(x) for x in right) - explanation = [u"Full diff (fallback to calling repr on each item):"] + explanation = ["Full diff (fallback to calling repr on each item):"] explanation.extend( line.strip() for line in difflib.ndiff(left_formatting, right_formatting) ) @@ -290,7 +283,9 @@ def _compare_eq_sequence(left, right, verbose=0): len_right = len(right) for i in range(min(len_left, len_right)): if left[i] != right[i]: - explanation += [u"At index %s diff: %r != %r" % (i, left[i], right[i])] + explanation += [ + "At index {} diff: {!r} != {!r}".format(i, left[i], right[i]) + ] break len_diff = len_left - len_right @@ -304,10 +299,12 @@ def _compare_eq_sequence(left, right, verbose=0): extra = saferepr(right[len_left]) if len_diff == 1: - explanation += [u"%s contains one more item: %s" % (dir_with_more, extra)] + explanation += [ + "{} contains one more item: {}".format(dir_with_more, extra) + ] else: explanation += [ - u"%s contains %d more items, first extra item: %s" + "%s contains %d more items, first extra item: %s" % (dir_with_more, len_diff, extra) ] return explanation @@ -318,11 +315,11 @@ def _compare_eq_set(left, right, verbose=0): diff_left = left - right diff_right = right - left if diff_left: - explanation.append(u"Extra items in the left set:") + explanation.append("Extra items in the left set:") for item in diff_left: explanation.append(saferepr(item)) if diff_right: - explanation.append(u"Extra items in the right set:") + explanation.append("Extra items in the right set:") for item in diff_right: explanation.append(saferepr(item)) return explanation @@ -335,20 +332,20 @@ def _compare_eq_dict(left, right, verbose=0): common = set_left.intersection(set_right) same = {k: left[k] for k in common if left[k] == right[k]} if same and verbose < 2: - explanation += [u"Omitting %s identical items, use -vv to show" % len(same)] + explanation += ["Omitting %s identical items, use -vv to show" % len(same)] elif same: - explanation += [u"Common items:"] + explanation += ["Common items:"] explanation += pprint.pformat(same).splitlines() diff = {k for k in common if left[k] != right[k]} if diff: - explanation += [u"Differing items:"] + explanation += ["Differing items:"] for k in diff: explanation += [saferepr({k: left[k]}) + " != " + saferepr({k: right[k]})] extra_left = set_left - set_right len_extra_left = len(extra_left) if len_extra_left: explanation.append( - u"Left contains %d more item%s:" + "Left contains %d more item%s:" % (len_extra_left, "" if len_extra_left == 1 else "s") ) explanation.extend( @@ -358,7 +355,7 @@ def _compare_eq_dict(left, right, verbose=0): len_extra_right = len(extra_right) if len_extra_right: explanation.append( - u"Right contains %d more item%s:" + "Right contains %d more item%s:" % (len_extra_right, "" if len_extra_right == 1 else "s") ) explanation.extend( @@ -386,15 +383,15 @@ def _compare_eq_cls(left, right, verbose, type_fns): explanation = [] if same and verbose < 2: - explanation.append(u"Omitting %s identical items, use -vv to show" % len(same)) + explanation.append("Omitting %s identical items, use -vv to show" % len(same)) elif same: - explanation += [u"Matching attributes:"] + explanation += ["Matching attributes:"] explanation += pprint.pformat(same).splitlines() if diff: - explanation += [u"Differing attributes:"] + explanation += ["Differing attributes:"] for field in diff: explanation += [ - (u"%s: %r != %r") % (field, getattr(left, field), getattr(right, field)) + ("%s: %r != %r") % (field, getattr(left, field), getattr(right, field)) ] return explanation @@ -405,14 +402,14 @@ def _notin_text(term, text, verbose=0): tail = text[index + len(term) :] correct_text = head + tail diff = _diff_text(correct_text, text, verbose) - newdiff = [u"%s is contained here:" % saferepr(term, maxsize=42)] + newdiff = ["%s is contained here:" % saferepr(term, maxsize=42)] for line in diff: - if line.startswith(u"Skipping"): + if line.startswith("Skipping"): continue - if line.startswith(u"- "): + if line.startswith("- "): continue - if line.startswith(u"+ "): - newdiff.append(u" " + line[2:]) + if line.startswith("+ "): + newdiff.append(" " + line[2:]) else: newdiff.append(line) return newdiff diff --git a/src/_pytest/cacheprovider.py b/src/_pytest/cacheprovider.py index 045248cb7c2..17463959fb4 100755 --- a/src/_pytest/cacheprovider.py +++ b/src/_pytest/cacheprovider.py @@ -1,29 +1,22 @@ -# -*- coding: utf-8 -*- """ merged implementation of the cache provider the name cache was not chosen to ensure pluggy automatically ignores the external pytest-cache """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import json import os from collections import OrderedDict import attr import py -import six import pytest -from .compat import _PY2 as PY2 from .pathlib import Path from .pathlib import resolve_from_str from .pathlib import rmtree -README_CONTENT = u"""\ +README_CONTENT = """\ # pytest cache directory # This directory contains data from the pytest's cache plugin, @@ -43,7 +36,7 @@ @attr.s -class Cache(object): +class Cache: _cachedir = attr.ib(repr=False) _config = attr.ib(repr=False) @@ -129,7 +122,7 @@ def set(self, key, value): if not cache_dir_exists_already: self._ensure_supporting_files() try: - f = path.open("wb" if PY2 else "w") + f = path.open("w") except (IOError, OSError): self.warn("cache could not write path {path}", path=path) else: @@ -142,14 +135,14 @@ def _ensure_supporting_files(self): readme_path.write_text(README_CONTENT) gitignore_path = self._cachedir.joinpath(".gitignore") - msg = u"# Created by pytest automatically.\n*" + msg = "# Created by pytest automatically.\n*" gitignore_path.write_text(msg, encoding="UTF-8") cachedir_tag_path = self._cachedir.joinpath("CACHEDIR.TAG") cachedir_tag_path.write_bytes(CACHEDIR_TAG_CONTENT) -class LFPlugin(object): +class LFPlugin: """ Plugin which implements the --lf (run last-failing) option """ def __init__(self, config): @@ -262,7 +255,7 @@ def pytest_sessionfinish(self, session): config.cache.set("cache/lastfailed", self.lastfailed) -class NFPlugin(object): +class NFPlugin: """ Plugin which implements the --nf (run new-first) option """ def __init__(self, config): @@ -281,8 +274,8 @@ def pytest_collection_modifyitems(self, session, config, items): other_items[item.nodeid] = item items[:] = self._get_increasing_order( - six.itervalues(new_items) - ) + self._get_increasing_order(six.itervalues(other_items)) + new_items.values() + ) + self._get_increasing_order(other_items.values()) self.cached_nodeids = [x.nodeid for x in items if isinstance(x, pytest.Item)] def _get_increasing_order(self, items): diff --git a/src/_pytest/capture.py b/src/_pytest/capture.py index 68c17772f39..302979ef4be 100644 --- a/src/_pytest/capture.py +++ b/src/_pytest/capture.py @@ -1,12 +1,7 @@ -# -*- coding: utf-8 -*- """ per-test stdout/stderr capturing mechanism. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import collections import contextlib import io @@ -15,10 +10,7 @@ from io import UnsupportedOperation 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"} @@ -67,7 +59,7 @@ def pytest_load_initial_conftests(early_config, parser, args): sys.stderr.write(err) -class CaptureManager(object): +class CaptureManager: """ Capture plugin, manages that the appropriate capture method is enabled/disabled during collection and each test phase (setup, call, teardown). After each of those points, the captured output is obtained and @@ -86,10 +78,8 @@ def __init__(self, method): self._current_item = None def __repr__(self): - return "" % ( - self._method, - self._global_capturing, - self._current_item, + return "".format( + self._method, self._global_capturing, self._current_item ) def _getcapture(self, method): @@ -283,10 +273,6 @@ def capsysbinary(request): ``out`` and ``err`` will be ``bytes`` objects. """ _ensure_only_one_capture_fixture(request, "capsysbinary") - # Currently, the implementation uses the python3 specific `.buffer` - # property of CaptureIO. - if sys.version_info < (3,): - raise request.raiseerror("capsysbinary is only supported on Python 3") with _install_capture_fixture_on_item(request, SysCaptureBinary) as fixture: yield fixture @@ -345,7 +331,7 @@ def _install_capture_fixture_on_item(request, capture_class): del request.node._capture_fixture -class CaptureFixture(object): +class CaptureFixture: """ Object returned by :py:func:`capsys`, :py:func:`capsysbinary`, :py:func:`capfd` and :py:func:`capfdbinary` fixtures. @@ -424,7 +410,7 @@ def safe_text_dupfile(f, mode, default_encoding="UTF8"): return EncodedFile(f, encoding or default_encoding) -class EncodedFile(object): +class EncodedFile: errors = "strict" # possibly needed by py3 code (issue555) def __init__(self, buffer, encoding): @@ -432,9 +418,9 @@ def __init__(self, buffer, encoding): self.encoding = encoding def write(self, obj): - if isinstance(obj, six.text_type): + if isinstance(obj, str): obj = obj.encode(self.encoding, "replace") - elif _PY3: + else: raise TypeError( "write() argument must be str, not {}".format(type(obj).__name__) ) @@ -460,7 +446,7 @@ def __getattr__(self, name): CaptureResult = collections.namedtuple("CaptureResult", ["out", "err"]) -class MultiCapture(object): +class MultiCapture: out = err = in_ = None _state = None @@ -473,7 +459,7 @@ def __init__(self, out=True, err=True, in_=True, Capture=None): self.err = Capture(2) def __repr__(self): - return "" % ( + return "".format( self.out, self.err, self.in_, @@ -539,12 +525,12 @@ def readouterr(self): ) -class NoCapture(object): +class NoCapture: EMPTY_BUFFER = None __init__ = start = done = suspend = resume = lambda *args: None -class FDCaptureBinary(object): +class FDCaptureBinary: """Capture IO to/from a given os-level filedescriptor. snap() produces `bytes` @@ -578,10 +564,8 @@ def __init__(self, targetfd, tmpfile=None): self.tmpfile_fd = tmpfile.fileno() def __repr__(self): - return "" % ( - self.targetfd, - getattr(self, "targetfd_save", None), - self._state, + return "".format( + self.targetfd, getattr(self, "targetfd_save", None), self._state ) def start(self): @@ -608,7 +592,7 @@ def done(self): os.dup2(targetfd_save, self.targetfd) os.close(targetfd_save) self.syscapture.done() - _attempt_to_close_capture_file(self.tmpfile) + self.tmpfile.close() self._state = "done" def suspend(self): @@ -623,7 +607,7 @@ def resume(self): def writeorg(self, data): """ write to original file descriptor. """ - if isinstance(data, six.text_type): + if isinstance(data, str): data = data.encode("utf8") # XXX use encoding of original stream os.write(self.targetfd_save, data) @@ -637,14 +621,14 @@ class FDCapture(FDCaptureBinary): EMPTY_BUFFER = str() def snap(self): - res = super(FDCapture, self).snap() + res = super().snap() enc = getattr(self.tmpfile, "encoding", None) if enc and isinstance(res, bytes): - res = six.text_type(res, enc, "replace") + res = str(res, enc, "replace") return res -class SysCapture(object): +class SysCapture: EMPTY_BUFFER = str() _state = None @@ -661,11 +645,8 @@ def __init__(self, fd, tmpfile=None): self.tmpfile = tmpfile def __repr__(self): - return "" % ( - self.name, - self._old, - self.tmpfile, - self._state, + return "".format( + self.name, self._old, self.tmpfile, self._state ) def start(self): @@ -681,7 +662,7 @@ def snap(self): def done(self): setattr(sys, self.name, self._old) del self._old - _attempt_to_close_capture_file(self.tmpfile) + self.tmpfile.close() self._state = "done" def suspend(self): @@ -707,7 +688,7 @@ def snap(self): return res -class DontReadFromInput(six.Iterator): +class DontReadFromInput: """Temporary stub class. Ideally when stdin is accessed, the capturing should be turned off, with possibly all data captured so far sent to the screen. This should be configurable, though, @@ -738,10 +719,7 @@ def close(self): @property def buffer(self): - if sys.version_info >= (3, 0): - return self - else: - raise AttributeError("redirected stdin has no attribute buffer") + return self def _colorama_workaround(): @@ -837,14 +815,3 @@ def _reopen_stdio(f, mode): sys.stdin = _reopen_stdio(sys.stdin, "rb") sys.stdout = _reopen_stdio(sys.stdout, "wb") sys.stderr = _reopen_stdio(sys.stderr, "wb") - - -def _attempt_to_close_capture_file(f): - """Suppress IOError when closing the temporary file used for capturing streams in py27 (#2370)""" - if six.PY2: - try: - f.close() - except IOError: - pass - else: - f.close() diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index 7668c3a94c7..a4f3bc00319 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -1,64 +1,28 @@ -# -*- coding: utf-8 -*- """ python version compatibility code """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import codecs import functools import inspect +import io import re import sys from contextlib import contextmanager +from inspect import Parameter +from inspect import signature import py -import six -from six import text_type import _pytest from _pytest._io.saferepr import saferepr from _pytest.outcomes import fail from _pytest.outcomes import TEST_OUTCOME -try: - import enum -except ImportError: # pragma: no cover - # Only available in Python 3.4+ or as a backport - enum = None - -_PY3 = sys.version_info > (3, 0) -_PY2 = not _PY3 - - -if _PY3: - from inspect import signature, Parameter as Parameter -else: - from funcsigs import signature, Parameter as Parameter NOTSET = object() -PY35 = sys.version_info[:2] >= (3, 5) -PY36 = sys.version_info[:2] >= (3, 6) -MODULE_NOT_FOUND_ERROR = "ModuleNotFoundError" if PY36 else "ImportError" - - -if _PY3: - from collections.abc import MutableMapping as MappingMixin - from collections.abc import Iterable, Mapping, Sequence, Sized -else: - # those raise DeprecationWarnings in Python >=3.7 - from collections import MutableMapping as MappingMixin # noqa - from collections import Iterable, Mapping, Sequence, Sized # noqa - - -if sys.version_info >= (3, 4): - from importlib.util import spec_from_file_location -else: - - def spec_from_file_location(*_, **__): - return None +MODULE_NOT_FOUND_ERROR = ( + "ModuleNotFoundError" if sys.version_info[:2] >= (3, 6) else "ImportError" +) def _format_args(func): @@ -184,10 +148,10 @@ def get_default_arg_names(function): _non_printable_ascii_translate_table = { - i: u"\\x{:02x}".format(i) for i in range(128) if i not in range(32, 127) + i: "\\x{:02x}".format(i) for i in range(128) if i not in range(32, 127) } _non_printable_ascii_translate_table.update( - {ord("\t"): u"\\t", ord("\r"): u"\\r", ord("\n"): u"\\n"} + {ord("\t"): "\\t", ord("\r"): "\\r", ord("\n"): "\\n"} ) @@ -195,75 +159,39 @@ def _translate_non_printable(s): return s.translate(_non_printable_ascii_translate_table) -if _PY3: - STRING_TYPES = bytes, str - UNICODE_TYPES = six.text_type +STRING_TYPES = bytes, str - if PY35: - def _bytes_to_ascii(val): - return val.decode("ascii", "backslashreplace") +def _bytes_to_ascii(val): + return val.decode("ascii", "backslashreplace") - else: - - def _bytes_to_ascii(val): - if val: - # source: http://goo.gl/bGsnwC - encoded_bytes, _ = codecs.escape_encode(val) - return encoded_bytes.decode("ascii") - else: - # empty bytes crashes codecs.escape_encode (#1087) - return "" - - def ascii_escaped(val): - """If val is pure ascii, returns it as a str(). Otherwise, escapes - bytes objects into a sequence of escaped bytes: - - b'\xc3\xb4\xc5\xd6' -> u'\\xc3\\xb4\\xc5\\xd6' - - and escapes unicode objects into a sequence of escaped unicode - ids, e.g.: - '4\\nV\\U00043efa\\x0eMXWB\\x1e\\u3028\\u15fd\\xcd\\U0007d944' +def ascii_escaped(val): + """If val is pure ascii, returns it as a str(). Otherwise, escapes + bytes objects into a sequence of escaped bytes: - note: - the obvious "v.decode('unicode-escape')" will return - valid utf-8 unicode if it finds them in bytes, but we - want to return escaped bytes for any byte, even if they match - a utf-8 string. + b'\xc3\xb4\xc5\xd6' -> u'\\xc3\\xb4\\xc5\\xd6' - """ - if isinstance(val, bytes): - ret = _bytes_to_ascii(val) - else: - ret = val.encode("unicode_escape").decode("ascii") - return _translate_non_printable(ret) + and escapes unicode objects into a sequence of escaped unicode + ids, e.g.: + '4\\nV\\U00043efa\\x0eMXWB\\x1e\\u3028\\u15fd\\xcd\\U0007d944' -else: - STRING_TYPES = six.string_types - UNICODE_TYPES = six.text_type + note: + the obvious "v.decode('unicode-escape')" will return + valid utf-8 unicode if it finds them in bytes, but we + want to return escaped bytes for any byte, even if they match + a utf-8 string. - def ascii_escaped(val): - """In py2 bytes and str are the same type, so return if it's a bytes - object, return it unchanged if it is a full ascii string, - otherwise escape it into its binary form. - - If it's a unicode string, change the unicode characters into - unicode escapes. - - """ - if isinstance(val, bytes): - try: - ret = val.decode("ascii") - except UnicodeDecodeError: - ret = val.encode("string-escape").decode("ascii") - else: - ret = val.encode("unicode-escape").decode("ascii") - return _translate_non_printable(ret) + """ + if isinstance(val, bytes): + ret = _bytes_to_ascii(val) + else: + ret = val.encode("unicode_escape").decode("ascii") + return _translate_non_printable(ret) -class _PytestWrapper(object): +class _PytestWrapper: """Dummy wrapper around a function object for internal use only. Used to correctly unwrap the underlying function object @@ -357,36 +285,6 @@ def safe_isclass(obj): return False -def _is_unittest_unexpected_success_a_failure(): - """Return if the test suite should fail if an @expectedFailure unittest test PASSES. - - From https://docs.python.org/3/library/unittest.html?highlight=unittest#unittest.TestResult.wasSuccessful: - Changed in version 3.4: Returns False if there were any - unexpectedSuccesses from tests marked with the expectedFailure() decorator. - """ - return sys.version_info >= (3, 4) - - -if _PY3: - - def safe_str(v): - """returns v as string""" - return str(v) - - -else: - - def safe_str(v): - """returns v as string, converting to ascii if necessary""" - try: - return str(v) - except UnicodeError: - if not isinstance(v, text_type): - v = text_type(v) - errors = "replace" - return v.encode("utf-8", errors) - - COLLECT_FAKEMODULE_ATTRIBUTES = ( "Collector", "Module", @@ -410,30 +308,15 @@ def _setup_collect_fakemodule(): setattr(pytest.collect, attr, getattr(pytest, attr)) -if _PY2: - # Without this the test_dupfile_on_textio will fail, otherwise CaptureIO could directly inherit from StringIO. - from py.io import TextIO - - class CaptureIO(TextIO): - @property - def encoding(self): - return getattr(self, "_encoding", "UTF-8") +class CaptureIO(io.TextIOWrapper): + def __init__(self): + super().__init__(io.BytesIO(), encoding="UTF-8", newline="", write_through=True) + def getvalue(self): + return self.buffer.getvalue().decode("UTF-8") -else: - import io - class CaptureIO(io.TextIOWrapper): - def __init__(self): - super(CaptureIO, self).__init__( - io.BytesIO(), encoding="UTF-8", newline="", write_through=True - ) - - def getvalue(self): - return self.buffer.getvalue().decode("UTF-8") - - -class FuncargnamesCompatAttr(object): +class FuncargnamesCompatAttr: """ helper class so that Metafunc, Function and FixtureRequest don't need to each define the "funcargnames" compatibility attribute. """ @@ -442,16 +325,3 @@ class FuncargnamesCompatAttr(object): def funcargnames(self): """ alias attribute for ``fixturenames`` for pre-2.3 compatibility""" return self.fixturenames - - -if six.PY2: - - def lru_cache(*_, **__): - def dec(fn): - return fn - - return dec - - -else: - from functools import lru_cache # noqa: F401 diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 7a5deb13f53..40f37480b4c 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- """ command line options, ini-file and conftest.py processing. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import argparse import copy import inspect @@ -12,10 +7,10 @@ import sys import types import warnings +from functools import lru_cache import importlib_metadata import py -import six from packaging.version import Version from pluggy import HookimplMarker from pluggy import HookspecMarker @@ -31,8 +26,6 @@ from _pytest import deprecated from _pytest._code import ExceptionInfo from _pytest._code import filter_traceback -from _pytest.compat import lru_cache -from _pytest.compat import safe_str from _pytest.outcomes import fail from _pytest.outcomes import Skipped from _pytest.warning_types import PytestConfigWarning @@ -73,7 +66,7 @@ def main(args=None, plugins=None): if exc_info.traceback else exc_info.exconly() ) - formatted_tb = safe_str(exc_repr) + formatted_tb = str(exc_repr) for line in formatted_tb.splitlines(): tw.line(line.rstrip(), red=True) return 4 @@ -89,7 +82,7 @@ def main(args=None, plugins=None): return EXIT_USAGEERROR -class cmdline(object): # compatibility namespace +class cmdline: # compatibility namespace main = staticmethod(main) @@ -195,7 +188,7 @@ def _prepareconfig(args=None, plugins=None): try: if plugins: for plugin in plugins: - if isinstance(plugin, six.string_types): + if isinstance(plugin, str): pluginmanager.consider_pluginarg(plugin) else: pluginmanager.register(plugin) @@ -222,7 +215,7 @@ class PytestPluginManager(PluginManager): """ def __init__(self): - super(PytestPluginManager, self).__init__("pytest") + super().__init__("pytest") self._conftest_plugins = set() # state related to local conftest plugins @@ -270,7 +263,7 @@ def parse_hookimpl_opts(self, plugin, name): return method = getattr(plugin, name) - opts = super(PytestPluginManager, self).parse_hookimpl_opts(plugin, name) + opts = super().parse_hookimpl_opts(plugin, name) # consider only actual functions for hooks (#3775) if not inspect.isroutine(method): @@ -289,9 +282,7 @@ def parse_hookimpl_opts(self, plugin, name): return opts def parse_hookspec_opts(self, module_or_class, name): - opts = super(PytestPluginManager, self).parse_hookspec_opts( - module_or_class, name - ) + opts = super().parse_hookspec_opts(module_or_class, name) if opts is None: method = getattr(module_or_class, name) @@ -318,7 +309,7 @@ def register(self, plugin, name=None): ) ) return - ret = super(PytestPluginManager, self).register(plugin, name) + ret = super().register(plugin, name) if ret: self.hook.pytest_plugin_registered.call_historic( kwargs=dict(plugin=plugin, manager=self) @@ -403,12 +394,6 @@ def _getconftestmodules(self, path): else: directory = path - if six.PY2: # py2 is not using lru_cache. - try: - return self._dirpath2confmods[directory] - except KeyError: - pass - # XXX these days we may rather want to use config.rootdir # and allow users to opt into looking into the rootdir parent # directories instead of requiring to specify confcutdir @@ -485,7 +470,7 @@ def consider_preparse(self, args): while i < n: opt = args[i] i += 1 - if isinstance(opt, six.string_types): + if isinstance(opt, str): if opt == "-p": try: parg = args[i] @@ -546,7 +531,7 @@ def import_plugin(self, modname, consider_entry_points=False): # "terminal" or "capture". Those plugins are registered under their # basename for historic purposes but must be imported with the # _pytest prefix. - assert isinstance(modname, six.string_types), ( + assert isinstance(modname, str), ( "module name as text required, got %r" % modname ) modname = str(modname) @@ -564,20 +549,19 @@ def import_plugin(self, modname, consider_entry_points=False): try: __import__(importspec) except ImportError as e: - new_exc_message = 'Error importing plugin "%s": %s' % ( - modname, - safe_str(e.args[0]), + new_exc_message = 'Error importing plugin "{}": {}'.format( + modname, str(e.args[0]) ) new_exc = ImportError(new_exc_message) tb = sys.exc_info()[2] - six.reraise(ImportError, new_exc, tb) + raise new_exc.with_traceback(tb) except Skipped as e: from _pytest.warnings import _issue_warning_captured _issue_warning_captured( - PytestConfigWarning("skipped plugin %r: %s" % (modname, e.msg)), + PytestConfigWarning("skipped plugin {!r}: {}".format(modname, e.msg)), self.hook, stacklevel=1, ) @@ -595,7 +579,7 @@ def _get_plugin_specs_as_list(specs): empty list is returned. """ if specs is not None and not isinstance(specs, types.ModuleType): - if isinstance(specs, six.string_types): + if isinstance(specs, str): specs = specs.split(",") if specs else [] if not isinstance(specs, (list, tuple)): raise UsageError( @@ -613,7 +597,7 @@ def _ensure_removed_sysmodule(modname): pass -class Notset(object): +class Notset: def __repr__(self): return "" @@ -633,7 +617,7 @@ def _iter_rewritable_modules(package_files): yield package_name -class Config(object): +class Config: """ access to configuration values, pluginmanager and plugin hooks. """ def __init__(self, pluginmanager): @@ -644,7 +628,7 @@ def __init__(self, pluginmanager): _a = FILE_OR_DIR self._parser = Parser( - usage="%%(prog)s [options] [%s] [%s] [...]" % (_a, _a), + usage="%(prog)s [options] [{}] [{}] [...]".format(_a, _a), processopt=self._processopt, ) #: a pluginmanager instance @@ -932,7 +916,7 @@ def _getini(self, name): try: description, type, default = self._parser._inidict[name] except KeyError: - raise ValueError("unknown configuration value: %r" % (name,)) + raise ValueError("unknown configuration value: {!r}".format(name)) value = self._get_override_ini_value(name) if value is None: try: @@ -1009,8 +993,8 @@ def getoption(self, name, default=notset, skip=False): if skip: import pytest - pytest.skip("no %r option found" % (name,)) - raise ValueError("no option named %r" % (name,)) + pytest.skip("no {!r} option found".format(name)) + raise ValueError("no option named {!r}".format(name)) def getvalue(self, name, path=None): """ (deprecated, use getoption()) """ @@ -1098,4 +1082,4 @@ def _strtobool(val): elif val in ("n", "no", "f", "false", "off", "0"): return 0 else: - raise ValueError("invalid truth value %r" % (val,)) + raise ValueError("invalid truth value {!r}".format(val)) diff --git a/src/_pytest/config/argparsing.py b/src/_pytest/config/argparsing.py index 37fb772db99..fb36c798526 100644 --- a/src/_pytest/config/argparsing.py +++ b/src/_pytest/config/argparsing.py @@ -1,16 +1,14 @@ -# -*- coding: utf-8 -*- import argparse import warnings import py -import six from _pytest.config.exceptions import UsageError FILE_OR_DIR = "file_or_dir" -class Parser(object): +class Parser: """ Parser for command line arguments and ini-file values. :ivar extra_info: dict of generic param -> value to display in case @@ -145,12 +143,12 @@ def __init__(self, msg, option): def __str__(self): if self.option_id: - return "option %s: %s" % (self.option_id, self.msg) + return "option {}: {}".format(self.option_id, self.msg) else: return self.msg -class Argument(object): +class Argument: """class that mimics the necessary behaviour of optparse.Option it's currently a least effort implementation @@ -179,7 +177,7 @@ def __init__(self, *names, **attrs): pass else: # this might raise a keyerror as well, don't want to catch that - if isinstance(typ, six.string_types): + if isinstance(typ, str): if typ == "choice": warnings.warn( "`type` argument to addoption() is the string %r." @@ -282,7 +280,7 @@ def __repr__(self): return "Argument({})".format(", ".join(args)) -class OptionGroup(object): +class OptionGroup: def __init__(self, name, description="", parser=None): self.name = name self.description = description @@ -337,10 +335,10 @@ def __init__(self, parser, extra_info=None, prog=None): def error(self, message): """Transform argparse error message into UsageError.""" - msg = "%s: error: %s" % (self.prog, message) + msg = "{}: error: {}".format(self.prog, message) if hasattr(self._parser, "_config_source_hint"): - msg = "%s (%s)" % (msg, self._parser._config_source_hint) + msg = "{} ({})".format(msg, self._parser._config_source_hint) raise UsageError(self.format_usage() + msg) @@ -352,7 +350,7 @@ def parse_args(self, args=None, namespace=None): if arg and arg[0] == "-": lines = ["unrecognized arguments: %s" % (" ".join(argv))] for k, v in sorted(self.extra_info.items()): - lines.append(" %s: %s" % (k, v)) + lines.append(" {}: {}".format(k, v)) self.error("\n".join(lines)) getattr(args, FILE_OR_DIR).extend(argv) return args diff --git a/src/_pytest/config/exceptions.py b/src/_pytest/config/exceptions.py index bf58fde5dbf..19fe5cb08ed 100644 --- a/src/_pytest/config/exceptions.py +++ b/src/_pytest/config/exceptions.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- class UsageError(Exception): """ error in pytest usage or invocation""" diff --git a/src/_pytest/config/findpaths.py b/src/_pytest/config/findpaths.py index 3ece3bdc130..fa202447013 100644 --- a/src/_pytest/config/findpaths.py +++ b/src/_pytest/config/findpaths.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import os import py diff --git a/src/_pytest/debugging.py b/src/_pytest/debugging.py index 99d35a5ab77..891630b432d 100644 --- a/src/_pytest/debugging.py +++ b/src/_pytest/debugging.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- """ interactive debugging with PDB, the Python Debugger. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import argparse import pdb import sys @@ -74,7 +69,7 @@ def fin(): config._cleanup.append(fin) -class pytestPDB(object): +class pytestPDB: """ Pseudo PDB that defers to the real pdb. """ _pluginmanager = None @@ -128,18 +123,18 @@ def _import_pdb_cls(cls, capman): def _get_pdb_wrapper_class(cls, pdb_cls, capman): import _pytest.config - class PytestPdbWrapper(pdb_cls, object): + class PytestPdbWrapper(pdb_cls): _pytest_capman = capman _continued = False def do_debug(self, arg): cls._recursive_debug += 1 - ret = super(PytestPdbWrapper, self).do_debug(arg) + ret = super().do_debug(arg) cls._recursive_debug -= 1 return ret def do_continue(self, arg): - ret = super(PytestPdbWrapper, self).do_continue(arg) + ret = super().do_continue(arg) if cls._recursive_debug == 0: tw = _pytest.config.create_terminal_writer(cls._config) tw.line() @@ -171,7 +166,7 @@ def do_quit(self, arg): could be handled, but this would require to wrap the whole pytest run, and adjust the report etc. """ - ret = super(PytestPdbWrapper, self).do_quit(arg) + ret = super().do_quit(arg) if cls._recursive_debug == 0: outcomes.exit("Quitting debugger") @@ -187,7 +182,7 @@ def setup(self, f, tb): Needed after do_continue resumed, and entering another breakpoint again. """ - ret = super(PytestPdbWrapper, self).setup(f, tb) + ret = super().setup(f, tb) if not ret and self._continued: # pdb.setup() returns True if the command wants to exit # from the interaction: do not suspend capturing then. @@ -196,7 +191,7 @@ def setup(self, f, tb): return ret def get_stack(self, f, t): - stack, i = super(PytestPdbWrapper, self).get_stack(f, t) + stack, i = super().get_stack(f, t) if f is None: # Find last non-hidden frame. i = max(0, len(stack) - 1) @@ -230,7 +225,7 @@ def _init_pdb(cls, method, *args, **kwargs): else: capturing = cls._is_capturing(capman) if capturing == "global": - tw.sep(">", "PDB %s (IO-capturing turned off)" % (method,)) + tw.sep(">", "PDB {} (IO-capturing turned off)".format(method)) elif capturing: tw.sep( ">", @@ -238,7 +233,7 @@ def _init_pdb(cls, method, *args, **kwargs): % (method, capturing), ) else: - tw.sep(">", "PDB %s" % (method,)) + tw.sep(">", "PDB {}".format(method)) _pdb = cls._import_pdb_cls(capman)(**kwargs) @@ -254,7 +249,7 @@ def set_trace(cls, *args, **kwargs): _pdb.set_trace(frame) -class PdbInvoke(object): +class PdbInvoke: def pytest_exception_interact(self, node, call, report): capman = node.config.pluginmanager.getplugin("capturemanager") if capman: @@ -269,7 +264,7 @@ def pytest_internalerror(self, excrepr, excinfo): post_mortem(tb) -class PdbTrace(object): +class PdbTrace: @hookimpl(hookwrapper=True) def pytest_pyfunc_call(self, pyfuncitem): _test_pytest_function(pyfuncitem) diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index 6283a840246..f80773fe577 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ This module contains deprecation messages and bits of code used elsewhere in the codebase that is planned to be removed in the next pytest release. @@ -9,10 +8,6 @@ All constants defined in this module should be either PytestWarning instances or UnformattedWarning in case of warnings which need to format their messages. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - from _pytest.warning_types import PytestDeprecationWarning from _pytest.warning_types import RemovedInPytest4Warning from _pytest.warning_types import UnformattedWarning diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index 95a80bb4fa2..afb7ede4cc7 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- """ discover and run doctests in modules and test files.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import inspect import platform import sys @@ -126,7 +121,7 @@ def toterminal(self, tw): class MultipleDoctestFailures(Exception): def __init__(self, failures): - super(MultipleDoctestFailures, self).__init__() + super().__init__() self.failures = failures @@ -181,7 +176,7 @@ def _get_runner(checker=None, verbose=None, optionflags=0, continue_on_failure=T class DoctestItem(pytest.Item): def __init__(self, name, parent, runner=None, dtest=None): - super(DoctestItem, self).__init__(name, parent) + super().__init__(name, parent) self.runner = runner self.dtest = dtest self.obj = None @@ -258,7 +253,7 @@ def repr_failure(self, excinfo): ] indent = ">>>" for line in example.source.splitlines(): - lines.append("??? %s %s" % (indent, line)) + lines.append("??? {} {}".format(indent, line)) indent = "..." if isinstance(failure, doctest.DocTestFailure): lines += checker.output_difference( @@ -271,7 +266,7 @@ def repr_failure(self, excinfo): reprlocation_lines.append((reprlocation, lines)) return ReprFailDoctest(reprlocation_lines) else: - return super(DoctestItem, self).repr_failure(excinfo) + return super().repr_failure(excinfo) def reportinfo(self): return self.fspath, self.dtest.lineno, "[doctest] %s" % self.name @@ -333,7 +328,6 @@ def collect(self): checker=_get_checker(), continue_on_failure=_get_continue_on_failure(self.config), ) - _fix_spoof_python2(runner, encoding) parser = doctest.DocTestParser() test = parser.get_doctest(text, globs, name, filename, 0) @@ -539,32 +533,6 @@ def _get_report_choice(key): }[key] -def _fix_spoof_python2(runner, encoding): - """ - Installs a "SpoofOut" into the given DebugRunner so it properly deals with unicode output. This - should patch only doctests for text files because they don't have a way to declare their - encoding. Doctests in docstrings from Python modules don't have the same problem given that - Python already decoded the strings. - - This fixes the problem related in issue #2434. - """ - from _pytest.compat import _PY2 - - if not _PY2: - return - - from doctest import _SpoofOut - - class UnicodeSpoof(_SpoofOut): - def getvalue(self): - result = _SpoofOut.getvalue(self) - if encoding and isinstance(result, bytes): - result = result.decode(encoding) - return result - - runner._fakeout = UnicodeSpoof() - - @pytest.fixture(scope="session") def doctest_namespace(): """ diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 280a48608b6..2f9b10b85d8 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -1,8 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import functools import inspect import itertools @@ -14,7 +9,6 @@ import attr import py -import six import _pytest from _pytest import nodes @@ -41,7 +35,7 @@ @attr.s(frozen=True) -class PseudoFixtureDef(object): +class PseudoFixtureDef: cached_result = attr.ib() scope = attr.ib() @@ -81,7 +75,7 @@ def provide(self): if func.__name__ in scope2props[self.scope]: return func(self) raise AttributeError( - "%s not available in %s-scoped context" % (scopename, self.scope) + "{} not available in {}-scoped context".format(scopename, self.scope) ) return property(provide, None, None, func.__doc__) @@ -94,7 +88,7 @@ def get_scope_package(node, fixturedef): cls = pytest.Package current = node - fixture_package_name = "%s/%s" % (fixturedef.baseid, "__init__.py") + fixture_package_name = "{}/{}".format(fixturedef.baseid, "__init__.py") while current and ( type(current) is not cls or fixture_package_name != current.nodeid ): @@ -301,7 +295,7 @@ def get_direct_param_fixture_func(request): @attr.s(slots=True) -class FuncFixtureInfo(object): +class FuncFixtureInfo: # original function argument names argnames = attr.ib(type=tuple) # argnames that function immediately requires. These include argnames + @@ -657,7 +651,7 @@ def __init__(self, request, scope, param, param_index, fixturedef): self._fixturemanager = request._fixturemanager def __repr__(self): - return "" % (self.fixturename, self._pyfuncitem) + return "".format(self.fixturename, self._pyfuncitem) def addfinalizer(self, finalizer): self._fixturedef.addfinalizer(finalizer) @@ -670,7 +664,7 @@ def _schedule_finalizers(self, fixturedef, subrequest): fixturedef.addfinalizer( functools.partial(self._fixturedef.finish, request=self) ) - super(SubRequest, self)._schedule_finalizers(fixturedef, subrequest) + super()._schedule_finalizers(fixturedef, subrequest) scopes = "session package module class function".split() @@ -723,7 +717,7 @@ def formatrepr(self): error_msg = "file %s, line %s: source code not available" addline(error_msg % (fspath, lineno + 1)) else: - addline("file %s, line %s" % (fspath, lineno + 1)) + addline("file {}, line {}".format(fspath, lineno + 1)) for i, line in enumerate(lines): line = line.rstrip() addline(" " + line) @@ -779,7 +773,7 @@ def toterminal(self, tw): def fail_fixturefunc(fixturefunc, msg): fs, lineno = getfslineno(fixturefunc) - location = "%s:%s" % (fs, lineno + 1) + location = "{}:{}".format(fs, lineno + 1) source = _pytest._code.Source(fixturefunc) fail(msg + ":\n\n" + str(source.indent()) + "\n" + location, pytrace=False) @@ -809,7 +803,7 @@ def _teardown_yield_fixture(fixturefunc, it): ) -class FixtureDef(object): +class FixtureDef: """ A container for a factory definition. """ def __init__( @@ -853,10 +847,10 @@ def finish(self, request): except: # noqa exceptions.append(sys.exc_info()) if exceptions: - e = exceptions[0] + _, val, tb = exceptions[0] # Ensure to not keep frame references through traceback. del exceptions - six.reraise(*e) + raise val.with_traceback(tb) finally: hook = self._fixturemanager.session.gethookproxy(request.node.fspath) hook.pytest_fixture_post_finalizer(fixturedef=self, request=request) @@ -882,7 +876,8 @@ def execute(self, request): result, cache_key, err = cached_result if my_cache_key == cache_key: if err is not None: - six.reraise(*err) + _, val, tb = err + raise val.with_traceback(tb) else: return result # we have a previous but differently parametrized fixture instance @@ -894,10 +889,8 @@ def execute(self, request): return hook.pytest_fixture_setup(fixturedef=self, request=request) def __repr__(self): - return "" % ( - self.argname, - self.scope, - self.baseid, + return "".format( + self.argname, self.scope, self.baseid ) @@ -957,7 +950,7 @@ def wrap_function_to_error_out_if_called_directly(function, fixture_marker): name=fixture_marker.name or function.__name__ ) - @six.wraps(function) + @functools.wraps(function) def result(*args, **kwargs): fail(message, pytrace=False) @@ -969,7 +962,7 @@ def result(*args, **kwargs): @attr.s(frozen=True) -class FixtureFunctionMarker(object): +class FixtureFunctionMarker: scope = attr.ib() params = attr.ib(converter=attr.converters.optional(tuple)) autouse = attr.ib(default=False) @@ -1083,7 +1076,7 @@ def pytest_addoption(parser): ) -class FixtureManager(object): +class FixtureManager: """ pytest fixtures definitions and information is stored and managed from this class. @@ -1303,11 +1296,7 @@ def parsefactories(self, node_or_obj, nodeid=NOTSET, unittest=False): # during fixture definition we wrap the original fixture function # to issue a warning if called directly, so here we unwrap it in order to not emit the warning # when pytest itself calls the fixture function - if six.PY2 and unittest: - # hack on Python 2 because of the unbound methods - obj = get_real_func(obj) - else: - obj = get_real_method(obj, holderobj) + obj = get_real_method(obj, holderobj) fixture_def = FixtureDef( self, diff --git a/src/_pytest/freeze_support.py b/src/_pytest/freeze_support.py index aeeec2a56b4..f9d613a2b64 100644 --- a/src/_pytest/freeze_support.py +++ b/src/_pytest/freeze_support.py @@ -1,11 +1,7 @@ -# -*- coding: utf-8 -*- """ Provides a function to report all internal modules for using freezing tools pytest """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function def freeze_includes(): diff --git a/src/_pytest/helpconfig.py b/src/_pytest/helpconfig.py index 56811601608..b379fae01dc 100644 --- a/src/_pytest/helpconfig.py +++ b/src/_pytest/helpconfig.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- """ version info, help messages, tracing configuration. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import os import sys from argparse import Action @@ -24,7 +19,7 @@ class HelpAction(Action): """ def __init__(self, option_strings, dest=None, default=False, help=None): - super(HelpAction, self).__init__( + super().__init__( option_strings=option_strings, dest=dest, const=True, @@ -122,7 +117,7 @@ def unset_tracing(): def showversion(config): p = py.path.local(pytest.__file__) sys.stderr.write( - "This is pytest version %s, imported from %s\n" % (pytest.__version__, p) + "This is pytest version {}, imported from {}\n".format(pytest.__version__, p) ) plugininfo = getpluginversioninfo(config) if plugininfo: @@ -160,7 +155,7 @@ def showhelp(config): help, type, default = config._parser._inidict[name] if type is None: type = "string" - spec = "%s (%s):" % (name, type) + spec = "{} ({}):".format(name, type) tw.write(" %s" % spec) spec_len = len(spec) if spec_len > (indent_len - 3): @@ -194,7 +189,7 @@ def showhelp(config): ("PYTEST_DEBUG", "set to enable debug tracing of pytest's internals"), ] for name, help in vars: - tw.line(" %-24s %s" % (name, help)) + tw.line(" {:<24} {}".format(name, help)) tw.line() tw.line() @@ -221,7 +216,7 @@ def getpluginversioninfo(config): lines.append("setuptools registered plugins:") for plugin, dist in plugininfo: loc = getattr(plugin, "__file__", repr(plugin)) - content = "%s-%s at %s" % (dist.project_name, dist.version, loc) + content = "{}-{} at {}".format(dist.project_name, dist.version, loc) lines.append(" " + content) return lines @@ -229,7 +224,9 @@ def getpluginversioninfo(config): def pytest_report_header(config): lines = [] if config.option.debug or config.option.traceconfig: - lines.append("using: pytest-%s pylib-%s" % (pytest.__version__, py.__version__)) + lines.append( + "using: pytest-{} pylib-{}".format(pytest.__version__, py.__version__) + ) verinfo = getpluginversioninfo(config) if verinfo: @@ -243,5 +240,5 @@ def pytest_report_header(config): r = plugin.__file__ else: r = repr(plugin) - lines.append(" %-20s: %s" % (name, r)) + lines.append(" {:<20}: {}".format(name, r)) return lines diff --git a/src/_pytest/hookspec.py b/src/_pytest/hookspec.py index 7ab6154b176..d40a368116c 100644 --- a/src/_pytest/hookspec.py +++ b/src/_pytest/hookspec.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ hook specifications for pytest plugins, invoked from main.py and builtin plugins. """ from pluggy import HookspecMarker diff --git a/src/_pytest/junitxml.py b/src/_pytest/junitxml.py index 3972113cbb0..ea33e606c30 100644 --- a/src/_pytest/junitxml.py +++ b/src/_pytest/junitxml.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ report test results in JUnit-XML format, for use with Jenkins and build integration servers. @@ -9,10 +8,6 @@ Output conforms to https://github.com/jenkinsci/xunit-plugin/blob/master/ src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import functools import os import re @@ -20,16 +15,11 @@ import time import py -import six import pytest from _pytest import nodes from _pytest.config import filename_arg -# Python 2.X and 3.X compatibility -if sys.version_info[0] < 3: - from codecs import open - class Junit(py.xml.Namespace): pass @@ -43,12 +33,12 @@ class Junit(py.xml.Namespace): _legal_chars = (0x09, 0x0A, 0x0D) _legal_ranges = ((0x20, 0x7E), (0x80, 0xD7FF), (0xE000, 0xFFFD), (0x10000, 0x10FFFF)) _legal_xml_re = [ - u"%s-%s" % (six.unichr(low), six.unichr(high)) + "{}-{}".format(chr(low), chr(high)) for (low, high) in _legal_ranges if low < sys.maxunicode ] -_legal_xml_re = [six.unichr(x) for x in _legal_chars] + _legal_xml_re -illegal_xml_re = re.compile(u"[^%s]" % u"".join(_legal_xml_re)) +_legal_xml_re = [chr(x) for x in _legal_chars] + _legal_xml_re +illegal_xml_re = re.compile("[^%s]" % "".join(_legal_xml_re)) del _legal_chars del _legal_ranges del _legal_xml_re @@ -60,9 +50,9 @@ def bin_xml_escape(arg): def repl(matchobj): i = ord(matchobj.group()) if i <= 0xFF: - return u"#x%02X" % i + return "#x%02X" % i else: - return u"#x%04X" % i + return "#x%04X" % i return py.xml.raw(illegal_xml_re.sub(repl, py.xml.escape(arg))) @@ -89,7 +79,7 @@ def merge_family(left, right): families["xunit2"] = families["_base"] -class _NodeReporter(object): +class _NodeReporter: def __init__(self, nodeid, xml): self.id = nodeid self.xml = xml @@ -229,7 +219,7 @@ def append_failure(self, report): else: if hasattr(report.longrepr, "reprcrash"): message = report.longrepr.reprcrash.message - elif isinstance(report.longrepr, six.string_types): + elif isinstance(report.longrepr, str): message = report.longrepr else: message = str(report.longrepr) @@ -268,7 +258,7 @@ def append_skipped(self, report): filename, lineno, skipreason = report.longrepr if skipreason.startswith("Skipped: "): skipreason = skipreason[9:] - details = "%s:%s: %s" % (filename, lineno, skipreason) + details = "{}:{}: {}".format(filename, lineno, skipreason) self.append( Junit.skipped( @@ -353,7 +343,7 @@ def _check_record_param_type(param, v): """Used by record_testsuite_property to check that the given parameter name is of the proper type""" __tracebackhide__ = True - if not isinstance(v, six.string_types): + if not isinstance(v, str): msg = "{param} parameter needs to be a string, but {g} given" raise TypeError(msg.format(param=param, g=type(v).__name__)) @@ -473,7 +463,7 @@ def mangle_test_address(address): return names -class LogXML(object): +class LogXML: def __init__( self, logfile, diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index 577a5407be0..ac0c4c2b337 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -1,15 +1,9 @@ -# -*- coding: utf-8 -*- """ Access and control log capturing. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import logging import re from contextlib import contextmanager import py -import six import pytest from _pytest.compat import dummy_context_manager @@ -39,14 +33,11 @@ class ColoredLevelFormatter(logging.Formatter): logging.DEBUG: {"purple"}, logging.NOTSET: set(), } - LEVELNAME_FMT_REGEX = re.compile(r"%\(levelname\)([+-]?\d*s)") + LEVELNAME_FMT_REGEX = re.compile(r"%\(levelname\)([+-.]?\d*s)") def __init__(self, terminalwriter, *args, **kwargs): - super(ColoredLevelFormatter, self).__init__(*args, **kwargs) - if six.PY2: - self._original_fmt = self._fmt - else: - self._original_fmt = self._style._fmt + super().__init__(*args, **kwargs) + self._original_fmt = self._style._fmt self._level_to_fmt_mapping = {} levelname_fmt_match = self.LEVELNAME_FMT_REGEX.search(self._fmt) @@ -70,41 +61,35 @@ def __init__(self, terminalwriter, *args, **kwargs): def format(self, record): fmt = self._level_to_fmt_mapping.get(record.levelno, self._original_fmt) - if six.PY2: - self._fmt = fmt - else: - self._style._fmt = fmt - return super(ColoredLevelFormatter, self).format(record) + self._style._fmt = fmt + return super().format(record) -if not six.PY2: - # Formatter classes don't support format styles in PY2 +class PercentStyleMultiline(logging.PercentStyle): + """A logging style with special support for multiline messages. - class PercentStyleMultiline(logging.PercentStyle): - """A logging style with special support for multiline messages. + If the message of a record consists of multiple lines, this style + formats the message as if each line were logged separately. + """ - If the message of a record consists of multiple lines, this style - formats the message as if each line were logged separately. - """ + @staticmethod + def _update_message(record_dict, message): + tmp = record_dict.copy() + tmp["message"] = message + return tmp - @staticmethod - def _update_message(record_dict, message): - tmp = record_dict.copy() - tmp["message"] = message - return tmp - - def format(self, record): - if "\n" in record.message: - lines = record.message.splitlines() - formatted = self._fmt % self._update_message(record.__dict__, lines[0]) - # TODO optimize this by introducing an option that tells the - # logging framework that the indentation doesn't - # change. This allows to compute the indentation only once. - indentation = _remove_ansi_escape_sequences(formatted).find(lines[0]) - lines[0] = formatted - return ("\n" + " " * indentation).join(lines) - else: - return self._fmt % record.__dict__ + def format(self, record): + if "\n" in record.message: + lines = record.message.splitlines() + formatted = self._fmt % self._update_message(record.__dict__, lines[0]) + # TODO optimize this by introducing an option that tells the + # logging framework that the indentation doesn't + # change. This allows to compute the indentation only once. + indentation = _remove_ansi_escape_sequences(formatted).find(lines[0]) + lines[0] = formatted + return ("\n" + " " * indentation).join(lines) + else: + return self._fmt % record.__dict__ def get_option_ini(config, *names): @@ -246,7 +231,7 @@ def reset(self): self.stream = py.io.TextIO() -class LogCaptureFixture(object): +class LogCaptureFixture: """Provides access and control of log capturing.""" def __init__(self, item): @@ -393,7 +378,7 @@ def get_actual_log_level(config, *setting_names): else: return - if isinstance(log_level, six.string_types): + if isinstance(log_level, str): log_level = log_level.upper() try: return int(getattr(logging, log_level, log_level)) @@ -412,7 +397,7 @@ def pytest_configure(config): config.pluginmanager.register(LoggingPlugin(config), "logging-plugin") -class LoggingPlugin(object): +class LoggingPlugin: """Attaches to the logging module and captures log messages for each test. """ @@ -475,8 +460,7 @@ def _create_formatter(self, log_format, log_date_format): else: formatter = logging.Formatter(log_format, log_date_format) - if not six.PY2: - formatter._style = PercentStyleMultiline(formatter._style._fmt) + formatter._style = PercentStyleMultiline(formatter._style._fmt) return formatter def _setup_cli_logging(self): diff --git a/src/_pytest/main.py b/src/_pytest/main.py index fa4d8d3d509..735d60bd68e 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -1,10 +1,4 @@ -# -*- coding: utf-8 -*- """ core implementation of testing process: init, session, runtest loop. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import contextlib import fnmatch import functools import os @@ -14,7 +8,6 @@ import attr import py -import six import _pytest._code from _pytest import nodes @@ -172,7 +165,7 @@ def pytest_addoption(parser): ) -class _ConfigDeprecated(object): +class _ConfigDeprecated: def __init__(self, config): self.__dict__["_config"] = config @@ -311,10 +304,7 @@ def pytest_ignore_collect(path, config): if excludeglobopt: ignore_globs.extend([py.path.local(x) for x in excludeglobopt]) - if any( - fnmatch.fnmatch(six.text_type(path), six.text_type(glob)) - for glob in ignore_globs - ): + if any(fnmatch.fnmatch(str(path), str(glob)) for glob in ignore_globs): return True allow_in_venv = config.getoption("collect_in_virtualenv") @@ -342,47 +332,7 @@ def pytest_collection_modifyitems(items, config): items[:] = remaining -@contextlib.contextmanager -def _patched_find_module(): - """Patch bug in pkgutil.ImpImporter.find_module - - When using pkgutil.find_loader on python<3.4 it removes symlinks - from the path due to a call to os.path.realpath. This is not consistent - with actually doing the import (in these versions, pkgutil and __import__ - did not share the same underlying code). This can break conftest - discovery for pytest where symlinks are involved. - - The only supported python<3.4 by pytest is python 2.7. - """ - if six.PY2: # python 3.4+ uses importlib instead - - def find_module_patched(self, fullname, path=None): - # Note: we ignore 'path' argument since it is only used via meta_path - subname = fullname.split(".")[-1] - if subname != fullname and self.path is None: - return None - if self.path is None: - path = None - else: - # original: path = [os.path.realpath(self.path)] - path = [self.path] - try: - file, filename, etc = pkgutil.imp.find_module(subname, path) - except ImportError: - return None - return pkgutil.ImpLoader(fullname, file, filename, etc) - - old_find_module = pkgutil.ImpImporter.find_module - pkgutil.ImpImporter.find_module = find_module_patched - try: - yield - finally: - pkgutil.ImpImporter.find_module = old_find_module - else: - yield - - -class FSHookProxy(object): +class FSHookProxy: def __init__(self, fspath, pm, remove_mods): self.fspath = fspath self.pm = pm @@ -522,8 +472,8 @@ def _perform_collect(self, args, genitems): if self._notfound: errors = [] for arg, exc in self._notfound: - line = "(no name %r in any of %r)" % (arg, exc.args[0]) - errors.append("not found: %s\n%s" % (arg, line)) + line = "(no name {!r} in any of {!r})".format(arg, exc.args[0]) + errors.append("not found: {}\n{}".format(arg, line)) # XXX: test this raise UsageError(*errors) if not genitems: @@ -540,8 +490,7 @@ def collect(self): self.trace("processing argument", arg) self.trace.root.indent += 1 try: - for x in self._collect(arg): - yield x + yield from self._collect(arg) except NoMatch: # we are inside a make_report hook so # we cannot directly pass through the exception @@ -578,7 +527,7 @@ def _collect(self, arg): # If it's a directory argument, recurse and look for any Subpackages. # Let the Package collector deal with subnodes, don't collect here. if argpath.check(dir=1): - assert not names, "invalid arg %r" % (arg,) + assert not names, "invalid arg {!r}".format(arg) seen_dirs = set() for path in argpath.visit( @@ -623,15 +572,13 @@ def _collect(self, arg): if argpath.basename == "__init__.py": yield next(m[0].collect()) return - for y in m: - yield y + yield from m def _collectfile(self, path, handle_dupes=True): - assert path.isfile(), "%r is not a file (isdir=%r, exists=%r, islink=%r)" % ( - path, - path.isdir(), - path.exists(), - path.islink(), + assert ( + path.isfile() + ), "{!r} is not a file (isdir={!r}, exists={!r}, islink={!r})".format( + path, path.isdir(), path.exists(), path.islink() ) ihook = self.gethookproxy(path) if not self.isinitpath(path): @@ -662,23 +609,14 @@ def _recurse(self, dirpath): ihook.pytest_collect_directory(path=dirpath, parent=self) return True - if six.PY2: - - @staticmethod - def _visit_filter(f): - return f.check(file=1) and not f.strpath.endswith("*.pyc") - - else: - - @staticmethod - def _visit_filter(f): - return f.check(file=1) + @staticmethod + def _visit_filter(f): + return f.check(file=1) def _tryconvertpyarg(self, x): """Convert a dotted module name to path.""" try: - with _patched_find_module(): - loader = pkgutil.find_loader(x) + loader = pkgutil.find_loader(x) except ImportError: return x if loader is None: @@ -686,8 +624,7 @@ def _tryconvertpyarg(self, x): # This method is sometimes invoked when AssertionRewritingHook, which # does not define a get_filename method, is already in place: try: - with _patched_find_module(): - path = loader.get_filename(x) + path = loader.get_filename(x) except AttributeError: # Retrieve path from AssertionRewritingHook: path = loader.modules[x][0].co_filename @@ -769,6 +706,5 @@ def genitems(self, node): rep = collect_one_node(node) if rep.passed: for subnode in rep.result: - for x in self.genitems(subnode): - yield x + yield from self.genitems(subnode) node.ihook.pytest_collectreport(report=rep) diff --git a/src/_pytest/mark/__init__.py b/src/_pytest/mark/__init__.py index 6bc22fe27da..30c6e0048f5 100644 --- a/src/_pytest/mark/__init__.py +++ b/src/_pytest/mark/__init__.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- """ generic mechanism for marking and selecting python functions. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - from .legacy import matchkeyword from .legacy import matchmark from .structures import EMPTY_PARAMETERSET_OPTION diff --git a/src/_pytest/mark/evaluate.py b/src/_pytest/mark/evaluate.py index 506546e253a..898278e30b3 100644 --- a/src/_pytest/mark/evaluate.py +++ b/src/_pytest/mark/evaluate.py @@ -1,11 +1,8 @@ -# -*- coding: utf-8 -*- import os import platform import sys import traceback -import six - from ..outcomes import fail from ..outcomes import TEST_OUTCOME @@ -23,7 +20,7 @@ def cached_eval(config, expr, d): return x -class MarkEvaluator(object): +class MarkEvaluator: def __init__(self, item, name): self.item = item self._marks = None @@ -87,7 +84,7 @@ def _istrue(self): for expr in args: self.expr = expr - if isinstance(expr, six.string_types): + if isinstance(expr, str): d = self._getglobals() result = cached_eval(self.item.config, expr, d) else: diff --git a/src/_pytest/mark/legacy.py b/src/_pytest/mark/legacy.py index c56482f14d0..d14ea3a8274 100644 --- a/src/_pytest/mark/legacy.py +++ b/src/_pytest/mark/legacy.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ this is a place where we put datastructures used by legacy apis we hope ot remove @@ -11,7 +10,7 @@ @attr.s -class MarkMapping(object): +class MarkMapping: """Provides a local mapping for markers where item access resolves to True if the marker is present. """ @@ -26,7 +25,7 @@ def __getitem__(self, name): return name in self.own_mark_names -class KeywordMapping(object): +class KeywordMapping: """Provides a local mapping for keywords. Given a list of names, map any substring of one of these names to True. """ diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index 561ccc3f45d..39cdb57e46d 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -1,15 +1,13 @@ -# -*- coding: utf-8 -*- import inspect import warnings from collections import namedtuple +from collections.abc import MutableMapping from operator import attrgetter import attr -import six from ..compat import ascii_escaped from ..compat import getfslineno -from ..compat import MappingMixin from ..compat import NOTSET from _pytest.deprecated import PYTEST_PARAM_UNKNOWN_KWARGS from _pytest.outcomes import fail @@ -72,7 +70,7 @@ def param(cls, *values, **kwargs): id_ = kwargs.pop("id", None) if id_ is not None: - if not isinstance(id_, six.string_types): + if not isinstance(id_, str): raise TypeError( "Expected id to be a string, got {}: {!r}".format(type(id_), id_) ) @@ -113,14 +111,18 @@ def _parse_parametrize_args(argnames, argvalues, **_): force_tuple = len(argnames) == 1 else: force_tuple = False - parameters = [ + return argnames, force_tuple + + @staticmethod + def _parse_parametrize_parameters(argvalues, force_tuple): + return [ ParameterSet.extract_from(x, force_tuple=force_tuple) for x in argvalues ] - return argnames, parameters @classmethod def _for_parametrize(cls, argnames, argvalues, func, config, function_definition): - argnames, parameters = cls._parse_parametrize_args(argnames, argvalues) + argnames, force_tuple = cls._parse_parametrize_args(argnames, argvalues) + parameters = cls._parse_parametrize_parameters(argvalues, force_tuple) del argvalues if parameters: @@ -154,7 +156,7 @@ def _for_parametrize(cls, argnames, argvalues, func, config, function_definition @attr.s(frozen=True) -class Mark(object): +class Mark: #: name of the mark name = attr.ib(type=str) #: positional arguments of the mark decorator @@ -177,7 +179,7 @@ def combined_with(self, other): @attr.s -class MarkDecorator(object): +class MarkDecorator: """ A decorator for test functions and test classes. When applied it will create :class:`MarkInfo` objects which may be :ref:`retrieved by hooks as item keywords `. @@ -225,7 +227,7 @@ def __eq__(self, other): return self.mark == other.mark if isinstance(other, MarkDecorator) else False def __repr__(self): - return "" % (self.mark,) + return "".format(self.mark) def with_args(self, *args, **kwargs): """ return a MarkDecorator with extra arguments added @@ -286,7 +288,7 @@ def store_mark(obj, mark): obj.pytestmark = get_unpacked_marks(obj) + [mark] -class MarkGenerator(object): +class MarkGenerator: """ Factory for :class:`MarkDecorator` objects - exposed as a ``pytest.mark`` singleton instance. Example:: @@ -339,7 +341,7 @@ def __getattr__(self, name): MARK_GEN = MarkGenerator() -class NodeKeywords(MappingMixin): +class NodeKeywords(MutableMapping): def __init__(self, node): self.node = node self.parent = node.parent @@ -373,11 +375,11 @@ def __len__(self): return len(self._seen()) def __repr__(self): - return "" % (self.node,) + return "".format(self.node) @attr.s(cmp=False, hash=False) -class NodeMarkers(object): +class NodeMarkers: """ internal structure for storing marks belonging to a node diff --git a/src/_pytest/monkeypatch.py b/src/_pytest/monkeypatch.py index e8671b0c702..090bf61d6e9 100644 --- a/src/_pytest/monkeypatch.py +++ b/src/_pytest/monkeypatch.py @@ -1,17 +1,10 @@ -# -*- coding: utf-8 -*- """ monkeypatching and mocking functionality. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import os import re import sys import warnings from contextlib import contextmanager -import six - import pytest from _pytest.fixtures import fixture from _pytest.pathlib import Path @@ -67,7 +60,7 @@ def resolve(name): if expected == used: raise else: - raise ImportError("import error in %s: %s" % (used, ex)) + raise ImportError("import error in {}: {}".format(used, ex)) found = annotated_getattr(found, part, used) return found @@ -77,14 +70,18 @@ def annotated_getattr(obj, name, ann): obj = getattr(obj, name) except AttributeError: raise AttributeError( - "%r object at %s has no attribute %r" % (type(obj).__name__, ann, name) + "{!r} object at {} has no attribute {!r}".format( + type(obj).__name__, ann, name + ) ) return obj def derive_importpath(import_path, raising): - if not isinstance(import_path, six.string_types) or "." not in import_path: - raise TypeError("must be absolute import path string, not %r" % (import_path,)) + if not isinstance(import_path, str) or "." not in import_path: + raise TypeError( + "must be absolute import path string, not {!r}".format(import_path) + ) module, attr = import_path.rsplit(".", 1) target = resolve(module) if raising: @@ -92,7 +89,7 @@ def derive_importpath(import_path, raising): return attr, target -class Notset(object): +class Notset: def __repr__(self): return "" @@ -100,7 +97,7 @@ def __repr__(self): notset = Notset() -class MonkeyPatch(object): +class MonkeyPatch: """ Object returned by the ``monkeypatch`` fixture keeping a record of setattr/item/env/syspath changes. """ @@ -151,7 +148,7 @@ def setattr(self, target, name, value=notset, raising=True): import inspect if value is notset: - if not isinstance(target, six.string_types): + if not isinstance(target, str): raise TypeError( "use setattr(target, name, value) or " "setattr(target, value) with target being a dotted " @@ -162,7 +159,7 @@ def setattr(self, target, name, value=notset, raising=True): oldval = getattr(target, name, notset) if raising and oldval is notset: - raise AttributeError("%r has no attribute %r" % (target, name)) + raise AttributeError("{!r} has no attribute {!r}".format(target, name)) # avoid class descriptors like staticmethod/classmethod if inspect.isclass(target): @@ -185,7 +182,7 @@ def delattr(self, target, name=notset, raising=True): import inspect if name is notset: - if not isinstance(target, six.string_types): + if not isinstance(target, str): raise TypeError( "use delattr(target, name) or " "delattr(target) with target being a dotted " @@ -222,15 +219,6 @@ def delitem(self, dic, name, raising=True): self._setitem.append((dic, name, dic.get(name, notset))) del dic[name] - def _warn_if_env_name_is_not_str(self, name): - """On Python 2, warn if the given environment variable name is not a native str (#4056)""" - if six.PY2 and not isinstance(name, str): - warnings.warn( - pytest.PytestWarning( - "Environment variable name {!r} should be str".format(name) - ) - ) - def setenv(self, name, value, prepend=None): """ Set environment variable ``name`` to ``value``. If ``prepend`` is a character, read the current environment variable value @@ -248,7 +236,6 @@ def setenv(self, name, value, prepend=None): value = str(value) if prepend and name in os.environ: value = value + prepend + os.environ[name] - self._warn_if_env_name_is_not_str(name) self.setitem(os.environ, name, value) def delenv(self, name, raising=True): @@ -258,7 +245,6 @@ def delenv(self, name, raising=True): If ``raising`` is set to False, no exception will be raised if the environment variable is missing. """ - self._warn_if_env_name_is_not_str(name) self.delitem(os.environ, name, raising=raising) def syspath_prepend(self, path): @@ -279,10 +265,9 @@ def syspath_prepend(self, path): # since then the mtime based FileFinder cache (that gets created in # this case already) gets not invalidated when writing the new files # quickly afterwards. - if sys.version_info >= (3, 3): - from importlib import invalidate_caches + from importlib import invalidate_caches - invalidate_caches() + invalidate_caches() def chdir(self, path): """ Change the current working directory to the specified path. diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index 7342d960738..f476e414168 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -1,13 +1,7 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import os import warnings import py -import six import _pytest._code from _pytest.compat import getfslineno @@ -55,7 +49,7 @@ def ischildnode(baseid, nodeid): return node_parts[: len(base_parts)] == base_parts -class Node(object): +class Node: """ base class for Collector and Item the test collection tree. Collector subclasses have children, Items are terminal nodes.""" @@ -103,7 +97,7 @@ def ihook(self): return self.session.gethookproxy(self.fspath) def __repr__(self): - return "<%s %s>" % (self.__class__.__name__, getattr(self, "name", None)) + return "<{} {}>".format(self.__class__.__name__, getattr(self, "name", None)) def warn(self, warning): """Issue a warning for this item. @@ -173,7 +167,7 @@ def add_marker(self, marker, append=True): """ from _pytest.mark import MarkDecorator, MARK_GEN - if isinstance(marker, six.string_types): + if isinstance(marker, str): marker = getattr(MARK_GEN, marker) elif not isinstance(marker, MarkDecorator): raise ValueError("is not a string or pytest.mark.* Marker") @@ -244,7 +238,7 @@ def _prunetraceback(self, excinfo): def _repr_failure_py(self, excinfo, style=None): if excinfo.errisinstance(fail.Exception): if not excinfo.value.pytrace: - return six.text_type(excinfo.value) + return str(excinfo.value) fm = self.session._fixturemanager if excinfo.errisinstance(fm.FixtureLookupError): return excinfo.value.formatrepr() @@ -371,9 +365,7 @@ def __init__(self, fspath, parent=None, config=None, session=None, nodeid=None): if nodeid and os.sep != SEP: nodeid = nodeid.replace(os.sep, SEP) - super(FSCollector, self).__init__( - name, parent, config, session, nodeid=nodeid, fspath=fspath - ) + super().__init__(name, parent, config, session, nodeid=nodeid, fspath=fspath) class File(FSCollector): @@ -388,7 +380,7 @@ class Item(Node): nextitem = None def __init__(self, name, parent=None, config=None, session=None, nodeid=None): - super(Item, self).__init__(name, parent, config, session, nodeid=nodeid) + super().__init__(name, parent, config, session, nodeid=nodeid) self._report_sections = [] #: user properties is a list of tuples (name, value) that holds user diff --git a/src/_pytest/nose.py b/src/_pytest/nose.py index fbab91da249..bb5ca198c79 100644 --- a/src/_pytest/nose.py +++ b/src/_pytest/nose.py @@ -1,13 +1,6 @@ -# -*- coding: utf-8 -*- """ run test suites written for nose. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import sys -import six - import pytest from _pytest import python from _pytest import runner @@ -28,7 +21,7 @@ def pytest_runtest_makereport(item, call): if call.excinfo and call.excinfo.errisinstance(get_skip_exceptions()): # let's substitute the excinfo with a pytest.skip one call2 = runner.CallInfo.from_call( - lambda: pytest.skip(six.text_type(call.excinfo.value)), call.when + lambda: pytest.skip(str(call.excinfo.value)), call.when ) call.excinfo = call2.excinfo diff --git a/src/_pytest/outcomes.py b/src/_pytest/outcomes.py index 4620f957c7d..c63c80e106d 100644 --- a/src/_pytest/outcomes.py +++ b/src/_pytest/outcomes.py @@ -1,12 +1,7 @@ -# -*- coding: utf-8 -*- """ exception classes and constants handling test outcomes as well as functions creating them """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import sys from packaging.version import Version @@ -28,7 +23,7 @@ def __repr__(self): if isinstance(val, bytes): val = val.decode("UTF-8", errors="replace") return val - return "<%s instance>" % (self.__class__.__name__,) + return "<{} instance>".format(self.__class__.__name__) __str__ = __repr__ @@ -58,7 +53,7 @@ class Exit(Exception): def __init__(self, msg="unknown reason", returncode=None): self.msg = msg self.returncode = returncode - super(Exit, self).__init__(msg) + super().__init__(msg) # exposed helper methods @@ -171,7 +166,7 @@ def importorskip(modname, minversion=None, reason=None): import_exc = exc if import_exc: if reason is None: - reason = "could not import %r: %s" % (modname, import_exc) + reason = "could not import {!r}: {}".format(modname, import_exc) raise Skipped(reason, allow_module_level=True) mod = sys.modules[modname] if minversion is None: diff --git a/src/_pytest/pastebin.py b/src/_pytest/pastebin.py index 3f4171207a3..ce0e73accc2 100644 --- a/src/_pytest/pastebin.py +++ b/src/_pytest/pastebin.py @@ -1,14 +1,6 @@ -# -*- coding: utf-8 -*- """ submit failure or test session information to a pastebin service. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import sys import tempfile -import six - import pytest @@ -39,7 +31,7 @@ def pytest_configure(config): def tee_write(s, **kwargs): oldwrite(s, **kwargs) - if isinstance(s, six.text_type): + if isinstance(s, str): s = s.encode("utf-8") config._pastebinfile.write(s) @@ -70,23 +62,15 @@ def create_new_paste(contents): :returns: url to the pasted contents """ import re + from urllib.request import urlopen + from urllib.parse import urlencode - if sys.version_info < (3, 0): - from urllib import urlopen, urlencode - else: - from urllib.request import urlopen - from urllib.parse import urlencode - - params = { - "code": contents, - "lexer": "python3" if sys.version_info[0] == 3 else "python", - "expiry": "1week", - } + params = {"code": contents, "lexer": "python3", "expiry": "1week"} url = "https://bpaste.net" response = urlopen(url, data=urlencode(params).encode("ascii")).read() m = re.search(r'href="/raw/(\w+)"', response.decode("utf-8")) if m: - return "%s/show/%s" % (url, m.group(1)) + return "{}/show/{}".format(url, m.group(1)) else: return "bad response: " + response @@ -111,4 +95,4 @@ def pytest_terminal_summary(terminalreporter): s = tw.stringio.getvalue() assert len(s) pastebinurl = create_new_paste(s) - tr.write_line("%s --> %s" % (msg, pastebinurl)) + tr.write_line("{} --> {}".format(msg, pastebinurl)) diff --git a/src/_pytest/pathlib.py b/src/_pytest/pathlib.py index 729c41797d9..09b1bb3d53e 100644 --- a/src/_pytest/pathlib.py +++ b/src/_pytest/pathlib.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import atexit import errno import fnmatch @@ -8,19 +7,14 @@ import shutil import sys import uuid -from functools import reduce from os.path import expanduser from os.path import expandvars from os.path import isabs from os.path import sep from posixpath import sep as posix_sep -import six -from six.moves import map -from .compat import PY36 - -if PY36: +if sys.version_info[:2] >= (3, 6): from pathlib import Path, PurePath else: from pathlib2 import Path, PurePath @@ -84,17 +78,6 @@ def parse_num(maybe_num): return -1 -if six.PY2: - - def _max(iterable, default): - """needed due to python2.7 lacking the default argument for max""" - return reduce(max, iterable, default) - - -else: - _max = max - - def _force_symlink(root, target, link_to): """helper to create the current symlink @@ -119,7 +102,7 @@ def make_numbered_dir(root, prefix): """create a directory with an increased number as suffix for the given prefix""" for i in range(10): # try up to 10 times to create the folder - max_existing = _max(map(parse_num, find_suffixes(root, prefix)), default=-1) + max_existing = max(map(parse_num, find_suffixes(root, prefix)), default=-1) new_number = max_existing + 1 new_path = root.joinpath("{}{}".format(prefix, new_number)) try: @@ -143,9 +126,10 @@ def create_cleanup_lock(p): fd = os.open(str(lock_path), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644) except OSError as e: if e.errno == errno.EEXIST: - six.raise_from( - EnvironmentError("cannot create lockfile in {path}".format(path=p)), e - ) + raise EnvironmentError( + "cannot create lockfile in {path}".format(path=p) + ) from e + else: raise else: @@ -230,7 +214,7 @@ def try_cleanup(path, consider_lock_dead_if_created_before): def cleanup_candidates(root, prefix, keep): """lists candidates for numbered directories to be removed - follows py.path""" - max_existing = _max(map(parse_num, find_suffixes(root, prefix)), default=-1) + max_existing = max(map(parse_num, find_suffixes(root, prefix)), default=-1) max_delete = max_existing - keep paths = find_prefixed(root, prefix) paths, paths2 = itertools.tee(paths) @@ -311,7 +295,7 @@ def fnmatch_ex(pattern, path): if sep not in pattern: name = path.name else: - name = six.text_type(path) + name = str(path) return fnmatch.fnmatch(name, pattern) diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 605451630a6..c7a8ca693e4 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -1,10 +1,4 @@ -# -*- coding: utf-8 -*- """(disabled by default) support for testing pytest and pytest plugins.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import codecs import gc import os import platform @@ -13,11 +7,11 @@ import sys import time import traceback +from collections.abc import Sequence from fnmatch import fnmatch from weakref import WeakKeyDictionary import py -import six import pytest from _pytest._code import Source @@ -25,8 +19,6 @@ from _pytest.assertion.rewrite import AssertionRewritingHook from _pytest.capture import MultiCapture from _pytest.capture import SysCapture -from _pytest.compat import safe_str -from _pytest.compat import Sequence from _pytest.main import EXIT_INTERRUPTED from _pytest.main import EXIT_OK from _pytest.main import Session @@ -34,7 +26,7 @@ from _pytest.pathlib import Path IGNORE_PAM = [ # filenames added when obtaining details about the current user - u"/var/lib/sss/mc/passwd" + "/var/lib/sss/mc/passwd" ] @@ -84,7 +76,7 @@ def raise_on_kwargs(kwargs): ) -class LsofFdLeakChecker(object): +class LsofFdLeakChecker: def get_open_files(self): out = self._exec_lsof() open_files = self._parse_lsof_output(out) @@ -166,7 +158,7 @@ def _pytest(request): return PytestArg(request) -class PytestArg(object): +class PytestArg: def __init__(self, request): self.request = request @@ -181,7 +173,7 @@ def get_public_names(values): return [x for x in values if x[0] != "_"] -class ParsedCall(object): +class ParsedCall: def __init__(self, name, kwargs): self.__dict__.update(kwargs) self._name = name @@ -189,10 +181,10 @@ def __init__(self, name, kwargs): def __repr__(self): d = self.__dict__.copy() del d["_name"] - return "" % (self._name, d) + return "".format(self._name, d) -class HookRecorder(object): +class HookRecorder: """Record all hooks called in a plugin manager. This wraps all the hook calls in the plugin manager, recording each call @@ -239,7 +231,7 @@ def assert_contains(self, entries): break print("NONAMEMATCH", name, "with", call) else: - pytest.fail("could not find %r check %r" % (name, check)) + pytest.fail("could not find {!r} check {!r}".format(name, check)) def popcall(self, name): __tracebackhide__ = True @@ -247,7 +239,7 @@ def popcall(self, name): if call._name == name: del self.calls[i] return call - lines = ["could not find call %r, in:" % (name,)] + lines = ["could not find call {!r}, in:".format(name)] lines.extend([" %s" % x for x in self.calls]) pytest.fail("\n".join(lines)) @@ -284,7 +276,9 @@ def matchreport( ) if len(values) > 1: raise ValueError( - "found 2 or more testreports matching %r: %s" % (inamepart, values) + "found 2 or more testreports matching {!r}: {}".format( + inamepart, values + ) ) return values[0] @@ -358,7 +352,7 @@ def _config_for_test(): rex_outcome = re.compile(r"(\d+) ([\w-]+)") -class RunResult(object): +class RunResult: """The result of running a command. Attributes: @@ -430,7 +424,7 @@ def assert_outcomes( assert obtained == expected -class CwdSnapshot(object): +class CwdSnapshot: def __init__(self): self.__saved = os.getcwd() @@ -438,7 +432,7 @@ def restore(self): os.chdir(self.__saved) -class SysModulesSnapshot(object): +class SysModulesSnapshot: def __init__(self, preserve=None): self.__preserve = preserve self.__saved = dict(sys.modules) @@ -452,7 +446,7 @@ def restore(self): sys.modules.update(self.__saved) -class SysPathsSnapshot(object): +class SysPathsSnapshot: def __init__(self): self.__saved = list(sys.path), list(sys.meta_path) @@ -460,7 +454,7 @@ def restore(self): sys.path[:], sys.meta_path[:] = self.__saved -class Testdir(object): +class Testdir: """Temporary test directory with tools to test/run pytest itself. This is based on the ``tmpdir`` fixture but provides a number of methods @@ -513,7 +507,7 @@ def __init__(self, request, tmpdir_factory): self._env_run_update = {"HOME": tmphome, "USERPROFILE": tmphome} def __repr__(self): - return "" % (self.tmpdir,) + return "".format(self.tmpdir) def __str__(self): return str(self.tmpdir) @@ -558,10 +552,10 @@ def _makefile(self, ext, args, kwargs, encoding="utf-8"): items = list(kwargs.items()) def to_text(s): - return s.decode(encoding) if isinstance(s, bytes) else six.text_type(s) + return s.decode(encoding) if isinstance(s, bytes) else str(s) if args: - source = u"\n".join(to_text(x) for x in args) + source = "\n".join(to_text(x) for x in args) basename = self.request.function.__name__ items.insert(0, (basename, source)) @@ -570,7 +564,7 @@ def to_text(s): p = self.tmpdir.join(basename).new(ext=ext) p.dirpath().ensure_dir() source = Source(value) - source = u"\n".join(to_text(line) for line in source.lines) + source = "\n".join(to_text(line) for line in source.lines) p.write(source.strip().encode(encoding), "wb") if ret is None: ret = p @@ -839,7 +833,7 @@ def revert_warn_already_imported(): rec = [] - class Collect(object): + class Collect: def pytest_configure(x, config): rec.append(self.make_hook_recorder(config.pluginmanager)) @@ -849,7 +843,7 @@ def pytest_configure(x, config): reprec = rec.pop() else: - class reprec(object): + class reprec: pass reprec.ret = ret @@ -881,13 +875,13 @@ def runpytest_inprocess(self, *args, **kwargs): reprec = self.inline_run(*args, **kwargs) except SystemExit as e: - class reprec(object): + class reprec: ret = e.args[0] except Exception: traceback.print_exc() - class reprec(object): + class reprec: ret = 3 finally: @@ -911,7 +905,7 @@ def runpytest(self, *args, **kwargs): def _ensure_basetemp(self, args): args = list(args) for x in args: - if safe_str(x).startswith("--basetemp"): + if str(x).startswith("--basetemp"): break else: args.append("--basetemp=%s" % self.tmpdir.dirpath("basetemp")) @@ -969,10 +963,8 @@ def getitem(self, source, funcname="test_func"): for item in items: if item.name == funcname: return item - assert 0, "%r item not found in module:\n%s\nitems: %s" % ( - funcname, - source, - items, + assert 0, "{!r} item not found in module:\n{}\nitems: {}".format( + funcname, source, items ) def getitems(self, source): @@ -1096,8 +1088,8 @@ def run(self, *cmdargs, **kwargs): p2 = self.tmpdir.join("stderr") print("running:", *cmdargs) print(" in:", py.path.local()) - f1 = codecs.open(str(p1), "w", encoding="utf8") - f2 = codecs.open(str(p2), "w", encoding="utf8") + f1 = open(str(p1), "w", encoding="utf8") + f2 = open(str(p2), "w", encoding="utf8") try: now = time.time() popen = self.popen( @@ -1124,30 +1116,16 @@ def handle_timeout(): if timeout is None: ret = popen.wait() - elif six.PY3: + else: try: ret = popen.wait(timeout) except subprocess.TimeoutExpired: handle_timeout() - else: - end = time.time() + timeout - - resolution = min(0.1, timeout / 10) - - while True: - ret = popen.poll() - if ret is not None: - break - - if time.time() > end: - handle_timeout() - - time.sleep(resolution) finally: f1.close() f2.close() - f1 = codecs.open(str(p1), "r", encoding="utf8") - f2 = codecs.open(str(p2), "r", encoding="utf8") + f1 = open(str(p1), "r", encoding="utf8") + f2 = open(str(p2), "r", encoding="utf8") try: out = f1.read().splitlines() err = f2.read().splitlines() @@ -1163,7 +1141,7 @@ def _dump_lines(self, lines, fp): for line in lines: print(line, file=fp) except UnicodeEncodeError: - print("couldn't print to %s because of encoding" % (fp,)) + print("couldn't print to {} because of encoding".format(fp)) def _getpytestargs(self): return sys.executable, "-mpytest" @@ -1220,7 +1198,7 @@ def spawn_pytest(self, string, expect_timeout=10.0): """ basetemp = self.tmpdir.mkdir("temp-pexpect") invoke = " ".join(map(str, self._getpytestargs())) - cmd = "%s --basetemp=%s %s" % (invoke, basetemp, string) + cmd = "{} --basetemp={} {}".format(invoke, basetemp, string) return self.spawn(cmd, expect_timeout=expect_timeout) def spawn(self, cmd, expect_timeout=10.0): @@ -1250,10 +1228,12 @@ def getdecoded(out): try: return out.decode("utf-8") except UnicodeDecodeError: - return "INTERNAL not-utf8-decodeable, truncated string:\n%s" % (saferepr(out),) + return "INTERNAL not-utf8-decodeable, truncated string:\n{}".format( + saferepr(out) + ) -class LineComp(object): +class LineComp: def __init__(self): self.stringio = py.io.TextIO() @@ -1271,7 +1251,7 @@ def assert_contains_lines(self, lines2): return LineMatcher(lines1).fnmatch_lines(lines2) -class LineMatcher(object): +class LineMatcher: """Flexible matching of text. This is a convenience class to test large texts like the output of @@ -1409,5 +1389,5 @@ def _match_lines(self, lines2, match_func, match_nickname): self._log(" and:", repr(nextline)) extralines.append(nextline) else: - self._log("remains unmatched: %r" % (line,)) + self._log("remains unmatched: {!r}".format(line)) pytest.fail(self._log_text) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 5f1e6885be3..b4d8f5ae0b0 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -1,10 +1,6 @@ -# -*- coding: utf-8 -*- """ Python test discovery, setup and run of test functions. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import collections +import enum import fnmatch import inspect import os @@ -14,7 +10,6 @@ from textwrap import dedent import py -import six import _pytest from _pytest import deprecated @@ -22,7 +17,6 @@ from _pytest import nodes from _pytest._code import filter_traceback from _pytest.compat import ascii_escaped -from _pytest.compat import enum from _pytest.compat import get_default_arg_names from _pytest.compat import get_real_func from _pytest.compat import getfslineno @@ -35,7 +29,6 @@ from _pytest.compat import REGEX_TYPE from _pytest.compat import safe_getattr from _pytest.compat import safe_isclass -from _pytest.compat import safe_str from _pytest.compat import STRING_TYPES from _pytest.config import hookimpl from _pytest.main import FSHookProxy @@ -55,8 +48,8 @@ def get(self): if node is not None: return node.obj - doc = "python %s object this node was collected from (can be None)." % ( - name.lower(), + doc = "python {} object this node was collected from (can be None).".format( + name.lower() ) return property(get, None, None, doc) @@ -239,7 +232,7 @@ def pytest_make_parametrize_id(config, val, argname=None): return None -class PyobjContext(object): +class PyobjContext: module = pyobj_property("Module") cls = pyobj_property("Class") instance = pyobj_property("Instance") @@ -249,7 +242,7 @@ class PyobjMixin(PyobjContext): _ALLOW_MARKERS = True def __init__(self, *k, **kw): - super(PyobjMixin, self).__init__(*k, **kw) + super().__init__(*k, **kw) @property def obj(self): @@ -421,7 +414,7 @@ def _genfunctions(self, name, funcobj): fixtureinfo.prune_dependency_tree() for callspec in metafunc._calls: - subname = "%s[%s]" % (name, callspec.id) + subname = "{}[{}]".format(name, callspec.id) yield Function( name=subname, parent=self, @@ -443,7 +436,7 @@ def collect(self): self._inject_setup_module_fixture() self._inject_setup_function_fixture() self.session._fixturemanager.parsefactories(self) - return super(Module, self).collect() + return super().collect() def _inject_setup_module_fixture(self): """Injects a hidden autouse, module scoped fixture into the collected module object @@ -531,7 +524,7 @@ def _importtestmodule(self): if exc_info.traceback else exc_info.exconly() ) - formatted_tb = safe_str(exc_repr) + formatted_tb = str(exc_repr) raise self.CollectError( "ImportError while importing test module '{fspath}'.\n" "Hint: make sure your test modules/packages have valid Python names.\n" @@ -606,11 +599,10 @@ def gethookproxy(self, fspath): return proxy def _collectfile(self, path, handle_dupes=True): - assert path.isfile(), "%r is not a file (isdir=%r, exists=%r, islink=%r)" % ( - path, - path.isdir(), - path.exists(), - path.islink(), + assert ( + path.isfile() + ), "{!r} is not a file (isdir={!r}, exists={!r}, islink={!r})".format( + path, path.isdir(), path.exists(), path.islink() ) ihook = self.gethookproxy(path) if not self.isinitpath(path): @@ -657,8 +649,7 @@ def collect(self): continue if is_file: - for x in self._collectfile(path): - yield x + yield from self._collectfile(path) elif not path.isdir(): # Broken symlink or invalid/missing file. continue @@ -800,7 +791,7 @@ def _getobj(self): def collect(self): self.session._fixturemanager.parsefactories(self) - return super(Instance, self).collect() + return super().collect() def newinstance(self): self.obj = self._getobj() @@ -858,7 +849,7 @@ def hasnew(obj): return new != object.__new__ -class CallSpec2(object): +class CallSpec2: def __init__(self, metafunc): self.metafunc = metafunc self.funcargs = {} @@ -884,7 +875,7 @@ def copy(self): def _checkargnotcontained(self, arg): if arg in self.params or arg in self.funcargs: - raise ValueError("duplicate %r" % (arg,)) + raise ValueError("duplicate {!r}".format(arg)) def getparam(self, name): try: @@ -1060,7 +1051,7 @@ def _resolve_arg_ids(self, argnames, ids, parameters, item): msg = "In {}: {} parameter sets specified, with different number of ids: {}" fail(msg.format(func_name, len(parameters), len(ids)), pytrace=False) for id_value in ids: - if id_value is not None and not isinstance(id_value, six.string_types): + if id_value is not None and not isinstance(id_value, str): msg = "In {}: ids must be list of strings, found: {} (type: {!r})" fail( msg.format(func_name, saferepr(id_value), type(id_value)), @@ -1183,7 +1174,7 @@ def _idval(val, argname, idx, idfn, item, config): msg = msg.format(item.nodeid, argname, idx) # we only append the exception type and message because on Python 2 reraise does nothing msg += " {}: {}\n".format(type(e).__name__, e) - six.raise_from(ValueError(msg), e) + raise ValueError(msg) from e elif config: hook_id = config.hook.pytest_make_parametrize_id( config=config, val=val, argname=argname @@ -1335,7 +1326,7 @@ def _showfixtures_main(config, session): if currentmodule != module: if not module.startswith("_pytest."): tw.line() - tw.sep("-", "fixtures defined from %s" % (module,)) + tw.sep("-", "fixtures defined from {}".format(module)) currentmodule = module if verbose <= 0 and argname[0] == "_": continue @@ -1350,7 +1341,7 @@ def _showfixtures_main(config, session): if doc: write_docstring(tw, doc) else: - tw.line(" %s: no docstring available" % (loc,), red=True) + tw.line(" {}: no docstring available".format(loc), red=True) tw.line() @@ -1390,7 +1381,7 @@ def __init__( fixtureinfo=None, originalname=None, ): - super(Function, self).__init__(name, parent, config=config, session=session) + super().__init__(name, parent, config=config, session=session) self._args = args if callobj is not NOTSET: self.obj = callobj @@ -1464,7 +1455,7 @@ def runtest(self): self.ihook.pytest_pyfunc_call(pyfuncitem=self) def setup(self): - super(Function, self).setup() + super().setup() fixtures.fillfixtures(self) diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index df09aa32d8f..011181a4071 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -1,23 +1,19 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import - import math import pprint import sys import warnings +from collections.abc import Iterable +from collections.abc import Mapping +from collections.abc import Sized from decimal import Decimal +from itertools import filterfalse from numbers import Number from more_itertools.more import always_iterable -from six.moves import filterfalse -from six.moves import zip import _pytest._code from _pytest import deprecated from _pytest.compat import isclass -from _pytest.compat import Iterable -from _pytest.compat import Mapping -from _pytest.compat import Sized from _pytest.compat import STRING_TYPES from _pytest.outcomes import fail @@ -50,7 +46,7 @@ def _non_numeric_type_error(value, at): # builtin pytest.approx helper -class ApproxBase(object): +class ApproxBase: """ Provide shared utilities for making approximate comparisons between numbers or sequences of numbers. @@ -81,9 +77,6 @@ def __eq__(self, actual): def __ne__(self, actual): return not (actual == self) - if sys.version_info[0] == 2: - __cmp__ = _cmp_raises_type_error - def _approx_scalar(self, x): return ApproxScalar(x, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok) @@ -122,9 +115,6 @@ def __repr__(self): list_scalars = _recursive_list_map(self._approx_scalar, self.expected.tolist()) return "approx({!r})".format(list_scalars) - if sys.version_info[0] == 2: - __cmp__ = _cmp_raises_type_error - def __eq__(self, actual): import numpy as np @@ -251,10 +241,7 @@ def __repr__(self): except ValueError: vetted_tolerance = "???" - if sys.version_info[0] == 2: - return "{} +- {}".format(self.expected, vetted_tolerance) - else: - return u"{} \u00b1 {}".format(self.expected, vetted_tolerance) + return "{} \u00b1 {}".format(self.expected, vetted_tolerance) def __eq__(self, actual): """ @@ -719,7 +706,7 @@ def raises(expected_exception, *args, **kwargs): raises.Exception = fail.Exception -class RaisesContext(object): +class RaisesContext: def __init__(self, expected_exception, message, match_expr): self.expected_exception = expected_exception self.message = message @@ -736,8 +723,6 @@ def __exit__(self, *tp): fail(self.message) self.excinfo.__init__(tp) suppress_exception = issubclass(self.excinfo.type, self.expected_exception) - if sys.version_info[0] == 2 and suppress_exception: - sys.exc_clear() if self.match_expr is not None and suppress_exception: self.excinfo.match(self.match_expr) return suppress_exception diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py index 574c6a1cced..006d97e7fc7 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -1,16 +1,9 @@ -# -*- coding: utf-8 -*- """ recording warnings during test function execution. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import inspect import re import sys import warnings -import six - import _pytest._code from _pytest.deprecated import PYTEST_WARNS_UNKNOWN_KWARGS from _pytest.deprecated import WARNS_EXEC @@ -117,7 +110,7 @@ class WarningsRecorder(warnings.catch_warnings): """ def __init__(self): - super(WarningsRecorder, self).__init__(record=True) + super().__init__(record=True) self._entered = False self._list = [] @@ -154,47 +147,16 @@ def __enter__(self): if self._entered: __tracebackhide__ = True raise RuntimeError("Cannot enter %r twice" % self) - self._list = super(WarningsRecorder, self).__enter__() + self._list = super().__enter__() warnings.simplefilter("always") - # python3 keeps track of a "filter version", when the filters are - # updated previously seen warnings can be re-warned. python2 has no - # concept of this so we must reset the warnings registry manually. - # trivial patching of `warnings.warn` seems to be enough somehow? - if six.PY2: - - def warn(message, category=None, stacklevel=1): - # duplicate the stdlib logic due to - # bad handing in the c version of warnings - if isinstance(message, Warning): - category = message.__class__ - # Check category argument - if category is None: - category = UserWarning - assert issubclass(category, Warning) - - # emulate resetting the warn registry - f_globals = sys._getframe(stacklevel).f_globals - if "__warningregistry__" in f_globals: - orig = f_globals["__warningregistry__"] - f_globals["__warningregistry__"] = None - try: - return self._saved_warn(message, category, stacklevel + 1) - finally: - f_globals["__warningregistry__"] = orig - else: - return self._saved_warn(message, category, stacklevel + 1) - - warnings.warn, self._saved_warn = warn, warnings.warn return self def __exit__(self, *exc_info): if not self._entered: __tracebackhide__ = True raise RuntimeError("Cannot exit %r without entering first" % self) - # see above where `self._saved_warn` is assigned - if six.PY2: - warnings.warn = self._saved_warn - super(WarningsRecorder, self).__exit__(*exc_info) + + super().__exit__(*exc_info) # Built-in catch_warnings does not reset entered state so we do it # manually here for this context manager to become reusable. @@ -203,7 +165,7 @@ def __exit__(self, *exc_info): class WarningsChecker(WarningsRecorder): def __init__(self, expected_warning=None, match_expr=None): - super(WarningsChecker, self).__init__() + super().__init__() msg = "exceptions must be old-style classes or derived from Warning, not %s" if isinstance(expected_warning, tuple): @@ -219,7 +181,7 @@ def __init__(self, expected_warning=None, match_expr=None): self.match_expr = match_expr def __exit__(self, *exc_info): - super(WarningsChecker, self).__exit__(*exc_info) + super().__exit__(*exc_info) __tracebackhide__ = True diff --git a/src/_pytest/reports.py b/src/_pytest/reports.py index 0bba6762c33..d2f1f33e2fe 100644 --- a/src/_pytest/reports.py +++ b/src/_pytest/reports.py @@ -1,8 +1,6 @@ -# -*- coding: utf-8 -*- from pprint import pprint import py -import six from _pytest._code.code import ExceptionInfo from _pytest._code.code import ReprEntry @@ -23,16 +21,13 @@ def getslaveinfoline(node): except AttributeError: d = node.slaveinfo ver = "%s.%s.%s" % d["version_info"][:3] - node._slaveinfocache = s = "[%s] %s -- Python %s %s" % ( - d["id"], - d["sysplatform"], - ver, - d["executable"], + node._slaveinfocache = s = "[{}] {} -- Python {} {}".format( + d["id"], d["sysplatform"], ver, d["executable"] ) return s -class BaseReport(object): +class BaseReport: when = None location = None @@ -195,7 +190,7 @@ def disassembled_report(rep): ): d["longrepr"] = disassembled_report(self) else: - d["longrepr"] = six.text_type(self.longrepr) + d["longrepr"] = str(self.longrepr) else: d["longrepr"] = self.longrepr for name in d: @@ -335,11 +330,8 @@ def __init__( self.__dict__.update(extra) def __repr__(self): - return "<%s %r when=%r outcome=%r>" % ( - self.__class__.__name__, - self.nodeid, - self.when, - self.outcome, + return "<{} {!r} when={!r} outcome={!r}>".format( + self.__class__.__name__, self.nodeid, self.when, self.outcome ) @classmethod @@ -372,7 +364,7 @@ def from_item_and_call(cls, item, call): excinfo, style=item.config.getoption("tbstyle", "auto") ) for rwhen, key, content in item._report_sections: - sections.append(("Captured %s %s" % (key, rwhen), content)) + sections.append(("Captured {} {}".format(key, rwhen), content)) return cls( item.nodeid, item.location, @@ -402,10 +394,8 @@ def location(self): return (self.fspath, None, self.fspath) def __repr__(self): - return "" % ( - self.nodeid, - len(self.result), - self.outcome, + return "".format( + self.nodeid, len(self.result), self.outcome ) diff --git a/src/_pytest/resultlog.py b/src/_pytest/resultlog.py index bd30b5071e9..a977b29da43 100644 --- a/src/_pytest/resultlog.py +++ b/src/_pytest/resultlog.py @@ -1,11 +1,6 @@ -# -*- coding: utf-8 -*- """ log machine-parseable test session result information in a plain text file. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import os import py @@ -48,13 +43,13 @@ def pytest_unconfigure(config): config.pluginmanager.unregister(resultlog) -class ResultLog(object): +class ResultLog: def __init__(self, config, logfile): self.config = config self.logfile = logfile # preferably line buffered def write_log_entry(self, testpath, lettercode, longrepr): - print("%s %s" % (lettercode, testpath), file=self.logfile) + print("{} {}".format(lettercode, testpath), file=self.logfile) for line in longrepr.splitlines(): print(" %s" % line, file=self.logfile) diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index d51e859f1d1..9c91a49a5e9 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -1,16 +1,10 @@ -# -*- coding: utf-8 -*- """ basic collect and runtest protocol implementations """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import bdb import os import sys from time import time import attr -import six from .reports import CollectErrorRepr from .reports import CollectReport @@ -62,7 +56,7 @@ def pytest_terminal_summary(terminalreporter): tr.write_line("") tr.write_line("(0.00 durations hidden. Use -vv to show these durations.)") break - tr.write_line("%02.2fs %-8s %s" % (rep.duration, rep.when, rep.nodeid)) + tr.write_line("{:02.2f}s {:<8} {}".format(rep.duration, rep.when, rep.nodeid)) def pytest_sessionstart(session): @@ -200,7 +194,7 @@ def call_runtest_hook(item, when, **kwds): @attr.s(repr=False) -class CallInfo(object): +class CallInfo: """ Result/Exception info a function invocation. """ _result = attr.ib() @@ -275,7 +269,7 @@ def pytest_make_collect_report(collector): return rep -class SetupState(object): +class SetupState: """ shared state for setting up/tearing down test items or collectors. """ def __init__(self): @@ -309,7 +303,8 @@ def _callfinalizers(self, colitem): if exc is None: exc = sys.exc_info() if exc: - six.reraise(*exc) + _, val, tb = exc + raise val.with_traceback(tb) def _teardown_with_finalization(self, colitem): self._callfinalizers(colitem) @@ -344,7 +339,8 @@ def _teardown_towards(self, needed_collectors): if exc is None: exc = sys.exc_info() if exc: - six.reraise(*exc) + _, val, tb = exc + raise val.with_traceback(tb) def prepare(self, colitem): """ setup objects along the collector chain to the test-method @@ -355,7 +351,8 @@ def prepare(self, colitem): # check if the last collection node has raised an error for col in self.stack: if hasattr(col, "_prepare_exc"): - six.reraise(*col._prepare_exc) + _, val, tb = col._prepare_exc + raise val.with_traceback(tb) for col in needed_collectors[len(self.stack) :]: self.stack.append(col) try: diff --git a/src/_pytest/setuponly.py b/src/_pytest/setuponly.py index 0859011241a..70d6ed12f86 100644 --- a/src/_pytest/setuponly.py +++ b/src/_pytest/setuponly.py @@ -1,8 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import sys import pytest diff --git a/src/_pytest/setupplan.py b/src/_pytest/setupplan.py index 47b0fe82ef6..697746f205a 100644 --- a/src/_pytest/setupplan.py +++ b/src/_pytest/setupplan.py @@ -1,8 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import pytest diff --git a/src/_pytest/skipping.py b/src/_pytest/skipping.py index bc8b88e7178..53737816f5c 100644 --- a/src/_pytest/skipping.py +++ b/src/_pytest/skipping.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- """ support for skip/xfail functions and markers. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - from _pytest.config import hookimpl from _pytest.mark.evaluate import MarkEvaluator from _pytest.outcomes import fail @@ -129,17 +124,13 @@ def pytest_runtest_makereport(item, call): evalxfail = getattr(item, "_evalxfail", None) # unitttest special case, see setting of _unexpectedsuccess if hasattr(item, "_unexpectedsuccess") and rep.when == "call": - from _pytest.compat import _is_unittest_unexpected_success_a_failure if item._unexpectedsuccess: rep.longrepr = "Unexpected success: {}".format(item._unexpectedsuccess) else: rep.longrepr = "Unexpected success" - if _is_unittest_unexpected_success_a_failure(): - rep.outcome = "failed" - else: - rep.outcome = "passed" - rep.wasxfail = rep.longrepr + rep.outcome = "failed" + elif item.config.option.runxfail: pass # don't interefere elif call.excinfo and call.excinfo.errisinstance(xfail.Exception): diff --git a/src/_pytest/stepwise.py b/src/_pytest/stepwise.py index 0427cd0ea45..68e53a31cb4 100644 --- a/src/_pytest/stepwise.py +++ b/src/_pytest/stepwise.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index eb1970d5103..6f989030175 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -1,12 +1,7 @@ -# -*- coding: utf-8 -*- """ terminal reporting of the full testing process. This is a good source for looking at the various reporting hooks. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import argparse import collections import platform @@ -17,7 +12,6 @@ import attr import pluggy import py -import six from more_itertools import collapse import pytest @@ -40,7 +34,7 @@ class MoreQuietAction(argparse.Action): """ def __init__(self, option_strings, dest, default=None, required=False, help=None): - super(MoreQuietAction, self).__init__( + super().__init__( option_strings=option_strings, dest=dest, nargs=0, @@ -191,7 +185,7 @@ def pytest_report_teststatus(report): @attr.s -class WarningReport(object): +class WarningReport: """ Simple structure to hold warnings information captured by ``pytest_warning_captured``. @@ -219,13 +213,13 @@ def get_location(self, config): relpath = py.path.local(filename).relto(config.invocation_dir) if not relpath: relpath = str(filename) - return "%s:%s" % (relpath, linenum) + return "{}:{}".format(relpath, linenum) else: return str(self.fslocation) return None -class TerminalReporter(object): +class TerminalReporter: def __init__(self, config, file=None): import _pytest.config @@ -320,8 +314,8 @@ def write(self, content, **markup): self._tw.write(content, **markup) def write_line(self, line, **markup): - if not isinstance(line, six.text_type): - line = six.text_type(line, errors="replace") + if not isinstance(line, str): + line = str(line, errors="replace") self.ensure_newline() self._tw.line(line, **markup) @@ -354,7 +348,7 @@ def line(self, msg, **kw): self._tw.line(msg, **kw) def pytest_internalerror(self, excrepr): - for line in six.text_type(excrepr).split("\n"): + for line in str(excrepr).split("\n"): self.write_line("INTERNALERROR> " + line) return 1 @@ -374,7 +368,7 @@ def pytest_warning_captured(self, warning_message, item): def pytest_plugin_registered(self, plugin): if self.config.option.traceconfig: - msg = "PLUGIN registered: %s" % (plugin,) + msg = "PLUGIN registered: {}".format(plugin) # XXX this event may happen during setup/teardown time # which unfortunately captures our output here # which garbles our output if we use self.write_line @@ -561,14 +555,12 @@ def pytest_sessionstart(self, session): return self.write_sep("=", "test session starts", bold=True) verinfo = platform.python_version() - msg = "platform %s -- Python %s" % (sys.platform, verinfo) + msg = "platform {} -- Python {}".format(sys.platform, verinfo) if hasattr(sys, "pypy_version_info"): verinfo = ".".join(map(str, sys.pypy_version_info[:3])) - msg += "[pypy-%s-%s]" % (verinfo, sys.pypy_version_info[3]) - msg += ", pytest-%s, py-%s, pluggy-%s" % ( - pytest.__version__, - py.__version__, - pluggy.__version__, + msg += "[pypy-{}-{}]".format(verinfo, sys.pypy_version_info[3]) + msg += ", pytest-{}, py-{}, pluggy-{}".format( + pytest.__version__, py.__version__, pluggy.__version__ ) if ( self.verbosity > 0 @@ -650,11 +642,11 @@ def _printcollecteditems(self, items): if col.name == "()": # Skip Instances. continue indent = (len(stack) - 1) * " " - self._tw.line("%s%s" % (indent, col)) + self._tw.line("{}{}".format(indent, col)) if self.config.option.verbose >= 1: if hasattr(col, "_obj") and col._obj.__doc__: for line in col._obj.__doc__.strip().splitlines(): - self._tw.line("%s%s" % (indent + " ", line.strip())) + self._tw.line("{}{}".format(indent + " ", line.strip())) @pytest.hookimpl(hookwrapper=True) def pytest_sessionfinish(self, exitstatus): @@ -854,7 +846,7 @@ def summary_errors(self): if rep.when == "collect": msg = "ERROR collecting " + msg else: - msg = "ERROR at %s of %s" % (rep.when, msg) + msg = "ERROR at {} of {}".format(rep.when, msg) self.write_sep("_", msg, red=True, bold=True) self._outrep_summary(rep) @@ -874,7 +866,7 @@ def _outrep_summary(self, rep): def summary_stats(self): session_duration = time.time() - self._sessionstarttime (line, color) = build_summary_stats_line(self.stats) - msg = "%s in %.2f seconds" % (line, session_duration) + msg = "{} in {:.2f} seconds".format(line, session_duration) markup = {color: True, "bold": True} if self.verbosity >= 0: @@ -901,7 +893,7 @@ def show_xfailed(lines): for rep in xfailed: verbose_word = rep._get_verbose_word(self.config) pos = _get_pos(self.config, rep) - lines.append("%s %s" % (verbose_word, pos)) + lines.append("{} {}".format(verbose_word, pos)) reason = rep.wasxfail if reason: lines.append(" " + str(reason)) @@ -912,7 +904,7 @@ def show_xpassed(lines): verbose_word = rep._get_verbose_word(self.config) pos = _get_pos(self.config, rep) reason = rep.wasxfail - lines.append("%s %s %s" % (verbose_word, pos, reason)) + lines.append("{} {} {}".format(verbose_word, pos, reason)) def show_skipped(lines): skipped = self.stats.get("skipped", []) @@ -966,7 +958,7 @@ def _get_line_with_reprcrash_message(config, rep, termwidth): verbose_word = rep._get_verbose_word(config) pos = _get_pos(config, rep) - line = "%s %s" % (verbose_word, pos) + line = "{} {}".format(verbose_word, pos) len_line = wcswidth(line) ellipsis, len_ellipsis = "...", 3 if len_line > termwidth - len_ellipsis: @@ -992,21 +984,6 @@ def _get_line_with_reprcrash_message(config, rep, termwidth): msg = msg[:max_len_msg] while wcswidth(msg) > max_len_msg: msg = msg[:-1] - if six.PY2: - # on python 2 systems with narrow unicode compilation, trying to - # get a single character out of a multi-byte unicode character such as - # u'😄' will result in a High Surrogate (U+D83D) character, which is - # rendered as u'�'; in this case we just strip that character out as it - # serves no purpose being rendered - try: - surrogate = six.unichr(0xD83D) - msg = msg.rstrip(surrogate) - except ValueError: # pragma: no cover - # Jython cannot represent this lone surrogate at all (#5256): - # ValueError: unichr() arg is a lone surrogate in range - # (0xD800, 0xDFFF) (Jython UTF-16 encoding) - # ignore this case as it shouldn't appear in the string anyway - pass msg += ellipsis line += sep + msg return line diff --git a/src/_pytest/tmpdir.py b/src/_pytest/tmpdir.py index a8a7037713c..f2c4d905cf5 100644 --- a/src/_pytest/tmpdir.py +++ b/src/_pytest/tmpdir.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- """ support for providing temporary directories to test functions. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import os import re import tempfile @@ -11,7 +6,6 @@ import attr import py -import six import pytest from .pathlib import ensure_reset_dir @@ -23,7 +17,7 @@ @attr.s -class TempPathFactory(object): +class TempPathFactory: """Factory for temporary directories under the common base temp directory. The base directory can be configured using the ``--basetemp`` option.""" @@ -32,9 +26,7 @@ class TempPathFactory(object): # using os.path.abspath() to get absolute path instead of resolve() as it # does not work the same in all platforms (see #4427) # Path.absolute() exists, but it is not public (see https://bugs.python.org/issue25012) - converter=attr.converters.optional( - lambda p: Path(os.path.abspath(six.text_type(p))) - ) + converter=attr.converters.optional(lambda p: Path(os.path.abspath(str(p)))) ) _trace = attr.ib() _basetemp = attr.ib(default=None) @@ -85,7 +77,7 @@ def getbasetemp(self): @attr.s -class TempdirFactory(object): +class TempdirFactory: """ backward comptibility wrapper that implements :class:``py.path.local`` for :class:``TempPathFactory`` diff --git a/src/_pytest/unittest.py b/src/_pytest/unittest.py index e2e7efdc539..da45e312f3a 100644 --- a/src/_pytest/unittest.py +++ b/src/_pytest/unittest.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- """ discovery and running of std-library "unittest" style tests. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import sys import traceback @@ -113,23 +108,9 @@ class TestCaseFunction(Function): def setup(self): self._testcase = self.parent.obj(self.name) - self._fix_unittest_skip_decorator() if hasattr(self, "_request"): self._request._fillfixtures() - def _fix_unittest_skip_decorator(self): - """ - The @unittest.skip decorator calls functools.wraps(self._testcase) - The call to functools.wraps() fails unless self._testcase - has a __name__ attribute. This is usually automatically supplied - if the test is a function or method, but we need to add manually - here. - - See issue #1169 - """ - if sys.version_info[0] == 2: - setattr(self._testcase, "__name__", self.name) - def teardown(self): self._testcase = None @@ -208,12 +189,7 @@ def _handle_skip(self): skip_why = getattr( self._testcase.__class__, "__unittest_skip_why__", "" ) or getattr(testMethod, "__unittest_skip_why__", "") - try: # PY3, unittest2 on PY2 - self._testcase._addSkip(self, self._testcase, skip_why) - except TypeError: # PY2 - if sys.version_info[0] != 2: - raise - self._testcase._addSkip(self, skip_why) + self._testcase._addSkip(self, self._testcase, skip_why) return True return False diff --git a/src/_pytest/warning_types.py b/src/_pytest/warning_types.py index 861010a1275..d7d37b4bbe0 100644 --- a/src/_pytest/warning_types.py +++ b/src/_pytest/warning_types.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import attr @@ -95,7 +94,7 @@ class RemovedInPytest4Warning(PytestDeprecationWarning): @attr.s -class UnformattedWarning(object): +class UnformattedWarning: """Used to hold warnings that need to format their message at runtime, as opposed to a direct message. Using this class avoids to keep all the warning types and messages in this module, avoiding misuse. diff --git a/src/_pytest/warnings.py b/src/_pytest/warnings.py index a3debae462f..2f10862251a 100644 --- a/src/_pytest/warnings.py +++ b/src/_pytest/warnings.py @@ -1,14 +1,8 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import sys import warnings from contextlib import contextmanager import pytest -from _pytest import compat SHOW_PYTEST_WARNINGS_ARG = "-Walways::pytest.RemovedInPytest4Warning" @@ -19,7 +13,7 @@ def _setoption(wmod, arg): """ parts = arg.split(":") if len(parts) > 5: - raise wmod._OptionError("too many fields (max 5): %r" % (arg,)) + raise wmod._OptionError("too many fields (max 5): {!r}".format(arg)) while len(parts) < 5: parts.append("") action, message, category, module, lineno = [s.strip() for s in parts] @@ -31,7 +25,7 @@ def _setoption(wmod, arg): if lineno < 0: raise ValueError except (ValueError, OverflowError): - raise wmod._OptionError("invalid lineno %r" % (lineno,)) + raise wmod._OptionError("invalid lineno {!r}".format(lineno)) else: lineno = 0 wmod.filterwarnings(action, message, category, module, lineno) @@ -104,22 +98,8 @@ def catch_warnings_for_item(config, ihook, when, item): def warning_record_to_str(warning_message): - """Convert a warnings.WarningMessage to a string. - - This takes lot of unicode shenaningans into account for Python 2. - When Python 2 support is dropped this function can be greatly simplified. - """ + """Convert a warnings.WarningMessage to a string.""" warn_msg = warning_message.message - unicode_warning = False - if compat._PY2 and any(isinstance(m, compat.UNICODE_TYPES) for m in warn_msg.args): - new_args = [] - for m in warn_msg.args: - new_args.append( - compat.ascii_escaped(m) if isinstance(m, compat.UNICODE_TYPES) else m - ) - unicode_warning = list(warn_msg.args) != new_args - warn_msg.args = new_args - msg = warnings.formatwarning( warn_msg, warning_message.category, @@ -127,12 +107,6 @@ def warning_record_to_str(warning_message): warning_message.lineno, warning_message.line, ) - if unicode_warning: - warnings.warn( - "Warning is using unicode non convertible to ascii, " - "converting to a safe representation:\n {!r}".format(compat.safe_str(msg)), - UnicodeWarning, - ) return msg diff --git a/src/pytest.py b/src/pytest.py index ccc77b476f5..a6376843d24 100644 --- a/src/pytest.py +++ b/src/pytest.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # PYTHON_ARGCOMPLETE_OK """ pytest: unit and functional testing with Python. diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 7016cf13b6e..9d903f80233 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -1,8 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import os import sys import textwrap @@ -11,7 +6,6 @@ import attr import importlib_metadata import py -import six import pytest from _pytest.main import EXIT_NOTESTSCOLLECTED @@ -26,7 +20,7 @@ def prepend_pythonpath(*dirs): return os.pathsep.join(str(p) for p in dirs) -class TestGeneralUsage(object): +class TestGeneralUsage: def test_config_error(self, testdir): testdir.copy_example("conftest_usageerror/conftest.py") result = testdir.runpytest(testdir.tmpdir) @@ -120,7 +114,7 @@ def test_early_load_setuptools_name(self, testdir, monkeypatch, load_cov_early): loaded = [] @attr.s - class DummyEntryPoint(object): + class DummyEntryPoint: name = attr.ib() module = attr.ib() group = "pytest11" @@ -137,7 +131,7 @@ def load(self): ] @attr.s - class DummyDist(object): + class DummyDist: entry_points = attr.ib() files = () @@ -176,7 +170,6 @@ def test_this(): result = testdir.runpytest(p) result.stdout.fnmatch_lines( [ - # XXX on jython this fails: "> import import_fails", "ImportError while importing test module*", "*No module named *does_not_work*", ] @@ -222,9 +215,7 @@ def foo(): " foo()", "conftest.py:2: in foo", " import qwerty", - "E {}: No module named {q}qwerty{q}".format( - exc_name, q="'" if six.PY3 else "" - ), + "E {}: No module named 'qwerty'".format(exc_name), ] ) @@ -516,20 +507,19 @@ def test_stuff(r): def test_parametrized_with_null_bytes(self, testdir): """Test parametrization with values that contain null bytes and unicode characters (#2644, #2957)""" p = testdir.makepyfile( - u""" - # encoding: UTF-8 + """\ import pytest @pytest.mark.parametrize("data", [b"\\x00", "\\x00", u'ação']) def test_foo(data): assert data - """ + """ ) res = testdir.runpytest(p) res.assert_outcomes(passed=3) -class TestInvocationVariants(object): +class TestInvocationVariants: def test_earlyinit(self, testdir): p = testdir.makepyfile( """ @@ -540,7 +530,6 @@ def test_earlyinit(self, testdir): result = testdir.runpython(p) assert result.ret == 0 - @pytest.mark.xfail("sys.platform.startswith('java')") def test_pydoc(self, testdir): for name in ("py.test", "pytest"): result = testdir.runpython_c("import {};help({})".format(name, name)) @@ -627,7 +616,7 @@ def test_invoke_with_path(self, tmpdir, capsys): out, err = capsys.readouterr() def test_invoke_plugin_api(self, testdir, capsys): - class MyPlugin(object): + class MyPlugin: def pytest_addoption(self, parser): parser.addoption("--myopt") @@ -765,7 +754,7 @@ def test_cmdline_python_package_symlink(self, testdir, monkeypatch): str(testdir.tmpdir.join("tmpfile2")), ) except OSError as e: - pytest.skip(six.text_type(e.args[0])) + pytest.skip(str(e.args[0])) monkeypatch.delenv("PYTHONDONTWRITEBYTECODE", raising=False) dirname = "lib" @@ -783,10 +772,7 @@ def test_cmdline_python_package_symlink(self, testdir, monkeypatch): d_local = testdir.mkdir("local") symlink_location = os.path.join(str(d_local), "lib") - if six.PY2: - os.symlink(str(d), symlink_location) - else: - os.symlink(str(d), symlink_location, target_is_directory=True) + os.symlink(str(d), symlink_location, target_is_directory=True) # The structure of the test directory is now: # . @@ -883,7 +869,7 @@ def test_has_plugin(self, request): assert request.config.pluginmanager.hasplugin("python") -class TestDurations(object): +class TestDurations: source = """ import time frag = 0.002 @@ -961,7 +947,7 @@ def test_with_not(self, testdir): assert result.ret == 0 -class TestDurationWithFixture(object): +class TestDurationWithFixture: source = """ import pytest import time @@ -1185,9 +1171,6 @@ def test_usage_error_code(testdir): assert result.ret == EXIT_USAGEERROR -@pytest.mark.skipif( - sys.version_info[:2] < (3, 5), reason="async def syntax python 3.5+ only" -) @pytest.mark.filterwarnings("default") def test_warn_on_async_function(testdir): testdir.makepyfile( diff --git a/testing/code/test_code.py b/testing/code/test_code.py index 0b63fcf4d42..d4579596735 100644 --- a/testing/code/test_code.py +++ b/testing/code/test_code.py @@ -1,21 +1,11 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import sys +from unittest import mock -from six import text_type from test_excinfo import TWMock import _pytest._code import pytest -try: - import mock -except ImportError: - import unittest.mock as mock - def test_ne(): code1 = _pytest._code.Code(compile('foo = "bar"', "", "exec")) @@ -34,7 +24,7 @@ def test_code_gives_back_name_for_not_existing_file(): def test_code_with_class(): - class A(object): + class A: pass pytest.raises(TypeError, _pytest._code.Code, A) @@ -85,28 +75,13 @@ def test_code_from_func(): def test_unicode_handling(): - value = u"ąć".encode("UTF-8") + value = "ąć".encode() def f(): raise Exception(value) - excinfo = pytest.raises(Exception, f) - text_type(excinfo) - if sys.version_info < (3,): - bytes(excinfo) - - -@pytest.mark.skipif(sys.version_info[0] >= 3, reason="python 2 only issue") -def test_unicode_handling_syntax_error(): - value = u"ąć".encode("UTF-8") - - def f(): - raise SyntaxError("invalid syntax", (None, 1, 3, value)) - excinfo = pytest.raises(Exception, f) str(excinfo) - if sys.version_info[0] < 3: - text_type(excinfo) def test_code_getargs(): @@ -161,7 +136,7 @@ def f4(x, *y, **z): assert fr4.getargs(var=True) == [("x", "a"), ("y", ("b",)), ("z", {"c": "d"})] -class TestExceptionInfo(object): +class TestExceptionInfo: def test_bad_getsource(self): try: if False: @@ -177,7 +152,7 @@ def test_from_current_with_missing(self): _pytest._code.ExceptionInfo.from_current() -class TestTracebackEntry(object): +class TestTracebackEntry: def test_getsource(self): try: if False: @@ -192,20 +167,18 @@ def test_getsource(self): assert "assert False" in source[5] -class TestReprFuncArgs(object): +class TestReprFuncArgs: def test_not_raise_exception_with_mixed_encoding(self): from _pytest._code.code import ReprFuncArgs tw = TWMock() - args = [("unicode_string", u"São Paulo"), ("utf8_string", b"S\xc3\xa3o Paulo")] + args = [("unicode_string", "São Paulo"), ("utf8_string", b"S\xc3\xa3o Paulo")] r = ReprFuncArgs(args) r.toterminal(tw) - if sys.version_info[0] >= 3: - assert ( - tw.lines[0] - == r"unicode_string = São Paulo, utf8_string = b'S\xc3\xa3o Paulo'" - ) - else: - assert tw.lines[0] == "unicode_string = São Paulo, utf8_string = São Paulo" + + assert ( + tw.lines[0] + == r"unicode_string = São Paulo, utf8_string = b'S\xc3\xa3o Paulo'" + ) diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index a76797301dd..3eac94a287e 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -1,23 +1,17 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import operator import os +import queue import sys import textwrap import py -import six -from six.moves import queue import _pytest import pytest from _pytest._code.code import ExceptionChainRepr from _pytest._code.code import ExceptionInfo from _pytest._code.code import FormattedExcinfo -from _pytest._code.code import ReprExceptionInfo + try: import importlib @@ -26,8 +20,6 @@ else: invalidate_import_caches = getattr(importlib, "invalidate_caches", None) -failsonjython = pytest.mark.xfail("sys.platform.startswith('java')") - pytest_version_info = tuple(map(int, pytest.__version__.split(".")[:3])) @@ -39,7 +31,7 @@ def limited_recursion_depth(): sys.setrecursionlimit(before) -class TWMock(object): +class TWMock: WRITE = object() def __init__(self): @@ -120,7 +112,7 @@ def h(): # -class TestTraceback_f_g_h(object): +class TestTraceback_f_g_h: def setup_method(self, method): try: h() @@ -146,7 +138,6 @@ def test_traceback_entry_getsource(self): assert s.startswith("def f():") assert s.endswith("raise ValueError") - @failsonjython def test_traceback_entry_getsource_in_construct(self): source = _pytest._code.Source( """\ @@ -260,7 +251,7 @@ def reraise_me(): import sys exc, val, tb = sys.exc_info() - six.reraise(exc, val, tb) + raise val.with_traceback(tb) def f(n): try: @@ -437,7 +428,7 @@ def test_division_zero(): result.stdout.fnmatch_lines(["*AssertionError*Pattern*[123]*not found*"]) -class TestFormattedExcinfo(object): +class TestFormattedExcinfo: @pytest.fixture def importasmod(self, request, _sys_snapshot): def importasmod(source): @@ -500,8 +491,7 @@ def test_repr_source_not_existing(self): excinfo = _pytest._code.ExceptionInfo.from_current() repr = pr.repr_excinfo(excinfo) assert repr.reprtraceback.reprentries[1].lines[0] == "> ???" - if sys.version_info[0] >= 3: - assert repr.chain[0][0].reprentries[1].lines[0] == "> ???" + assert repr.chain[0][0].reprentries[1].lines[0] == "> ???" def test_repr_many_line_source_not_existing(self): pr = FormattedExcinfo() @@ -519,14 +509,13 @@ def test_repr_many_line_source_not_existing(self): excinfo = _pytest._code.ExceptionInfo.from_current() repr = pr.repr_excinfo(excinfo) assert repr.reprtraceback.reprentries[1].lines[0] == "> ???" - if sys.version_info[0] >= 3: - assert repr.chain[0][0].reprentries[1].lines[0] == "> ???" + assert repr.chain[0][0].reprentries[1].lines[0] == "> ???" def test_repr_source_failing_fullsource(self): pr = FormattedExcinfo() - class FakeCode(object): - class raw(object): + class FakeCode: + class raw: co_filename = "?" path = "?" @@ -537,7 +526,7 @@ def fullsource(self): fullsource = property(fullsource) - class FakeFrame(object): + class FakeFrame: code = FakeCode() f_locals = {} f_globals = {} @@ -568,7 +557,7 @@ def errisinstance(self, cls): excinfo = FakeExcinfo() - class FakeRawTB(object): + class FakeRawTB: tb_next = None tb = FakeRawTB() @@ -577,14 +566,12 @@ class FakeRawTB(object): fail = IOError() repr = pr.repr_excinfo(excinfo) assert repr.reprtraceback.reprentries[0].lines[0] == "> ???" - if sys.version_info[0] >= 3: - assert repr.chain[0][0].reprentries[0].lines[0] == "> ???" + assert repr.chain[0][0].reprentries[0].lines[0] == "> ???" fail = py.error.ENOENT # noqa repr = pr.repr_excinfo(excinfo) assert repr.reprtraceback.reprentries[0].lines[0] == "> ???" - if sys.version_info[0] >= 3: - assert repr.chain[0][0].reprentries[0].lines[0] == "> ???" + assert repr.chain[0][0].reprentries[0].lines[0] == "> ???" def test_repr_local(self): p = FormattedExcinfo(showlocals=True) @@ -828,9 +815,9 @@ def entry(): repr = p.repr_excinfo(excinfo) assert repr.reprtraceback assert len(repr.reprtraceback.reprentries) == len(reprtb.reprentries) - if sys.version_info[0] >= 3: - assert repr.chain[0][0] - assert len(repr.chain[0][0].reprentries) == len(reprtb.reprentries) + + assert repr.chain[0][0] + assert len(repr.chain[0][0].reprentries) == len(reprtb.reprentries) assert repr.reprcrash.path.endswith("mod.py") assert repr.reprcrash.message == "ValueError: 0" @@ -916,23 +903,21 @@ def entry(): for style in ("short", "long", "no"): for showlocals in (True, False): repr = excinfo.getrepr(style=style, showlocals=showlocals) - if sys.version_info[0] < 3: - assert isinstance(repr, ReprExceptionInfo) assert repr.reprtraceback.style == style - if sys.version_info[0] >= 3: - assert isinstance(repr, ExceptionChainRepr) - for repr in repr.chain: - assert repr[0].style == style + + assert isinstance(repr, ExceptionChainRepr) + for repr in repr.chain: + assert repr[0].style == style def test_reprexcinfo_unicode(self): from _pytest._code.code import TerminalRepr class MyRepr(TerminalRepr): def toterminal(self, tw): - tw.line(u"я") + tw.line("я") - x = six.text_type(MyRepr()) - assert x == u"я" + x = str(MyRepr()) + assert x == "я" def test_toterminal_long(self, importasmod): mod = importasmod( @@ -1133,7 +1118,6 @@ def i(): msg.endswith("mod.py") assert tw.lines[20] == ":9: ValueError" - @pytest.mark.skipif("sys.version_info[0] < 3") def test_exc_chain_repr(self, importasmod): mod = importasmod( """ @@ -1219,7 +1203,6 @@ def h(): assert line.endswith("mod.py") assert tw.lines[47] == ":15: AttributeError" - @pytest.mark.skipif("sys.version_info[0] < 3") @pytest.mark.parametrize("mode", ["from_none", "explicit_suppress"]) def test_exc_repr_chain_suppression(self, importasmod, mode): """Check that exc repr does not show chained exceptions in Python 3. @@ -1261,7 +1244,6 @@ def g(): assert tw.lines[9] == ":6: AttributeError" assert len(tw.lines) == 10 - @pytest.mark.skipif("sys.version_info[0] < 3") @pytest.mark.parametrize( "reason, description", [ @@ -1321,7 +1303,6 @@ def g(): ] ) - @pytest.mark.skipif("sys.version_info[0] < 3") def test_exc_chain_repr_cycle(self, importasmod): mod = importasmod( """ @@ -1369,7 +1350,7 @@ def unreraise(): @pytest.mark.parametrize("style", ["short", "long"]) @pytest.mark.parametrize("encoding", [None, "utf8", "utf16"]) def test_repr_traceback_with_unicode(style, encoding): - msg = u"☹" + msg = "☹" if encoding is not None: msg = msg.encode(encoding) try: @@ -1403,7 +1384,7 @@ def test_exception_repr_extraction_error_on_recursion(): """ from _pytest.pytester import LineMatcher - class numpy_like(object): + class numpy_like: def __eq__(self, other): if type(other) is numpy_like: raise ValueError( @@ -1437,7 +1418,7 @@ def test_no_recursion_index_on_recursion_error(): during a recursion error (#2486). """ - class RecursionDepthError(object): + class RecursionDepthError: def __getattr__(self, attr): return getattr(self, "_" + attr) diff --git a/testing/code/test_source.py b/testing/code/test_source.py index 0a2a98c0202..a12a102a057 100644 --- a/testing/code/test_source.py +++ b/testing/code/test_source.py @@ -1,23 +1,14 @@ -# -*- coding: utf-8 -*- # flake8: noqa # disable flake check on this file because some constructs are strange # or redundant on purpose and can't be disable on a line-by-line basis -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import ast import inspect import sys -import six - import _pytest._code import pytest from _pytest._code import Source -failsonjython = pytest.mark.xfail("sys.platform.startswith('java')") - def test_source_str_function(): x = Source("3") @@ -35,11 +26,11 @@ def test_source_str_function(): def test_unicode(): - x = Source(u"4") + x = Source("4") assert str(x) == "4" - co = _pytest._code.compile(u'u"å"', mode="eval") + co = _pytest._code.compile('u"å"', mode="eval") val = eval(co) - assert isinstance(val, six.text_type) + assert isinstance(val, str) def test_source_from_function(): @@ -48,7 +39,7 @@ def test_source_from_function(): def test_source_from_method(): - class TestClass(object): + class TestClass: def test_method(self): pass @@ -122,7 +113,7 @@ def test_source_strip_multiline(): def test_syntaxerror_rerepresentation(): ex = pytest.raises(SyntaxError, _pytest._code.compile, "xyz xyz") assert ex.value.lineno == 1 - assert ex.value.offset in (4, 5, 7) # XXX pypy/jython versus cpython? + assert ex.value.offset in {5, 7} # cpython: 7, pypy3.6 7.1.1: 5 assert ex.value.text.strip(), "x x" @@ -135,7 +126,7 @@ def test_isparseable(): assert not Source(chr(0)).isparseable() -class TestAccesses(object): +class TestAccesses: source = Source( """\ def f(x): @@ -163,7 +154,7 @@ def test_iter(self): assert len(values) == 4 -class TestSourceParsingAndCompiling(object): +class TestSourceParsingAndCompiling: source = Source( """\ def f(x): @@ -340,7 +331,7 @@ def test_offsetless_synerr(self): def test_getstartingblock_singleline(): - class A(object): + class A: def __init__(self, *args): frame = sys._getframe(1) self.source = _pytest._code.Frame(frame).statement @@ -487,7 +478,7 @@ def f(x): assert fspath.basename == "test_source.py" assert lineno == _pytest._code.getrawcode(f).co_firstlineno - 1 # see findsource - class A(object): + class A: pass fspath, lineno = getfslineno(A) @@ -498,7 +489,7 @@ class A(object): assert getfslineno(3) == ("", -1) - class B(object): + class B: pass B.__name__ = "B2" @@ -506,19 +497,19 @@ class B(object): def test_code_of_object_instance_with_call(): - class A(object): + class A: pass pytest.raises(TypeError, lambda: _pytest._code.Source(A())) - class WithCall(object): + class WithCall: def __call__(self): pass code = _pytest._code.Code(WithCall()) assert "pass" in str(code.source()) - class Hello(object): + class Hello: def __call__(self): pass @@ -627,7 +618,7 @@ def test_multiline(): assert str(source) == "raise ValueError(\n 23\n)" -class TestTry(object): +class TestTry: source = """\ try: raise ValueError @@ -654,7 +645,7 @@ def test_else(self): assert str(source) == " raise KeyError()" -class TestTryFinally(object): +class TestTryFinally: source = """\ try: raise ValueError @@ -671,7 +662,7 @@ def test_finally(self): assert str(source) == " raise IndexError(1)" -class TestIf(object): +class TestIf: source = """\ if 1: y = 3 @@ -727,7 +718,7 @@ def XXX_test_expression_multiline(): def test_getstartingblock_multiline(): - class A(object): + class A: def __init__(self, *args): frame = sys._getframe(1) self.source = _pytest._code.Frame(frame).statement diff --git a/testing/conftest.py b/testing/conftest.py index 6e01d710d11..35d5f9661b4 100644 --- a/testing/conftest.py +++ b/testing/conftest.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index a072f6b210c..f64db798b2c 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -1,8 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import os import pytest diff --git a/testing/example_scripts/acceptance/fixture_mock_integration.py b/testing/example_scripts/acceptance/fixture_mock_integration.py index c8ccdf1b488..5b00ac90e1b 100644 --- a/testing/example_scripts/acceptance/fixture_mock_integration.py +++ b/testing/example_scripts/acceptance/fixture_mock_integration.py @@ -1,10 +1,5 @@ -# -*- coding: utf-8 -*- """Reproduces issue #3774""" - -try: - import mock -except ImportError: - import unittest.mock as mock +from unittest import mock import pytest diff --git a/testing/example_scripts/collect/collect_init_tests/tests/__init__.py b/testing/example_scripts/collect/collect_init_tests/tests/__init__.py index 6211055718f..9cd366295e7 100644 --- a/testing/example_scripts/collect/collect_init_tests/tests/__init__.py +++ b/testing/example_scripts/collect/collect_init_tests/tests/__init__.py @@ -1,3 +1,2 @@ -# -*- coding: utf-8 -*- def test_init(): pass diff --git a/testing/example_scripts/collect/collect_init_tests/tests/test_foo.py b/testing/example_scripts/collect/collect_init_tests/tests/test_foo.py index 8c560fd139f..8f2d73cfa4f 100644 --- a/testing/example_scripts/collect/collect_init_tests/tests/test_foo.py +++ b/testing/example_scripts/collect/collect_init_tests/tests/test_foo.py @@ -1,3 +1,2 @@ -# -*- coding: utf-8 -*- def test_foo(): pass diff --git a/testing/example_scripts/collect/package_infinite_recursion/conftest.py b/testing/example_scripts/collect/package_infinite_recursion/conftest.py index 9c3d9a791c0..9629fa646af 100644 --- a/testing/example_scripts/collect/package_infinite_recursion/conftest.py +++ b/testing/example_scripts/collect/package_infinite_recursion/conftest.py @@ -1,3 +1,2 @@ -# -*- coding: utf-8 -*- def pytest_ignore_collect(path): return False diff --git a/testing/example_scripts/collect/package_infinite_recursion/tests/test_basic.py b/testing/example_scripts/collect/package_infinite_recursion/tests/test_basic.py index 10c5f605202..f174823854e 100644 --- a/testing/example_scripts/collect/package_infinite_recursion/tests/test_basic.py +++ b/testing/example_scripts/collect/package_infinite_recursion/tests/test_basic.py @@ -1,3 +1,2 @@ -# -*- coding: utf-8 -*- def test(): pass diff --git a/testing/example_scripts/collect/package_init_given_as_arg/pkg/test_foo.py b/testing/example_scripts/collect/package_init_given_as_arg/pkg/test_foo.py index 10c5f605202..f174823854e 100644 --- a/testing/example_scripts/collect/package_init_given_as_arg/pkg/test_foo.py +++ b/testing/example_scripts/collect/package_init_given_as_arg/pkg/test_foo.py @@ -1,3 +1,2 @@ -# -*- coding: utf-8 -*- def test(): pass diff --git a/testing/example_scripts/config/collect_pytest_prefix/conftest.py b/testing/example_scripts/config/collect_pytest_prefix/conftest.py index 13397ccc4c8..2da4ffe2fed 100644 --- a/testing/example_scripts/config/collect_pytest_prefix/conftest.py +++ b/testing/example_scripts/config/collect_pytest_prefix/conftest.py @@ -1,3 +1,2 @@ -# -*- coding: utf-8 -*- -class pytest_something(object): +class pytest_something: pass diff --git a/testing/example_scripts/config/collect_pytest_prefix/test_foo.py b/testing/example_scripts/config/collect_pytest_prefix/test_foo.py index 8c560fd139f..8f2d73cfa4f 100644 --- a/testing/example_scripts/config/collect_pytest_prefix/test_foo.py +++ b/testing/example_scripts/config/collect_pytest_prefix/test_foo.py @@ -1,3 +1,2 @@ -# -*- coding: utf-8 -*- def test_foo(): pass diff --git a/testing/example_scripts/conftest_usageerror/conftest.py b/testing/example_scripts/conftest_usageerror/conftest.py index d2f040578fa..8973e4252d3 100644 --- a/testing/example_scripts/conftest_usageerror/conftest.py +++ b/testing/example_scripts/conftest_usageerror/conftest.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- def pytest_configure(config): import pytest diff --git a/testing/example_scripts/dataclasses/test_compare_dataclasses.py b/testing/example_scripts/dataclasses/test_compare_dataclasses.py index 5381b5ed071..82a685c6314 100644 --- a/testing/example_scripts/dataclasses/test_compare_dataclasses.py +++ b/testing/example_scripts/dataclasses/test_compare_dataclasses.py @@ -1,11 +1,10 @@ -# -*- coding: utf-8 -*- from dataclasses import dataclass from dataclasses import field def test_dataclasses(): @dataclass - class SimpleDataObject(object): + class SimpleDataObject: field_a: int = field() field_b: int = field() diff --git a/testing/example_scripts/dataclasses/test_compare_dataclasses_field_comparison_off.py b/testing/example_scripts/dataclasses/test_compare_dataclasses_field_comparison_off.py index d418dd6b0cf..fa89e4a2044 100644 --- a/testing/example_scripts/dataclasses/test_compare_dataclasses_field_comparison_off.py +++ b/testing/example_scripts/dataclasses/test_compare_dataclasses_field_comparison_off.py @@ -1,11 +1,10 @@ -# -*- coding: utf-8 -*- from dataclasses import dataclass from dataclasses import field def test_dataclasses_with_attribute_comparison_off(): @dataclass - class SimpleDataObject(object): + class SimpleDataObject: field_a: int = field() field_b: int = field(compare=False) diff --git a/testing/example_scripts/dataclasses/test_compare_dataclasses_verbose.py b/testing/example_scripts/dataclasses/test_compare_dataclasses_verbose.py index a72ca5119e6..06634565b16 100644 --- a/testing/example_scripts/dataclasses/test_compare_dataclasses_verbose.py +++ b/testing/example_scripts/dataclasses/test_compare_dataclasses_verbose.py @@ -1,11 +1,10 @@ -# -*- coding: utf-8 -*- from dataclasses import dataclass from dataclasses import field def test_dataclasses_verbose(): @dataclass - class SimpleDataObject(object): + class SimpleDataObject: field_a: int = field() field_b: int = field() diff --git a/testing/example_scripts/dataclasses/test_compare_two_different_dataclasses.py b/testing/example_scripts/dataclasses/test_compare_two_different_dataclasses.py index b77b70d3c2d..4c638e1fcd6 100644 --- a/testing/example_scripts/dataclasses/test_compare_two_different_dataclasses.py +++ b/testing/example_scripts/dataclasses/test_compare_two_different_dataclasses.py @@ -1,16 +1,15 @@ -# -*- coding: utf-8 -*- from dataclasses import dataclass from dataclasses import field def test_comparing_two_different_data_classes(): @dataclass - class SimpleDataObjectOne(object): + class SimpleDataObjectOne: field_a: int = field() field_b: int = field() @dataclass - class SimpleDataObjectTwo(object): + class SimpleDataObjectTwo: field_a: int = field() field_b: int = field() diff --git a/testing/example_scripts/deprecated/test_fixture_named_request.py b/testing/example_scripts/deprecated/test_fixture_named_request.py index 36addc29954..75514bf8b8c 100644 --- a/testing/example_scripts/deprecated/test_fixture_named_request.py +++ b/testing/example_scripts/deprecated/test_fixture_named_request.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest diff --git a/testing/example_scripts/fixtures/custom_item/conftest.py b/testing/example_scripts/fixtures/custom_item/conftest.py index fa41d0fba68..25299d72690 100644 --- a/testing/example_scripts/fixtures/custom_item/conftest.py +++ b/testing/example_scripts/fixtures/custom_item/conftest.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest diff --git a/testing/example_scripts/fixtures/custom_item/foo/test_foo.py b/testing/example_scripts/fixtures/custom_item/foo/test_foo.py index 10c5f605202..f174823854e 100644 --- a/testing/example_scripts/fixtures/custom_item/foo/test_foo.py +++ b/testing/example_scripts/fixtures/custom_item/foo/test_foo.py @@ -1,3 +1,2 @@ -# -*- coding: utf-8 -*- def test(): pass diff --git a/testing/example_scripts/fixtures/fill_fixtures/test_conftest_funcargs_only_available_in_subdir/sub1/conftest.py b/testing/example_scripts/fixtures/fill_fixtures/test_conftest_funcargs_only_available_in_subdir/sub1/conftest.py index 952e7d60a69..79af4bc4790 100644 --- a/testing/example_scripts/fixtures/fill_fixtures/test_conftest_funcargs_only_available_in_subdir/sub1/conftest.py +++ b/testing/example_scripts/fixtures/fill_fixtures/test_conftest_funcargs_only_available_in_subdir/sub1/conftest.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest diff --git a/testing/example_scripts/fixtures/fill_fixtures/test_conftest_funcargs_only_available_in_subdir/sub1/test_in_sub1.py b/testing/example_scripts/fixtures/fill_fixtures/test_conftest_funcargs_only_available_in_subdir/sub1/test_in_sub1.py index b824f4e99ef..df36da1369b 100644 --- a/testing/example_scripts/fixtures/fill_fixtures/test_conftest_funcargs_only_available_in_subdir/sub1/test_in_sub1.py +++ b/testing/example_scripts/fixtures/fill_fixtures/test_conftest_funcargs_only_available_in_subdir/sub1/test_in_sub1.py @@ -1,3 +1,2 @@ -# -*- coding: utf-8 -*- def test_1(arg1): pass diff --git a/testing/example_scripts/fixtures/fill_fixtures/test_conftest_funcargs_only_available_in_subdir/sub2/conftest.py b/testing/example_scripts/fixtures/fill_fixtures/test_conftest_funcargs_only_available_in_subdir/sub2/conftest.py index 2be90b4dfe7..00981c5dc12 100644 --- a/testing/example_scripts/fixtures/fill_fixtures/test_conftest_funcargs_only_available_in_subdir/sub2/conftest.py +++ b/testing/example_scripts/fixtures/fill_fixtures/test_conftest_funcargs_only_available_in_subdir/sub2/conftest.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest diff --git a/testing/example_scripts/fixtures/fill_fixtures/test_conftest_funcargs_only_available_in_subdir/sub2/test_in_sub2.py b/testing/example_scripts/fixtures/fill_fixtures/test_conftest_funcargs_only_available_in_subdir/sub2/test_in_sub2.py index a1432042bfd..1c34f94acc4 100644 --- a/testing/example_scripts/fixtures/fill_fixtures/test_conftest_funcargs_only_available_in_subdir/sub2/test_in_sub2.py +++ b/testing/example_scripts/fixtures/fill_fixtures/test_conftest_funcargs_only_available_in_subdir/sub2/test_in_sub2.py @@ -1,3 +1,2 @@ -# -*- coding: utf-8 -*- def test_2(arg2): pass diff --git a/testing/example_scripts/fixtures/fill_fixtures/test_detect_recursive_dependency_error.py b/testing/example_scripts/fixtures/fill_fixtures/test_detect_recursive_dependency_error.py index d1e54eea7e4..d1efcbb338c 100644 --- a/testing/example_scripts/fixtures/fill_fixtures/test_detect_recursive_dependency_error.py +++ b/testing/example_scripts/fixtures/fill_fixtures/test_detect_recursive_dependency_error.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest diff --git a/testing/example_scripts/fixtures/fill_fixtures/test_extend_fixture_conftest_conftest/conftest.py b/testing/example_scripts/fixtures/fill_fixtures/test_extend_fixture_conftest_conftest/conftest.py index 87756e6491f..5dfd2f77957 100644 --- a/testing/example_scripts/fixtures/fill_fixtures/test_extend_fixture_conftest_conftest/conftest.py +++ b/testing/example_scripts/fixtures/fill_fixtures/test_extend_fixture_conftest_conftest/conftest.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest diff --git a/testing/example_scripts/fixtures/fill_fixtures/test_extend_fixture_conftest_conftest/pkg/conftest.py b/testing/example_scripts/fixtures/fill_fixtures/test_extend_fixture_conftest_conftest/pkg/conftest.py index 1cd88524b6f..4e22ce5a137 100644 --- a/testing/example_scripts/fixtures/fill_fixtures/test_extend_fixture_conftest_conftest/pkg/conftest.py +++ b/testing/example_scripts/fixtures/fill_fixtures/test_extend_fixture_conftest_conftest/pkg/conftest.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest diff --git a/testing/example_scripts/fixtures/fill_fixtures/test_extend_fixture_conftest_conftest/pkg/test_spam.py b/testing/example_scripts/fixtures/fill_fixtures/test_extend_fixture_conftest_conftest/pkg/test_spam.py index 65690c49f23..0d891fbb503 100644 --- a/testing/example_scripts/fixtures/fill_fixtures/test_extend_fixture_conftest_conftest/pkg/test_spam.py +++ b/testing/example_scripts/fixtures/fill_fixtures/test_extend_fixture_conftest_conftest/pkg/test_spam.py @@ -1,3 +1,2 @@ -# -*- coding: utf-8 -*- def test_spam(spam): assert spam == "spamspam" diff --git a/testing/example_scripts/fixtures/fill_fixtures/test_extend_fixture_conftest_module/conftest.py b/testing/example_scripts/fixtures/fill_fixtures/test_extend_fixture_conftest_module/conftest.py index 87756e6491f..5dfd2f77957 100644 --- a/testing/example_scripts/fixtures/fill_fixtures/test_extend_fixture_conftest_module/conftest.py +++ b/testing/example_scripts/fixtures/fill_fixtures/test_extend_fixture_conftest_module/conftest.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest diff --git a/testing/example_scripts/fixtures/fill_fixtures/test_extend_fixture_conftest_module/test_extend_fixture_conftest_module.py b/testing/example_scripts/fixtures/fill_fixtures/test_extend_fixture_conftest_module/test_extend_fixture_conftest_module.py index 5b344c122f1..46d1446f470 100644 --- a/testing/example_scripts/fixtures/fill_fixtures/test_extend_fixture_conftest_module/test_extend_fixture_conftest_module.py +++ b/testing/example_scripts/fixtures/fill_fixtures/test_extend_fixture_conftest_module/test_extend_fixture_conftest_module.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest diff --git a/testing/example_scripts/fixtures/fill_fixtures/test_extend_fixture_module_class.py b/testing/example_scripts/fixtures/fill_fixtures/test_extend_fixture_module_class.py index 4332c66017b..87a0c894111 100644 --- a/testing/example_scripts/fixtures/fill_fixtures/test_extend_fixture_module_class.py +++ b/testing/example_scripts/fixtures/fill_fixtures/test_extend_fixture_module_class.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest @@ -7,7 +6,7 @@ def spam(): return "spam" -class TestSpam(object): +class TestSpam: @pytest.fixture def spam(self, spam): return spam * 2 diff --git a/testing/example_scripts/fixtures/fill_fixtures/test_funcarg_basic.py b/testing/example_scripts/fixtures/fill_fixtures/test_funcarg_basic.py index ba302c49e6c..0661cb301fc 100644 --- a/testing/example_scripts/fixtures/fill_fixtures/test_funcarg_basic.py +++ b/testing/example_scripts/fixtures/fill_fixtures/test_funcarg_basic.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest diff --git a/testing/example_scripts/fixtures/fill_fixtures/test_funcarg_lookup_classlevel.py b/testing/example_scripts/fixtures/fill_fixtures/test_funcarg_lookup_classlevel.py index 1004d5d1352..256b92a17dd 100644 --- a/testing/example_scripts/fixtures/fill_fixtures/test_funcarg_lookup_classlevel.py +++ b/testing/example_scripts/fixtures/fill_fixtures/test_funcarg_lookup_classlevel.py @@ -1,8 +1,7 @@ -# -*- coding: utf-8 -*- import pytest -class TestClass(object): +class TestClass: @pytest.fixture def something(self, request): return request.instance diff --git a/testing/example_scripts/fixtures/fill_fixtures/test_funcarg_lookup_modulelevel.py b/testing/example_scripts/fixtures/fill_fixtures/test_funcarg_lookup_modulelevel.py index 79d283e9f66..e15dbd2ca45 100644 --- a/testing/example_scripts/fixtures/fill_fixtures/test_funcarg_lookup_modulelevel.py +++ b/testing/example_scripts/fixtures/fill_fixtures/test_funcarg_lookup_modulelevel.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest @@ -7,7 +6,7 @@ def something(request): return request.function.__name__ -class TestClass(object): +class TestClass: def test_method(self, something): assert something == "test_method" diff --git a/testing/example_scripts/fixtures/fill_fixtures/test_funcarg_lookupfails.py b/testing/example_scripts/fixtures/fill_fixtures/test_funcarg_lookupfails.py index 18f426af9c9..b775203231f 100644 --- a/testing/example_scripts/fixtures/fill_fixtures/test_funcarg_lookupfails.py +++ b/testing/example_scripts/fixtures/fill_fixtures/test_funcarg_lookupfails.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest diff --git a/testing/example_scripts/fixtures/test_getfixturevalue_dynamic.py b/testing/example_scripts/fixtures/test_getfixturevalue_dynamic.py index 219e57421d8..055a1220b1c 100644 --- a/testing/example_scripts/fixtures/test_getfixturevalue_dynamic.py +++ b/testing/example_scripts/fixtures/test_getfixturevalue_dynamic.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest diff --git a/testing/example_scripts/issue88_initial_file_multinodes/conftest.py b/testing/example_scripts/issue88_initial_file_multinodes/conftest.py index 8f3e7b4e52a..aa5d878313c 100644 --- a/testing/example_scripts/issue88_initial_file_multinodes/conftest.py +++ b/testing/example_scripts/issue88_initial_file_multinodes/conftest.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest diff --git a/testing/example_scripts/issue88_initial_file_multinodes/test_hello.py b/testing/example_scripts/issue88_initial_file_multinodes/test_hello.py index 653d570a88c..56444d14748 100644 --- a/testing/example_scripts/issue88_initial_file_multinodes/test_hello.py +++ b/testing/example_scripts/issue88_initial_file_multinodes/test_hello.py @@ -1,3 +1,2 @@ -# -*- coding: utf-8 -*- def test_hello(): pass diff --git a/testing/example_scripts/issue_519.py b/testing/example_scripts/issue_519.py index 24d864a5380..7199df820fb 100644 --- a/testing/example_scripts/issue_519.py +++ b/testing/example_scripts/issue_519.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pprint import pytest diff --git a/testing/example_scripts/marks/marks_considered_keywords/test_marks_as_keywords.py b/testing/example_scripts/marks/marks_considered_keywords/test_marks_as_keywords.py index 61d969a36b8..35a2c7b7628 100644 --- a/testing/example_scripts/marks/marks_considered_keywords/test_marks_as_keywords.py +++ b/testing/example_scripts/marks/marks_considered_keywords/test_marks_as_keywords.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest 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 32ca818ead9..ff1eaf7d6bb 100644 --- a/testing/example_scripts/perf_examples/collect_stats/generate_folders.py +++ b/testing/example_scripts/perf_examples/collect_stats/generate_folders.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import argparse import pathlib diff --git a/testing/example_scripts/perf_examples/collect_stats/template_test.py b/testing/example_scripts/perf_examples/collect_stats/template_test.py index 5d0cd5a7164..064ade190a1 100644 --- a/testing/example_scripts/perf_examples/collect_stats/template_test.py +++ b/testing/example_scripts/perf_examples/collect_stats/template_test.py @@ -1,3 +1,2 @@ -# -*- coding: utf-8 -*- def test_x(): pass diff --git a/testing/example_scripts/tmpdir/tmpdir_fixture.py b/testing/example_scripts/tmpdir/tmpdir_fixture.py index 33f0315d52f..f4ad07462cb 100644 --- a/testing/example_scripts/tmpdir/tmpdir_fixture.py +++ b/testing/example_scripts/tmpdir/tmpdir_fixture.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest diff --git a/testing/example_scripts/unittest/test_parametrized_fixture_error_message.py b/testing/example_scripts/unittest/test_parametrized_fixture_error_message.py index cef950ee782..d421ce927c9 100644 --- a/testing/example_scripts/unittest/test_parametrized_fixture_error_message.py +++ b/testing/example_scripts/unittest/test_parametrized_fixture_error_message.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import unittest import pytest diff --git a/testing/example_scripts/unittest/test_setup_skip.py b/testing/example_scripts/unittest/test_setup_skip.py index 26b6195e96d..93f79bb3b2e 100644 --- a/testing/example_scripts/unittest/test_setup_skip.py +++ b/testing/example_scripts/unittest/test_setup_skip.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """Skipping an entire subclass with unittest.skip() should *not* call setUp from a base class.""" import unittest diff --git a/testing/example_scripts/unittest/test_setup_skip_class.py b/testing/example_scripts/unittest/test_setup_skip_class.py index 0c4a3775723..4f251dcba17 100644 --- a/testing/example_scripts/unittest/test_setup_skip_class.py +++ b/testing/example_scripts/unittest/test_setup_skip_class.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """Skipping an entire subclass with unittest.skip() should *not* call setUpClass from a base class.""" import unittest diff --git a/testing/example_scripts/unittest/test_setup_skip_module.py b/testing/example_scripts/unittest/test_setup_skip_module.py index abf782a6217..98befbe510f 100644 --- a/testing/example_scripts/unittest/test_setup_skip_module.py +++ b/testing/example_scripts/unittest/test_setup_skip_module.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """setUpModule is always called, even if all tests in the module are skipped""" import unittest diff --git a/testing/example_scripts/warnings/test_group_warnings_by_message.py b/testing/example_scripts/warnings/test_group_warnings_by_message.py index 29d89597452..c736135b7b9 100644 --- a/testing/example_scripts/warnings/test_group_warnings_by_message.py +++ b/testing/example_scripts/warnings/test_group_warnings_by_message.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import warnings import pytest diff --git a/testing/examples/test_issue519.py b/testing/examples/test_issue519.py index 7da1f4ee664..e83f18fdc93 100644 --- a/testing/examples/test_issue519.py +++ b/testing/examples/test_issue519.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- def test_510(testdir): testdir.copy_example("issue_519.py") testdir.runpytest("issue_519.py") diff --git a/testing/freeze/create_executable.py b/testing/freeze/create_executable.py index ab8013317b2..b53eb09f53b 100644 --- a/testing/freeze/create_executable.py +++ b/testing/freeze/create_executable.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Generates an executable with pytest runner embedded using PyInstaller. """ diff --git a/testing/freeze/runtests_script.py b/testing/freeze/runtests_script.py index f7381f56863..d03bca8406d 100644 --- a/testing/freeze/runtests_script.py +++ b/testing/freeze/runtests_script.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ This is the script that is actually frozen into an executable: simply executes py.test main(). diff --git a/testing/freeze/tests/test_trivial.py b/testing/freeze/tests/test_trivial.py index 449dbf9d362..08a55552abb 100644 --- a/testing/freeze/tests/test_trivial.py +++ b/testing/freeze/tests/test_trivial.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- def test_upper(): assert "foo".upper() == "FOO" diff --git a/testing/freeze/tox_run.py b/testing/freeze/tox_run.py index 29b72792384..678a69c858a 100644 --- a/testing/freeze/tox_run.py +++ b/testing/freeze/tox_run.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Called by tox.ini: uses the generated executable to run the tests in ./tests/ directory. diff --git a/testing/io/test_saferepr.py b/testing/io/test_saferepr.py index 90120308889..e57b53f81e9 100644 --- a/testing/io/test_saferepr.py +++ b/testing/io/test_saferepr.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- from _pytest._io.saferepr import saferepr @@ -53,7 +52,7 @@ def test_big_repr(): def test_repr_on_newstyle(): - class Function(object): + class Function: def __repr__(self): return "<%s>" % (self.name) @@ -61,6 +60,6 @@ def __repr__(self): def test_unicode(): - val = u"£€" - reprval = u"'£€'" + val = "£€" + reprval = "'£€'" assert saferepr(val) == reprval diff --git a/testing/logging/test_fixture.py b/testing/logging/test_fixture.py index ff772e7ec62..5d2ff4654d0 100644 --- a/testing/logging/test_fixture.py +++ b/testing/logging/test_fixture.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import logging import pytest @@ -103,15 +102,15 @@ def test_record_tuples(caplog): def test_unicode(caplog): caplog.set_level(logging.INFO) - logger.info(u"bū") + logger.info("bū") assert caplog.records[0].levelname == "INFO" - assert caplog.records[0].msg == u"bū" - assert u"bū" in caplog.text + assert caplog.records[0].msg == "bū" + assert "bū" in caplog.text def test_clear(caplog): caplog.set_level(logging.INFO) - logger.info(u"bū") + logger.info("bū") assert len(caplog.records) assert caplog.text caplog.clear() diff --git a/testing/logging/test_formatter.py b/testing/logging/test_formatter.py index c851c34d784..6850a83cdb7 100644 --- a/testing/logging/test_formatter.py +++ b/testing/logging/test_formatter.py @@ -1,10 +1,7 @@ -# -*- coding: utf-8 -*- import logging import py.io -import six -import pytest from _pytest.logging import ColoredLevelFormatter @@ -21,8 +18,8 @@ def test_coloredlogformatter(): exc_info=False, ) - class ColorConfig(object): - class option(object): + class ColorConfig: + class option: pass tw = py.io.TerminalWriter() @@ -39,9 +36,6 @@ class option(object): assert output == ("dummypath 10 INFO Test Message") -@pytest.mark.skipif( - six.PY2, reason="Formatter classes don't support format styles in PY2" -) def test_multiline_message(): from _pytest.logging import PercentStyleMultiline @@ -65,3 +59,28 @@ def test_multiline_message(): "dummypath 10 INFO Test Message line1\n" " line2" ) + + +def test_colored_short_level(): + logfmt = "%(levelname).1s %(message)s" + + record = logging.LogRecord( + name="dummy", + level=logging.INFO, + pathname="dummypath", + lineno=10, + msg="Test Message", + args=(), + exc_info=False, + ) + + class ColorConfig: + class option: + pass + + tw = py.io.TerminalWriter() + tw.hasmarkup = True + formatter = ColoredLevelFormatter(tw, logfmt) + output = formatter.format(record) + # the I (of INFO) is colored + assert output == ("\x1b[32mI\x1b[0m Test Message") diff --git a/testing/logging/test_reporting.py b/testing/logging/test_reporting.py index e7a3a80ebf1..68be819b9a4 100644 --- a/testing/logging/test_reporting.py +++ b/testing/logging/test_reporting.py @@ -1,9 +1,6 @@ -# -*- coding: utf-8 -*- +import io import os import re -from io import open - -import six import pytest @@ -841,16 +838,14 @@ def test_log_file_unicode(testdir): ) ) testdir.makepyfile( - """ - # -*- coding: utf-8 -*- - from __future__ import unicode_literals + """\ import logging def test_log_file(): logging.getLogger('catchlog').info("Normal message") logging.getLogger('catchlog').info("├") logging.getLogger('catchlog').info("Another normal message") - """ + """ ) result = testdir.runpytest() @@ -861,7 +856,7 @@ def test_log_file(): with open(log_file, encoding="utf-8") as rfh: contents = rfh.read() assert "Normal message" in contents - assert u"├" in contents + assert "├" in contents assert "Another normal message" in contents @@ -889,7 +884,7 @@ def global_and_fixture_disabled(self): yield self.calls.append("exit disabled") - class DummyTerminal(six.StringIO): + class DummyTerminal(io.StringIO): def section(self, *args, **kwargs): pass diff --git a/testing/python/approx.py b/testing/python/approx.py index cd8df5d274f..0575557ae78 100644 --- a/testing/python/approx.py +++ b/testing/python/approx.py @@ -1,7 +1,5 @@ -# -*- coding: utf-8 -*- import doctest import operator -import sys from decimal import Decimal from fractions import Fraction from operator import eq @@ -25,10 +23,10 @@ def report_failure(self, out, test, example, got): ) -class TestApprox(object): +class TestApprox: @pytest.fixture def plus_minus(self): - return u"\u00b1" if sys.version_info[0] > 2 else u"+-" + return "\u00b1" def test_repr_string(self, plus_minus): tol1, tol2, infr = "1.0e-06", "2.0e-06", "inf" @@ -498,7 +496,7 @@ def test_numpy_scalar_with_array(self): assert approx(expected, rel=5e-8, abs=0) != actual def test_generic_sized_iterable_object(self): - class MySizedIterable(object): + class MySizedIterable: def __iter__(self): return iter([1, 2, 3, 4]) diff --git a/testing/python/collect.py b/testing/python/collect.py index 501b30a49b0..981e30fc3bc 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import os import sys import textwrap @@ -9,7 +8,7 @@ from _pytest.nodes import Collector -class TestModule(object): +class TestModule: def test_failing_import(self, testdir): modcol = testdir.getmodulecol("import alksdjalskdjalkjals") pytest.raises(Collector.CollectError, modcol.collect) @@ -117,12 +116,7 @@ def test_show_traceback_import_error_unicode(self, testdir): """Check test modules collected which raise ImportError with unicode messages are handled properly (#2336). """ - testdir.makepyfile( - u""" - # -*- coding: utf-8 -*- - raise ImportError(u'Something bad happened ☺') - """ - ) + testdir.makepyfile("raise ImportError(u'Something bad happened ☺')") result = testdir.runpytest() result.stdout.fnmatch_lines( [ @@ -134,7 +128,7 @@ def test_show_traceback_import_error_unicode(self, testdir): assert result.ret == 2 -class TestClass(object): +class TestClass: def test_class_with_init_warning(self, testdir): testdir.makepyfile( """ @@ -255,7 +249,7 @@ def prop(self): assert result.ret == EXIT_NOTESTSCOLLECTED -class TestFunction(object): +class TestFunction: def test_getmodulecollector(self, testdir): item = testdir.getitem("def test_func(): pass") modcol = item.getparent(pytest.Module) @@ -514,11 +508,11 @@ def test_pyfunc_call(self, testdir): item = testdir.getitem("def test_func(): raise ValueError") config = item.config - class MyPlugin1(object): + class MyPlugin1: def pytest_pyfunc_call(self, pyfuncitem): raise ValueError - class MyPlugin2(object): + class MyPlugin2: def pytest_pyfunc_call(self, pyfuncitem): return True @@ -664,7 +658,7 @@ def test_func(arg): assert [x.originalname for x in items] == ["test_func", "test_func"] -class TestSorting(object): +class TestSorting: def test_check_equality(self, testdir): modcol = testdir.getmodulecol( """ @@ -679,8 +673,6 @@ def test_fail(): assert 0 assert fn1 == fn2 assert fn1 != modcol - if sys.version_info < (3, 0): - assert cmp(fn1, fn2) == 0 # NOQA assert hash(fn1) == hash(fn2) fn3 = testdir.collect_by_name(modcol, "test_fail") @@ -718,7 +710,7 @@ def test_a(y): assert [item.name for item in colitems] == ["test_b", "test_a"] -class TestConftestCustomization(object): +class TestConftestCustomization: def test_pytest_pycollect_module(self, testdir): testdir.makeconftest( """ @@ -893,7 +885,7 @@ def test_modulecol_roundtrip(testdir): assert modcol.name == newcol.name -class TestTracebackCutting(object): +class TestTracebackCutting: def test_skip_simple(self): with pytest.raises(pytest.skip.Exception) as excinfo: pytest.skip("xxx") @@ -1022,7 +1014,7 @@ def foo(): assert filter_traceback(tb[-1]) -class TestReportInfo(object): +class TestReportInfo: def test_itemreport_reportinfo(self, testdir, linecomp): testdir.makeconftest( """ @@ -1259,13 +1251,7 @@ def test_injection(self): def test_syntax_error_with_non_ascii_chars(testdir): """Fix decoding issue while formatting SyntaxErrors during collection (#578) """ - testdir.makepyfile( - u""" - # -*- coding: utf-8 -*- - - ☃ - """ - ) + testdir.makepyfile("☃") result = testdir.runpytest() result.stdout.fnmatch_lines(["*ERROR collecting*", "*SyntaxError*", "*1 error in*"]) diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index bd1b1d97541..a9ea333adc4 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import sys import textwrap @@ -32,7 +31,7 @@ def h(arg1, arg2, arg3="hello"): assert fixtures.getfuncargnames(h) == ("arg1", "arg2") - class A(object): + class A: def f(self, arg1, arg2="hello"): pass @@ -45,7 +44,7 @@ def static(arg1, arg2): @pytest.mark.pytester_example_path("fixtures/fill_fixtures") -class TestFillFixtures(object): +class TestFillFixtures: def test_fillfuncargs_exposed(self): # used by oejskit, kept for compatibility assert pytest._fillfuncargs == fixtures.fillfixtures @@ -443,7 +442,7 @@ def test_leak(leak): assert result.ret == 0 -class TestRequestBasic(object): +class TestRequestBasic: def test_request_attributes(self, testdir): item = testdir.getitem( """ @@ -902,7 +901,7 @@ def test_1(arg): reprec.assertoutcome(passed=2) -class TestRequestMarking(object): +class TestRequestMarking: def test_applymarker(self, testdir): item1, item2 = testdir.getitems( """ @@ -972,7 +971,7 @@ def test_fun2(keywords): reprec.assertoutcome(passed=2) -class TestFixtureUsages(object): +class TestFixtureUsages: def test_noargfixturedec(self, testdir): testdir.makepyfile( """ @@ -1303,7 +1302,7 @@ def test_printer_2(self): result.stdout.fnmatch_lines(["* 2 passed in *"]) -class TestFixtureManagerParseFactories(object): +class TestFixtureManagerParseFactories: @pytest.fixture def testdir(self, request): testdir = request.getfixturevalue("testdir") @@ -1358,9 +1357,8 @@ def test_hello(item, fm): def test_parsefactories_conftest_and_module_and_class(self, testdir): testdir.makepyfile( - """ + """\ import pytest - import six @pytest.fixture def hello(request): @@ -1377,7 +1375,7 @@ def test_hello(self, item, fm): assert faclist[0].func(item._request) == "conftest" assert faclist[1].func(item._request) == "module" assert faclist[2].func(item._request) == "class" - """ + """ ) reprec = testdir.inline_run("-s") reprec.assertoutcome(passed=1) @@ -1529,7 +1527,7 @@ def test_collect_custom_items(self, testdir): result.stdout.fnmatch_lines(["*passed*"]) -class TestAutouseDiscovery(object): +class TestAutouseDiscovery: @pytest.fixture def testdir(self, testdir): testdir.makeconftest( @@ -1705,7 +1703,7 @@ def test_world(self): reprec.assertoutcome(passed=3) -class TestAutouseManagement(object): +class TestAutouseManagement: def test_autouse_conftest_mid_directory(self, testdir): pkgdir = testdir.mkpydir("xyz123") pkgdir.join("conftest.py").write( @@ -1953,7 +1951,7 @@ def test_check(): reprec.assertoutcome(passed=2) -class TestFixtureMarker(object): +class TestFixtureMarker: def test_parametrize(self, testdir): testdir.makepyfile( """ @@ -2909,7 +2907,7 @@ def test_foo(B): assert out1 == out2 -class TestRequestScopeAccess(object): +class TestRequestScopeAccess: pytestmark = pytest.mark.parametrize( ("scope", "ok", "error"), [ @@ -2963,7 +2961,7 @@ def test_func(arg): reprec.assertoutcome(passed=1) -class TestErrors(object): +class TestErrors: def test_subfactory_missing_funcarg(self, testdir): testdir.makepyfile( """ @@ -3030,7 +3028,7 @@ def test_something(): ) -class TestShowFixtures(object): +class TestShowFixtures: def test_funcarg_compat(self, testdir): config = testdir.parseconfigure("--funcargs") assert config.option.showfixtures @@ -3318,7 +3316,7 @@ def foo(): pass -class TestContextManagerFixtureFuncs(object): +class TestContextManagerFixtureFuncs: @pytest.fixture(params=["fixture", "yield_fixture"]) def flavor(self, request, testdir, monkeypatch): monkeypatch.setenv("PYTEST_FIXTURE_FLAVOR", request.param) @@ -3339,7 +3337,6 @@ def flavor(self, request, testdir, monkeypatch): def test_simple(self, testdir, flavor): testdir.makepyfile( """ - from __future__ import print_function from test_context import fixture @fixture def arg1(): @@ -3368,7 +3365,6 @@ def test_2(arg1): def test_scoped(self, testdir, flavor): testdir.makepyfile( """ - from __future__ import print_function from test_context import fixture @fixture(scope="module") def arg1(): @@ -3466,7 +3462,7 @@ def test_1(meow): result.stdout.fnmatch_lines(["*mew*"]) -class TestParameterizedSubRequest(object): +class TestParameterizedSubRequest: def test_call_from_fixture(self, testdir): testdir.makepyfile( test_call_from_fixture=""" @@ -3602,7 +3598,6 @@ def test_foo(request): def test_pytest_fixture_setup_and_post_finalizer_hook(testdir): testdir.makeconftest( """ - from __future__ import print_function def pytest_fixture_setup(fixturedef, request): print('ROOT setup hook called for {0} from {1}'.format(fixturedef.argname, request.node.name)) def pytest_fixture_post_finalizer(fixturedef, request): @@ -3612,14 +3607,12 @@ def pytest_fixture_post_finalizer(fixturedef, request): testdir.makepyfile( **{ "tests/conftest.py": """ - from __future__ import print_function def pytest_fixture_setup(fixturedef, request): print('TESTS setup hook called for {0} from {1}'.format(fixturedef.argname, request.node.name)) def pytest_fixture_post_finalizer(fixturedef, request): print('TESTS finalizer hook called for {0} from {1}'.format(fixturedef.argname, request.node.name)) """, "tests/test_hooks.py": """ - from __future__ import print_function import pytest @pytest.fixture() @@ -3645,7 +3638,7 @@ def test_func(my_fixture): ) -class TestScopeOrdering(object): +class TestScopeOrdering: """Class of tests that ensure fixtures are ordered based on their scopes (#2405)""" @pytest.mark.parametrize("variant", ["mark", "autouse"]) diff --git a/testing/python/integration.py b/testing/python/integration.py index d2fea2dc5ca..0b87fea3327 100644 --- a/testing/python/integration.py +++ b/testing/python/integration.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- import pytest from _pytest import python from _pytest import runner -class TestOEJSKITSpecials(object): +class TestOEJSKITSpecials: def test_funcarg_non_pycollectobj(self, testdir): # rough jstests usage testdir.makeconftest( """ @@ -87,7 +86,7 @@ def wrapped_func(x, y, z): assert lineno > lineno2, "getfslineno does not unwrap correctly" -class TestMockDecoration(object): +class TestMockDecoration: def test_wrapped_getfuncargnames(self): from _pytest.compat import getfuncargnames @@ -264,7 +263,7 @@ def test_simple_thing(self, mock_path, mock_getcwd): reprec.assertoutcome(passed=1) -class TestReRunTests(object): +class TestReRunTests: def test_rerun(self, testdir): testdir.makeconftest( """ @@ -310,7 +309,7 @@ def test_pytestconfig_is_session_scoped(): assert pytestconfig._pytestfixturefunction.scope == "session" -class TestNoselikeTestAttribute(object): +class TestNoselikeTestAttribute: def test_module_with_global_test(self, testdir): testdir.makepyfile( """ @@ -394,7 +393,7 @@ def test_blah(self): assert not call.items -class TestParameterize(object): +class TestParameterize: """#351""" def test_idfn_marker(self, testdir): diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 945ab8627cb..4702f0b57d2 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -1,11 +1,9 @@ -# -*- coding: utf-8 -*- import re import sys import textwrap import attr import hypothesis -import six from hypothesis import strategies import pytest @@ -13,22 +11,20 @@ from _pytest import python from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG -PY3 = sys.version_info >= (3, 0) - -class TestMetafunc(object): +class TestMetafunc: def Metafunc(self, func, config=None): # the unit tests of this class check if things work correctly # on the funcarg level, so we don't need a full blown # initiliazation - class FixtureInfo(object): + class FixtureInfo: name2fixturedefs = None def __init__(self, names): self.names_closure = names @attr.s - class DefinitionMock(object): + class DefinitionMock: obj = attr.ib() names = fixtures.getfuncargnames(func) @@ -82,7 +78,7 @@ def test_find_parametrized_scope(self): from _pytest.python import _find_parametrized_scope @attr.s - class DummyFixtureDef(object): + class DummyFixtureDef: scope = attr.ib() fixtures_defs = dict( @@ -141,9 +137,9 @@ def func(x): pass metafunc = self.Metafunc(func) - metafunc.parametrize("x", [1, 2], ids=[u"basic", u"advanced"]) + metafunc.parametrize("x", [1, 2], ids=["basic", "advanced"]) ids = [x.id for x in metafunc._calls] - assert ids == [u"basic", u"advanced"] + assert ids == ["basic", "advanced"] def test_parametrize_with_wrong_number_of_ids(self, testdir): def func(x, y): @@ -165,7 +161,7 @@ def test_parametrize_empty_list(self): def func(y): pass - class MockConfig(object): + class MockConfig: def getini(self, name): return "" @@ -186,7 +182,7 @@ def func(x, y): metafunc = self.Metafunc(func) - class A(object): + class A: pass metafunc.parametrize("x", [A(), A()]) @@ -204,7 +200,7 @@ def test_idval_hypothesis(self, value): from _pytest.python import _idval escaped = _idval(value, "a", 6, None, item=None, config=None) - assert isinstance(escaped, six.text_type) + assert isinstance(escaped, str) escaped.encode("ascii") def test_unicode_idval(self): @@ -216,12 +212,12 @@ def test_unicode_idval(self): from _pytest.python import _idval values = [ - (u"", ""), - (u"ascii", "ascii"), - (u"ação", "a\\xe7\\xe3o"), - (u"josé@blah.com", "jos\\xe9@blah.com"), + ("", ""), + ("ascii", "ascii"), + ("ação", "a\\xe7\\xe3o"), + ("josé@blah.com", "jos\\xe9@blah.com"), ( - u"δοκ.ιμή@παράδειγμα.δοκιμή", + "δοκ.ιμή@παράδειγμα.δοκιμή", "\\u03b4\\u03bf\\u03ba.\\u03b9\\u03bc\\u03ae@\\u03c0\\u03b1\\u03c1\\u03ac\\u03b4\\u03b5\\u03b9\\u03b3" "\\u03bc\\u03b1.\\u03b4\\u03bf\\u03ba\\u03b9\\u03bc\\u03ae", ), @@ -236,7 +232,7 @@ def test_unicode_idval_with_config(self): """ from _pytest.python import _idval - class MockConfig(object): + class MockConfig: def __init__(self, config): self.config = config @@ -253,8 +249,8 @@ def getini(self, name): option = "disable_test_id_escaping_and_forfeit_all_rights_to_community_support" values = [ - (u"ação", MockConfig({option: True}), u"ação"), - (u"ação", MockConfig({option: False}), "a\\xe7\\xe3o"), + ("ação", MockConfig({option: True}), "ação"), + ("ação", MockConfig({option: False}), "a\\xe7\\xe3o"), ] for val, config, expected in values: assert _idval(val, "a", 6, None, item=None, config=config) == expected @@ -272,7 +268,7 @@ def test_bytes_idval(self): (b"", ""), (b"\xc3\xb4\xff\xe4", "\\xc3\\xb4\\xff\\xe4"), (b"ascii", "ascii"), - (u"αρά".encode("utf-8"), "\\xce\\xb1\\xcf\\x81\\xce\\xac"), + ("αρά".encode(), "\\xce\\xb1\\xcf\\x81\\xce\\xac"), ] for val, expected in values: assert _idval(val, "a", 6, idfn=None, item=None, config=None) == expected @@ -283,7 +279,7 @@ def test_class_or_function_idval(self): """ from _pytest.python import _idval - class TestClass(object): + class TestClass: pass def test_function(): @@ -307,7 +303,7 @@ def test_idmaker_autoname(self): ) assert result == ["a0-1.0", "a1-b1"] # unicode mixing, issue250 - result = idmaker((u"a", "b"), [pytest.param({}, b"\xc3\xb4")]) + result = idmaker(("a", "b"), [pytest.param({}, b"\xc3\xb4")]) assert result == ["a0-\\xc3\\xb4"] def test_idmaker_with_bytes_regex(self): @@ -333,7 +329,7 @@ def test_idmaker_native_strings(self): pytest.param({7}, set("seven")), pytest.param(tuple("eight"), (8, -8, 8)), pytest.param(b"\xc3\xb4", b"name"), - pytest.param(b"\xc3\xb4", u"other"), + pytest.param(b"\xc3\xb4", "other"), ], ) assert result == [ @@ -431,7 +427,7 @@ def test_idmaker_with_idfn_and_config(self): """ from _pytest.python import idmaker - class MockConfig(object): + class MockConfig: def __init__(self, config): self.config = config @@ -448,12 +444,12 @@ def getini(self, name): option = "disable_test_id_escaping_and_forfeit_all_rights_to_community_support" values = [ - (MockConfig({option: True}), u"ação"), + (MockConfig({option: True}), "ação"), (MockConfig({option: False}), "a\\xe7\\xe3o"), ] for config, expected in values: result = idmaker( - ("a",), [pytest.param("string")], idfn=lambda _: u"ação", config=config + ("a",), [pytest.param("string")], idfn=lambda _: "ação", config=config ) assert result == [expected] @@ -464,7 +460,7 @@ def test_idmaker_with_ids_and_config(self): """ from _pytest.python import idmaker - class MockConfig(object): + class MockConfig: def __init__(self, config): self.config = config @@ -481,12 +477,12 @@ def getini(self, name): option = "disable_test_id_escaping_and_forfeit_all_rights_to_community_support" values = [ - (MockConfig({option: True}), u"ação"), + (MockConfig({option: True}), "ação"), (MockConfig({option: False}), "a\\xe7\\xe3o"), ] for config, expected in values: result = idmaker( - ("a",), [pytest.param("string")], ids=[u"ação"], config=config + ("a",), [pytest.param("string")], ids=["ação"], config=config ) assert result == [expected] @@ -891,12 +887,12 @@ def function4(arg1, *args, **kwargs): assert fixtures._format_args(function4) == "(arg1, *args, **kwargs)" -class TestMetafuncFunctional(object): +class TestMetafuncFunctional: def test_attributes(self, testdir): p = testdir.makepyfile( """ # assumes that generate/provide runs in the same process - import sys, pytest, six + import sys, pytest def pytest_generate_tests(metafunc): metafunc.parametrize('metafunc', [metafunc]) @@ -914,7 +910,7 @@ class TestClass(object): def test_method(self, metafunc, pytestconfig): assert metafunc.config == pytestconfig assert metafunc.module.__name__ == __name__ - unbound = six.get_unbound_function(TestClass.test_method) + unbound = TestClass.test_method assert metafunc.function == unbound assert metafunc.cls == TestClass """ @@ -1327,7 +1323,7 @@ def test_foo(x): ) -class TestMetafuncFunctionalAuto(object): +class TestMetafuncFunctionalAuto: """ Tests related to automatically find out the correct scope for parametrized tests (#1832). """ @@ -1489,7 +1485,7 @@ def pytest_generate_tests(metafunc): assert output.count("preparing foo-3") == 1 -class TestMarkersWithParametrization(object): +class TestMarkersWithParametrization: """#308""" def test_simple_mark(self, testdir): diff --git a/testing/python/raises.py b/testing/python/raises.py index cd463d74b07..bfcb3dbb163 100644 --- a/testing/python/raises.py +++ b/testing/python/raises.py @@ -1,14 +1,11 @@ -# -*- coding: utf-8 -*- import sys -import six - import pytest from _pytest.outcomes import Failed from _pytest.warning_types import PytestDeprecationWarning -class TestRaises(object): +class TestRaises: def test_raises(self): source = "int('qwe')" with pytest.warns(PytestDeprecationWarning): @@ -36,7 +33,7 @@ def test_raises_function(self): pytest.raises(ValueError, int, "hello") def test_raises_callable_no_exception(self): - class A(object): + class A: def __call__(self): pass @@ -190,7 +187,7 @@ def test_raises_cyclic_reference(self, method): """ import gc - class T(object): + class T: def __call__(self): raise ValueError @@ -238,8 +235,6 @@ def test_raises_match_wrong_type(self): int("asdf") def test_raises_exception_looks_iterable(self): - from six import add_metaclass - class Meta(type(object)): def __getitem__(self, item): return 1 / 0 @@ -247,8 +242,7 @@ def __getitem__(self, item): def __len__(self): return 1 - @add_metaclass(Meta) - class ClassLooksIterableException(Exception): + class ClassLooksIterableException(Exception, metaclass=Meta): pass with pytest.raises( @@ -265,16 +259,7 @@ class CrappyClass(Exception): def __class__(self): assert False, "via __class__" - if six.PY2: - with pytest.raises(pytest.fail.Exception) as excinfo: - with pytest.raises(CrappyClass()): - pass - assert "DID NOT RAISE" in excinfo.value.args[0] - - with pytest.raises(CrappyClass) as excinfo: - raise CrappyClass() - else: - with pytest.raises(AssertionError) as excinfo: - with pytest.raises(CrappyClass()): - pass - assert "via __class__" in excinfo.value.args[0] + with pytest.raises(AssertionError) as excinfo: + with pytest.raises(CrappyClass()): + pass + assert "via __class__" in excinfo.value.args[0] diff --git a/testing/python/setup_only.py b/testing/python/setup_only.py index ba4ceb00fce..4ae24b15a6d 100644 --- a/testing/python/setup_only.py +++ b/testing/python/setup_only.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest diff --git a/testing/python/setup_plan.py b/testing/python/setup_plan.py index ad64e42c29c..0321939a8aa 100644 --- a/testing/python/setup_plan.py +++ b/testing/python/setup_plan.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- def test_show_fixtures_and_test(testdir): """ Verifies that fixtures are not executed. """ p = testdir.makepyfile( diff --git a/testing/python/show_fixtures_per_test.py b/testing/python/show_fixtures_per_test.py index e14344d4ebf..aff8aa0e5e2 100644 --- a/testing/python/show_fixtures_per_test.py +++ b/testing/python/show_fixtures_per_test.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- - - def test_no_items_should_not_show_output(testdir): result = testdir.runpytest("--fixtures-per-test") assert "fixtures used by" not in result.stdout.str() diff --git a/testing/test_argcomplete.py b/testing/test_argcomplete.py index da7758e8e9e..7ccca11ba70 100644 --- a/testing/test_argcomplete.py +++ b/testing/test_argcomplete.py @@ -1,8 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import subprocess import sys @@ -30,24 +25,12 @@ def equal_with_bash(prefix, ffc, fc, out=None): def _wrapcall(*args, **kargs): try: - if sys.version_info > (2, 7): - return subprocess.check_output(*args, **kargs).decode().splitlines() - if "stdout" in kargs: - raise ValueError("stdout argument not allowed, it will be overridden.") - process = subprocess.Popen(stdout=subprocess.PIPE, *args, **kargs) - output, unused_err = process.communicate() - retcode = process.poll() - if retcode: - cmd = kargs.get("args") - if cmd is None: - cmd = args[0] - raise subprocess.CalledProcessError(retcode, cmd) - return output.decode().splitlines() + return subprocess.check_output(*args, **kargs).decode().splitlines() except subprocess.CalledProcessError: return [] -class FilesCompleter(object): +class FilesCompleter: "File completer class, optionally takes a list of allowed extensions" def __init__(self, allowednames=(), directories=True): @@ -90,7 +73,7 @@ def __call__(self, prefix, **kwargs): return completion -class TestArgComplete(object): +class TestArgComplete: @pytest.mark.skipif("sys.platform in ('win32', 'darwin')") def test_compare_with_compgen(self, tmpdir): from _pytest._argcomplete import FastFilesCompleter diff --git a/testing/test_assertion.py b/testing/test_assertion.py index 2085ffd8b44..e651c09cef6 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -1,13 +1,8 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - +import collections.abc as collections_abc import sys import textwrap import attr -import six import _pytest.assertion as plugin import pytest @@ -15,11 +10,9 @@ from _pytest.assertion import truncate from _pytest.assertion import util -PY3 = sys.version_info >= (3, 0) - def mock_config(): - class Config(object): + class Config: verbose = False def getoption(self, name): @@ -30,7 +23,7 @@ def getoption(self, name): return Config() -class TestImportHookInstallation(object): +class TestImportHookInstallation: @pytest.mark.parametrize("initial_conftest", [True, False]) @pytest.mark.parametrize("mode", ["plain", "rewrite"]) def test_conftest_assertion_rewrite(self, testdir, initial_conftest, mode): @@ -275,7 +268,7 @@ def test_register_assert_rewrite_checks_types(self): ) -class TestBinReprIntegration(object): +class TestBinReprIntegration: def test_pytest_assertrepr_compare_called(self, testdir): testdir.makeconftest( """ @@ -307,7 +300,7 @@ def callequal(left, right, verbose=False): return plugin.pytest_assertrepr_compare(config, "==", left, right) -class TestAssert_reprcompare(object): +class TestAssert_reprcompare: def test_different_types(self): assert callequal([0, 1], "foo") is None @@ -372,14 +365,6 @@ def test_list(self): {0, 2}, """ Full diff: - - set([0, 1]) - ? ^ - + set([0, 2]) - ? ^ - """ - if not PY3 - else """ - Full diff: - {0, 1} ? ^ + {0, 2} @@ -483,10 +468,7 @@ def test_frozenzet(self): assert len(expl) > 1 def test_Sequence(self): - if sys.version_info >= (3, 3): - import collections.abc as collections_abc - else: - import collections as collections_abc + if not hasattr(collections_abc, "MutableSequence"): pytest.skip("cannot import MutableSequence") MutableSequence = collections_abc.MutableSequence @@ -544,7 +526,7 @@ def __repr__(self): assert "+" + repr(nums_y) in expl def test_list_bad_repr(self): - class A(object): + class A: def __repr__(self): raise ValueError(42) @@ -571,12 +553,12 @@ def test_repr_no_exc(self): assert "raised in repr()" not in expl def test_unicode(self): - left = u"£€" - right = u"£" + left = "£€" + right = "£" expl = callequal(left, right) - assert expl[0] == u"'£€' == '£'" - assert expl[1] == u"- £€" - assert expl[2] == u"+ £" + assert expl[0] == "'£€' == '£'" + assert expl[1] == "- £€" + assert expl[2] == "+ £" def test_nonascii_text(self): """ @@ -589,10 +571,7 @@ def __repr__(self): return "\xff" expl = callequal(A(), "1") - if PY3: - assert expl == ["ÿ == '1'", "+ 1"] - else: - assert expl == [u"\ufffd == '1'", u"+ 1"] + assert expl == ["ÿ == '1'", "+ 1"] def test_format_nonascii_explanation(self): assert util.format_explanation("λ") @@ -603,12 +582,12 @@ def test_mojibake(self): right = b"\xc3\xa9" expl = callequal(left, right) for line in expl: - assert isinstance(line, six.text_type) - msg = u"\n".join(expl) + assert isinstance(line, str) + msg = "\n".join(expl) assert msg -class TestAssert_reprcompare_dataclass(object): +class TestAssert_reprcompare_dataclass: @pytest.mark.skipif(sys.version_info < (3, 7), reason="Dataclasses in Python3.7+") def test_dataclasses(self, testdir): p = testdir.copy_example("dataclasses/test_compare_dataclasses.py") @@ -653,10 +632,10 @@ def test_comparing_two_different_data_classes(self, testdir): result.assert_outcomes(failed=0, passed=1) -class TestAssert_reprcompare_attrsclass(object): +class TestAssert_reprcompare_attrsclass: def test_attrs(self): @attr.s - class SimpleDataObject(object): + class SimpleDataObject: field_a = attr.ib() field_b = attr.ib() @@ -671,7 +650,7 @@ class SimpleDataObject(object): def test_attrs_verbose(self): @attr.s - class SimpleDataObject(object): + class SimpleDataObject: field_a = attr.ib() field_b = attr.ib() @@ -685,7 +664,7 @@ class SimpleDataObject(object): def test_attrs_with_attribute_comparison_off(self): @attr.s - class SimpleDataObject(object): + class SimpleDataObject: field_a = attr.ib() field_b = attr.ib(cmp=False) @@ -701,12 +680,12 @@ class SimpleDataObject(object): def test_comparing_two_different_attrs_classes(self): @attr.s - class SimpleDataObjectOne(object): + class SimpleDataObjectOne: field_a = attr.ib() field_b = attr.ib() @attr.s - class SimpleDataObjectTwo(object): + class SimpleDataObjectTwo: field_a = attr.ib() field_b = attr.ib() @@ -717,7 +696,7 @@ class SimpleDataObjectTwo(object): assert lines is None -class TestFormatExplanation(object): +class TestFormatExplanation: def test_special_chars_full(self, testdir): # Issue 453, for the bug this would raise IndexError testdir.makepyfile( @@ -804,7 +783,7 @@ def test_fmt_multi_newline_before_where(self): assert util.format_explanation(expl) == res -class TestTruncateExplanation(object): +class TestTruncateExplanation: """ Confirm assertion output is truncated as expected """ @@ -1100,10 +1079,6 @@ def test_onefails(): ) -@pytest.mark.skipif( - sys.version_info[:2] <= (3, 3), - reason="Python 3.4+ shows chained exceptions on multiprocess", -) def test_exception_handling_no_traceback(testdir): """ Handle chain exceptions in tasks submitted by the multiprocess module (#1984). @@ -1137,9 +1112,7 @@ def test_multitask_job(): ) -@pytest.mark.skipif( - "'__pypy__' in sys.builtin_module_names or sys.platform.startswith('java')" -) +@pytest.mark.skipif("'__pypy__' in sys.builtin_module_names") def test_warn_missing(testdir): testdir.makepyfile("") result = testdir.run(sys.executable, "-OO", "-m", "pytest", "-h") @@ -1187,45 +1160,6 @@ def test_hello(): ) -@pytest.mark.skipif(PY3, reason="This bug does not exist on PY3") -def test_set_with_unsortable_elements(): - # issue #718 - class UnsortableKey(object): - def __init__(self, name): - self.name = name - - def __lt__(self, other): - raise RuntimeError() - - def __repr__(self): - return "repr({})".format(self.name) - - def __eq__(self, other): - return self.name == other.name - - def __hash__(self): - return hash(self.name) - - left_set = {UnsortableKey(str(i)) for i in range(1, 3)} - right_set = {UnsortableKey(str(i)) for i in range(2, 4)} - expl = callequal(left_set, right_set, verbose=True) - # skip first line because it contains the "construction" of the set, which does not have a guaranteed order - expl = expl[1:] - dedent = textwrap.dedent( - """ - Extra items in the left set: - repr(1) - Extra items in the right set: - repr(3) - Full diff (fallback to calling repr on each item): - - repr(1) - repr(2) - + repr(3) - """ - ).strip() - assert "\n".join(expl) == dedent - - def test_diff_newline_at_end(monkeypatch, testdir): testdir.makepyfile( r""" @@ -1283,11 +1217,10 @@ def test_tuple(): def test_assert_with_unicode(monkeypatch, testdir): testdir.makepyfile( - u""" - # -*- coding: utf-8 -*- + """\ def test_unicode(): assert u'유니코드' == u'Unicode' - """ + """ ) result = testdir.runpytest() result.stdout.fnmatch_lines(["*AssertionError*"]) @@ -1308,7 +1241,7 @@ def test_raise_assertion_error(): def test_raise_assertion_error_raisin_repr(testdir): testdir.makepyfile( - u""" + """ class RaisingRepr(object): def __repr__(self): raise Exception() diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index 19d050f8769..6720a790f50 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -1,8 +1,4 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - +import ast import glob import os import py_compile @@ -12,7 +8,6 @@ import zipfile import py -import six import _pytest._code import pytest @@ -22,11 +17,6 @@ from _pytest.assertion.rewrite import rewrite_asserts from _pytest.main import EXIT_NOTESTSCOLLECTED -ast = pytest.importorskip("ast") -if sys.platform.startswith("java"): - # XXX should be xfail - pytest.skip("assert rewrite does currently not work on jython") - def setup_module(mod): mod._old_reprcompare = util._reprcompare @@ -59,7 +49,7 @@ def getmsg(f, extra_ns=None, must_pass=False): except AssertionError: if must_pass: pytest.fail("shouldn't have raised") - s = six.text_type(sys.exc_info()[1]) + s = str(sys.exc_info()[1]) if not s.startswith("assert"): return "AssertionError: " + s return s @@ -68,7 +58,7 @@ def getmsg(f, extra_ns=None, must_pass=False): pytest.fail("function didn't raise at all") -class TestAssertionRewrite(object): +class TestAssertionRewrite: def test_place_initial_imports(self): s = """'Doc string'\nother = stuff""" m = rewrite(s) @@ -161,28 +151,22 @@ def f(): def f(): assert cls == 42 # noqa: F821 - class X(object): + class X: pass msg = getmsg(f, {"cls": X}).splitlines() if verbose > 0: - if six.PY2: - assert msg == [ - "assert == 42", - " -", - " +42", - ] - else: - assert msg == [ - "assert .X'> == 42", - " -.X'>", - " +42", - ] + + assert msg == [ + "assert .X'> == 42", + " -.X'>", + " +42", + ] else: assert msg == ["assert cls == 42"] def test_dont_rewrite_if_hasattr_fails(self, request): - class Y(object): + class Y: """ A class whos getattr fails, but not with `AttributeError` """ def __getattr__(self, attribute_name): @@ -277,9 +261,6 @@ def test_foo(): ["*AssertionError: To be escaped: %", "*assert 1 == 2"] ) - @pytest.mark.skipif( - sys.version_info < (3,), reason="bytes is a string type in python 2" - ) def test_assertion_messages_bytes(self, testdir): testdir.makepyfile("def test_bytes_assertion():\n assert False, b'ohai!'\n") result = testdir.runpytest() @@ -426,7 +407,6 @@ def f(): assert getmsg(f) == "assert (False or (4 % 2))" - @pytest.mark.skipif("sys.version_info < (3,5)") def test_at_operator_issue1290(self, testdir): testdir.makepyfile( """ @@ -441,7 +421,6 @@ def test_multmat_operator(): ) testdir.runpytest().assert_outcomes(passed=1) - @pytest.mark.skipif("sys.version_info < (3,5)") def test_starred_with_side_effect(self, testdir): """See #4412""" testdir.makepyfile( @@ -526,7 +505,7 @@ def f(): ) def test_attribute(self): - class X(object): + class X: g = 3 ns = {"x": X} @@ -616,7 +595,7 @@ def f(): def test_assert_raising_nonzero_in_comparison(self): def f(): - class A(object): + class A: def __nonzero__(self): raise ValueError(42) @@ -641,7 +620,7 @@ def f(): def test_custom_repr(self, request): def f(): - class Foo(object): + class Foo: a = 1 def __repr__(self): @@ -664,8 +643,8 @@ def f(): def test_custom_repr_non_ascii(self): def f(): - class A(object): - name = u"ä" + class A: + name = "ä" def __repr__(self): return self.name.encode("UTF-8") # only legal in python2 @@ -677,7 +656,7 @@ def __repr__(self): assert "UnicodeDecodeError" not in msg assert "UnicodeEncodeError" not in msg - def test_unroll_generator(self, testdir): + def test_unroll_all_generator(self, testdir): testdir.makepyfile( """ def check_even(num): @@ -692,7 +671,7 @@ def test_generator(): result = testdir.runpytest() result.stdout.fnmatch_lines(["*assert False*", "*where False = check_even(1)*"]) - def test_unroll_list_comprehension(self, testdir): + def test_unroll_all_list_comprehension(self, testdir): testdir.makepyfile( """ def check_even(num): @@ -707,6 +686,31 @@ def test_list_comprehension(): result = testdir.runpytest() result.stdout.fnmatch_lines(["*assert False*", "*where False = check_even(1)*"]) + def test_unroll_all_object(self, testdir): + """all() for non generators/non list-comprehensions (#5358)""" + testdir.makepyfile( + """ + def test(): + assert all((1, 0)) + """ + ) + result = testdir.runpytest() + result.stdout.fnmatch_lines(["*assert False*", "*where False = all((1, 0))*"]) + + def test_unroll_all_starred(self, testdir): + """all() for non generators/non list-comprehensions (#5358)""" + testdir.makepyfile( + """ + def test(): + x = ((1, 0),) + assert all(*x) + """ + ) + result = testdir.runpytest() + result.stdout.fnmatch_lines( + ["*assert False*", "*where False = all(*((1, 0),))*"] + ) + def test_for_loop(self, testdir): testdir.makepyfile( """ @@ -725,7 +729,7 @@ def test_for_loop(): result.stdout.fnmatch_lines(["*assert False*", "*where False = check_even(1)*"]) -class TestRewriteOnImport(object): +class TestRewriteOnImport: def test_pycache_is_a_file(self, testdir): testdir.tmpdir.join("__pycache__").write("Hello") testdir.makepyfile( @@ -797,9 +801,6 @@ def test_no_bytecode(): assert testdir.runpytest_subprocess().ret == 0 def test_orphaned_pyc_file(self, testdir): - if sys.version_info < (3, 0) and hasattr(sys, "pypy_version_info"): - pytest.skip("pypy2 doesn't run orphaned pyc files") - testdir.makepyfile( """ import orphan @@ -865,10 +866,6 @@ def test_translate_newlines(self, testdir): testdir.tmpdir.join("test_newlines.py").write(b, "wb") assert testdir.runpytest().ret == 0 - @pytest.mark.skipif( - sys.version_info < (3, 4), - reason="packages without __init__.py not supported on python 2", - ) def test_package_without__init__py(self, testdir): pkg = testdir.mkdir("a_package_without_init_py") pkg.join("module.py").ensure() @@ -951,28 +948,8 @@ def test(): result.stdout.fnmatch_lines(["*= 1 passed in *=*"]) assert "pytest-warning summary" not in result.stdout.str() - @pytest.mark.skipif(sys.version_info[0] > 2, reason="python 2 only") - def test_rewrite_future_imports(self, testdir): - """Test that rewritten modules don't inherit the __future__ flags - from the assertrewrite module. - - assertion.rewrite imports __future__.division (and others), so - ensure rewritten modules don't inherit those flags. - The test below will fail if __future__.division is enabled - """ - testdir.makepyfile( - """ - def test(): - x = 1 / 2 - assert type(x) is int - """ - ) - result = testdir.runpytest() - assert result.ret == 0 - - -class TestAssertionRewriteHookDetails(object): +class TestAssertionRewriteHookDetails: def test_loader_is_package_false_for_module(self, testdir): testdir.makepyfile( test_fun=""" @@ -1000,48 +977,6 @@ def test_missing(): result = testdir.runpytest() result.stdout.fnmatch_lines(["* 3 passed*"]) - @pytest.mark.skipif("sys.version_info[0] >= 3") - @pytest.mark.xfail("hasattr(sys, 'pypy_translation_info')") - def test_assume_ascii(self, testdir): - content = "u'\xe2\x99\xa5\x01\xfe'" - testdir.tmpdir.join("test_encoding.py").write(content, "wb") - res = testdir.runpytest() - assert res.ret != 0 - assert "SyntaxError: Non-ASCII character" in res.stdout.str() - - @pytest.mark.skipif("sys.version_info[0] >= 3") - def test_detect_coding_cookie(self, testdir): - testdir.makepyfile( - test_cookie=""" - # -*- coding: utf-8 -*- - u"St\xc3\xa4d" - def test_rewritten(): - assert "@py_builtins" in globals()""" - ) - assert testdir.runpytest().ret == 0 - - @pytest.mark.skipif("sys.version_info[0] >= 3") - def test_detect_coding_cookie_second_line(self, testdir): - testdir.makepyfile( - test_cookie=""" - # -*- coding: utf-8 -*- - u"St\xc3\xa4d" - def test_rewritten(): - assert "@py_builtins" in globals()""" - ) - assert testdir.runpytest().ret == 0 - - @pytest.mark.skipif("sys.version_info[0] >= 3") - def test_detect_coding_cookie_crlf(self, testdir): - testdir.makepyfile( - test_cookie=""" - # -*- coding: utf-8 -*- - u"St\xc3\xa4d" - def test_rewritten(): - assert "@py_builtins" in globals()""" - ) - assert testdir.runpytest().ret == 0 - def test_sys_meta_path_munged(self, testdir): testdir.makepyfile( """ @@ -1233,7 +1168,7 @@ def test_long_repr(): assert "unbalanced braces" not in result.stdout.str() -class TestIssue925(object): +class TestIssue925: def test_simple_case(self, testdir): testdir.makepyfile( """ @@ -1340,7 +1275,7 @@ def spy_write_pyc(*args, **kwargs): assert len(write_pyc_called) == 1 -class TestEarlyRewriteBailout(object): +class TestEarlyRewriteBailout: @pytest.fixture def hook(self, pytestconfig, monkeypatch, testdir): """Returns a patched AssertionRewritingHook instance so we can configure its initial paths and track @@ -1351,7 +1286,7 @@ def hook(self, pytestconfig, monkeypatch, testdir): self.find_module_calls = [] self.initial_paths = set() - class StubSession(object): + class StubSession: _initialpaths = self.initial_paths def isinitpath(self, p): diff --git a/testing/test_cacheprovider.py b/testing/test_cacheprovider.py index 6de0b312929..a2e701740a0 100644 --- a/testing/test_cacheprovider.py +++ b/testing/test_cacheprovider.py @@ -1,8 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import os import shutil import sys @@ -16,7 +11,7 @@ pytest_plugins = ("pytester",) -class TestNewAPI(object): +class TestNewAPI: def test_config_cache_makedir(self, testdir): testdir.makeini("[pytest]") config = testdir.parseconfigure() @@ -241,7 +236,7 @@ def pytest_configure(config): assert result.ret == 0 -class TestLastFailed(object): +class TestLastFailed: def test_lastfailed_usecase(self, testdir, monkeypatch): monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", "1") p = testdir.makepyfile( @@ -875,7 +870,7 @@ def test_lastfailed_with_known_failures_not_being_selected(self, testdir): ) -class TestNewFirst(object): +class TestNewFirst: def test_newfirst_usecase(self, testdir): testdir.makepyfile( **{ @@ -1000,7 +995,7 @@ def test_1(num): assert num ) -class TestReadme(object): +class TestReadme: def check_readme(self, testdir): config = testdir.parseconfigure() readme = config.cache._cachedir.joinpath("README.md") @@ -1039,7 +1034,7 @@ def test_gitignore(testdir): assert gitignore_path.read_text(encoding="UTF-8") == msg # Does not overwrite existing/custom one. - gitignore_path.write_text(u"custom") + gitignore_path.write_text("custom") cache.set("something", "else") assert gitignore_path.read_text(encoding="UTF-8") == "custom" diff --git a/testing/test_capture.py b/testing/test_capture.py index 01123bf5b40..0825745ad39 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -1,8 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import contextlib import io import os @@ -13,12 +8,10 @@ from io import UnsupportedOperation import py -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 @@ -38,7 +31,7 @@ def StdCapture(out=True, err=True, in_=True): return capture.MultiCapture(out, err, in_, Capture=capture.SysCapture) -class TestCaptureManager(object): +class TestCaptureManager: def test_getmethod_default_no_fd(self, monkeypatch): from _pytest.capture import pytest_addoption from _pytest.config.argparsing import Parser @@ -100,19 +93,15 @@ def test_init_capturing(self): def test_capturing_unicode(testdir, method): if hasattr(sys, "pypy_version_info") and sys.pypy_version_info < (2, 2): pytest.xfail("does not work on pypy < 2.2") - if sys.version_info >= (3, 0): - obj = "'b\u00f6y'" - else: - obj = "u'\u00f6y'" + obj = "'b\u00f6y'" testdir.makepyfile( - """ - # -*- coding: utf-8 -*- + """\ # taken from issue 227 from nosetests def test_unicode(): import sys print(sys.stdout) print(%s) - """ + """ % obj ) result = testdir.runpytest("--capture=%s" % method) @@ -152,7 +141,7 @@ def test_collect_capturing(testdir): ) -class TestPerTestCapturing(object): +class TestPerTestCapturing: def test_capture_and_fixtures(self, testdir): p = testdir.makepyfile( """ @@ -295,7 +284,7 @@ def test_capturing_error(): ) -class TestLoggingInteraction(object): +class TestLoggingInteraction: def test_logging_stream_ownership(self, testdir): p = testdir.makepyfile( """\ @@ -433,7 +422,7 @@ def test_hello(log_on_teardown): ) -class TestCaptureFixture(object): +class TestCaptureFixture: @pytest.mark.parametrize("opt", [[], ["-s"]]) def test_std_functional(self, testdir, opt): reprec = testdir.inline_runsource( @@ -545,9 +534,6 @@ def test_hello(capfdbinary): ) reprec.assertoutcome(passed=1) - @pytest.mark.skipif( - sys.version_info < (3,), reason="only have capsysbinary in python 3" - ) def test_capsysbinary(self, testdir): reprec = testdir.inline_runsource( """\ @@ -562,25 +548,6 @@ def test_hello(capsysbinary): ) reprec.assertoutcome(passed=1) - @pytest.mark.skipif( - sys.version_info >= (3,), reason="only have capsysbinary in python 3" - ) - def test_capsysbinary_forbidden_in_python2(self, testdir): - testdir.makepyfile( - """\ - def test_hello(capsysbinary): - pass - """ - ) - result = testdir.runpytest() - result.stdout.fnmatch_lines( - [ - "*test_hello*", - "*capsysbinary is only supported on Python 3*", - "*1 error in*", - ] - ) - def test_partial_setup_failure(self, testdir): p = testdir.makepyfile( """\ @@ -656,7 +623,6 @@ def test_fixture_use_by_other_fixtures(self, testdir, fixture): """ testdir.makepyfile( """\ - from __future__ import print_function import sys import pytest @@ -833,7 +799,7 @@ def bad_snap(self): ) -class TestCaptureIO(object): +class TestCaptureIO: def test_text(self): f = capture.CaptureIO() f.write("hello") @@ -843,17 +809,9 @@ def test_text(self): def test_unicode_and_str_mixture(self): f = capture.CaptureIO() - if sys.version_info >= (3, 0): - f.write("\u00f6") - pytest.raises(TypeError, f.write, b"hello") - else: - f.write(u"\u00f6") - f.write(b"hello") - s = f.getvalue() - f.close() - assert isinstance(s, text_type) + f.write("\u00f6") + pytest.raises(TypeError, f.write, b"hello") - @pytest.mark.skipif(sys.version_info[0] == 2, reason="python 3 only behaviour") def test_write_bytes_to_buffer(self): """In python3, stdout / stderr are text io wrappers (exposing a buffer property of the underlying bytestream). See issue #1407 @@ -876,7 +834,6 @@ def test_dontreadfrominput(): f.close() # just for completeness -@pytest.mark.skipif("sys.version_info < (3,)", reason="python2 has no buffer") def test_dontreadfrominput_buffer_python3(): from _pytest.capture import DontReadFromInput @@ -891,17 +848,7 @@ def test_dontreadfrominput_buffer_python3(): f.close() # just for completeness -@pytest.mark.skipif("sys.version_info >= (3,)", reason="python2 has no buffer") -def test_dontreadfrominput_buffer_python2(): - from _pytest.capture import DontReadFromInput - - f = DontReadFromInput() - with pytest.raises(AttributeError): - f.buffer - f.close() # just for completeness - - -@pytest.yield_fixture +@pytest.fixture def tmpfile(testdir): f = testdir.makepyfile("").open("wb+") yield f @@ -967,7 +914,7 @@ def lsof_check(): assert len2 < len1 + 3, out2 -class TestFDCapture(object): +class TestFDCapture: pytestmark = needsosdup def test_simple(self, tmpfile): @@ -1065,7 +1012,7 @@ def saved_fd(fd): os.close(new_fd) -class TestStdCapture(object): +class TestStdCapture: captureclass = staticmethod(StdCapture) @contextlib.contextmanager @@ -1116,17 +1063,7 @@ def test_capturing_readouterr_unicode(self): with self.getcapture() as cap: print("hxąć") out, err = cap.readouterr() - assert out == u"hxąć\n" - - @pytest.mark.skipif( - "sys.version_info >= (3,)", reason="text output different for bytes on python3" - ) - def test_capturing_readouterr_decode_error_handling(self): - with self.getcapture() as cap: - # triggered an internal error in pytest - print("\xa6") - out, err = cap.readouterr() - assert out == u"\ufffd\n" + assert out == "hxąć\n" def test_reset_twice_error(self): with self.getcapture() as cap: @@ -1236,7 +1173,7 @@ def test_many(self, capfd): cap.stop_capturing() -class TestStdCaptureFDinvalidFD(object): +class TestStdCaptureFDinvalidFD: pytestmark = needsosdup def test_stdcapture_fd_invalid_fd(self, testdir): @@ -1399,7 +1336,7 @@ def test_py36_windowsconsoleio_workaround_non_standard_streams(): """ from _pytest.capture import _py36_windowsconsoleio_workaround - class DummyStream(object): + class DummyStream: def write(self, s): pass @@ -1424,7 +1361,6 @@ def test_capattr(): def test_crash_on_closing_tmpfile_py27(testdir): p = testdir.makepyfile( """ - from __future__ import print_function import threading import sys @@ -1571,9 +1507,6 @@ def test_fails(): 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 + result_with_capture.stdout.fnmatch_lines( + ["E TypeError: write() argument must be str, not bytes"] + ) diff --git a/testing/test_collection.py b/testing/test_collection.py index 1cc8938666f..c938a915d1d 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -1,8 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import os import pprint import sys @@ -17,7 +12,7 @@ from _pytest.main import Session -class TestCollector(object): +class TestCollector: def test_collect_versus_item(self): from pytest import Collector, Item @@ -38,8 +33,6 @@ def test_fail(): assert 0 assert fn1 == fn2 assert fn1 != modcol - if sys.version_info < (3, 0): - assert cmp(fn1, fn2) == 0 # NOQA assert hash(fn1) == hash(fn2) fn3 = testdir.collect_by_name(modcol, "test_fail") @@ -109,7 +102,7 @@ def test_foo(): result.stdout.fnmatch_lines(["collected 0 items", "*no tests ran in*"]) -class TestCollectFS(object): +class TestCollectFS: def test_ignored_certain_directories(self, testdir): tmpdir = testdir.tmpdir tmpdir.ensure("build", "test_notfound.py") @@ -246,11 +239,11 @@ def test_testpaths_ini(self, testdir, monkeypatch): assert [x.name for x in items] == ["test_%s" % dirname] -class TestCollectPluginHookRelay(object): +class TestCollectPluginHookRelay: def test_pytest_collect_file(self, testdir): wascalled = [] - class Plugin(object): + class Plugin: def pytest_collect_file(self, path, parent): if not path.basename.startswith("."): # Ignore hidden files, e.g. .testmondata. @@ -264,7 +257,7 @@ def pytest_collect_file(self, path, parent): def test_pytest_collect_directory(self, testdir): wascalled = [] - class Plugin(object): + class Plugin: def pytest_collect_directory(self, path, parent): wascalled.append(path.basename) @@ -275,7 +268,7 @@ def pytest_collect_directory(self, path, parent): assert "world" in wascalled -class TestPrunetraceback(object): +class TestPrunetraceback: def test_custom_repr_failure(self, testdir): p = testdir.makepyfile( """ @@ -324,7 +317,7 @@ def pytest_make_collect_report(): result.stdout.fnmatch_lines(["*ERROR collecting*", "*header1*"]) -class TestCustomConftests(object): +class TestCustomConftests: def test_ignore_collect_path(self, testdir): testdir.makeconftest( """ @@ -445,7 +438,7 @@ def pytest_collect_file(path, parent): result.stdout.fnmatch_lines(["*MyModule1*", "*MyModule2*", "*test_x*"]) -class TestSession(object): +class TestSession: def test_parsearg(self, testdir): p = testdir.makepyfile("def test_func(): pass") subdir = testdir.mkdir("sub") @@ -636,7 +629,7 @@ def test_method(self): assert [x.name for x in self.get_reported_items(hookrec)] == ["test_method"] -class Test_getinitialnodes(object): +class Test_getinitialnodes: def test_global_file(self, testdir, tmpdir): x = tmpdir.ensure("x.py") with tmpdir.as_cwd(): @@ -670,7 +663,7 @@ def test_pkgfile(self, testdir): assert col.config is config -class Test_genitems(object): +class Test_genitems: def test_check_collect_hashes(self, testdir): p = testdir.makepyfile( """ @@ -783,7 +776,7 @@ def runtest(self): res.stdout.fnmatch_lines(["*1 passed*"]) -class TestNodekeywords(object): +class TestNodekeywords: def test_no_under(self, testdir): modcol = testdir.getmodulecol( """ diff --git a/testing/test_compat.py b/testing/test_compat.py index b4298d1531d..028d48bed30 100644 --- a/testing/test_compat.py +++ b/testing/test_compat.py @@ -1,13 +1,6 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import sys from functools import wraps -import six - import pytest from _pytest.compat import _PytestWrapper from _pytest.compat import get_real_func @@ -29,7 +22,7 @@ def foo(): def test_real_func_loop_limit(): - class Evil(object): + class Evil: def __init__(self): self.left = 1000 @@ -62,8 +55,6 @@ def decorator(f): def inner(): pass # pragma: no cover - if six.PY2: - inner.__wrapped__ = f return inner def func(): @@ -81,9 +72,6 @@ def func(): assert get_real_func(wrapped_func2) is wrapped_func -@pytest.mark.skipif( - sys.version_info < (3, 4), reason="asyncio available in Python 3.4+" -) def test_is_generator_asyncio(testdir): testdir.makepyfile( """ @@ -125,7 +113,7 @@ async def bar(): result.stdout.fnmatch_lines(["*1 passed*"]) -class ErrorsHelper(object): +class ErrorsHelper: @property def raise_exception(self): raise Exception("exception should be catched") diff --git a/testing/test_config.py b/testing/test_config.py index e588c262a9c..7b2a1209eaf 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -1,8 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import sys import textwrap @@ -21,7 +16,7 @@ from _pytest.main import EXIT_USAGEERROR -class TestParseIni(object): +class TestParseIni: @pytest.mark.parametrize( "section, filename", [("pytest", "pytest.ini"), ("tool:pytest", "setup.cfg")] ) @@ -144,7 +139,7 @@ def test_confcutdir(self, testdir): assert result.ret == 0 -class TestConfigCmdlineParsing(object): +class TestConfigCmdlineParsing: def test_parsing_again_fails(self, testdir): config = testdir.parseconfig() pytest.raises(AssertionError, lambda: config.parse([])) @@ -197,7 +192,7 @@ def test_absolute_win32_path(self, testdir): assert ret == _pytest.main.EXIT_OK -class TestConfigAPI(object): +class TestConfigAPI: def test_config_trace(self, testdir): config = testdir.parseconfig() values = [] @@ -218,12 +213,9 @@ def pytest_addoption(parser): assert config.getoption(x) == "this" pytest.raises(ValueError, config.getoption, "qweqwe") - @pytest.mark.skipif("sys.version_info[0] < 3") def test_config_getoption_unicode(self, testdir): testdir.makeconftest( """ - from __future__ import unicode_literals - def pytest_addoption(parser): parser.addoption('--hello', type=str) """ @@ -436,7 +428,7 @@ def test_iter_rewritable_modules(self, names, expected): assert list(_iter_rewritable_modules(["/".join(names)])) == expected -class TestConfigFromdictargs(object): +class TestConfigFromdictargs: def test_basic_behavior(self, _sys_snapshot): from _pytest.config import Config @@ -534,17 +526,17 @@ def test_f2(): assert 0 def test_preparse_ordering_with_setuptools(testdir, monkeypatch): monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD", raising=False) - class EntryPoint(object): + class EntryPoint: name = "mytestplugin" group = "pytest11" def load(self): - class PseudoPlugin(object): + class PseudoPlugin: x = 42 return PseudoPlugin() - class Dist(object): + class Dist: files = () entry_points = (EntryPoint(),) @@ -566,14 +558,14 @@ def my_dists(): def test_setuptools_importerror_issue1479(testdir, monkeypatch): monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD", raising=False) - class DummyEntryPoint(object): + class DummyEntryPoint: name = "mytestplugin" group = "pytest11" def load(self): raise ImportError("Don't hide me!") - class Distribution(object): + class Distribution: version = "1.0" files = ("foo.txt",) entry_points = (DummyEntryPoint(),) @@ -592,14 +584,14 @@ def test_plugin_preparse_prevents_setuptools_loading(testdir, monkeypatch, block plugin_module_placeholder = object() - class DummyEntryPoint(object): + class DummyEntryPoint: name = "mytestplugin" group = "pytest11" def load(self): return plugin_module_placeholder - class Distribution(object): + class Distribution: version = "1.0" files = ("foo.txt",) entry_points = (DummyEntryPoint(),) @@ -624,7 +616,7 @@ def distributions(): "parse_args,should_load", [(("-p", "mytestplugin"), True), ((), False)] ) def test_disable_plugin_autoload(testdir, monkeypatch, parse_args, should_load): - class DummyEntryPoint(object): + class DummyEntryPoint: project_name = name = "mytestplugin" group = "pytest11" version = "1.0" @@ -632,11 +624,11 @@ class DummyEntryPoint(object): def load(self): return sys.modules[self.name] - class Distribution(object): + class Distribution: entry_points = (DummyEntryPoint(),) files = () - class PseudoPlugin(object): + class PseudoPlugin: x = 42 def distributions(): @@ -748,7 +740,7 @@ def test_notify_exception(testdir, capfd): out, err = capfd.readouterr() assert "ValueError" in err - class A(object): + class A: def pytest_internalerror(self, excrepr): return True @@ -768,7 +760,7 @@ def pytest_internalerror(self, excrepr): def test_load_initial_conftest_last_ordering(testdir, _config_for_test): pm = _config_for_test.pluginmanager - class My(object): + class My: def pytest_load_initial_conftests(self): pass @@ -806,15 +798,15 @@ def test_collect_pytest_prefix_bug_integration(testdir): def test_collect_pytest_prefix_bug(pytestconfig): """Ensure we collect only actual functions from conftest files (#3775)""" - class Dummy(object): - class pytest_something(object): + class Dummy: + class pytest_something: pass pm = pytestconfig.pluginmanager assert pm.parse_hookimpl_opts(Dummy(), "pytest_something") is None -class TestRootdir(object): +class TestRootdir: def test_simple_noini(self, tmpdir): assert get_common_ancestor([tmpdir]) == tmpdir a = tmpdir.mkdir("a") @@ -872,7 +864,7 @@ def test_with_specific_inifile(self, tmpdir): assert rootdir == tmpdir -class TestOverrideIniArgs(object): +class TestOverrideIniArgs: @pytest.mark.parametrize("name", "setup.cfg tox.ini pytest.ini".split()) def test_override_ini_names(self, testdir, name): section = "[pytest]" if name != "setup.cfg" else "[tool:pytest]" diff --git a/testing/test_conftest.py b/testing/test_conftest.py index f8e3dfa92e4..f29531eb7bd 100644 --- a/testing/test_conftest.py +++ b/testing/test_conftest.py @@ -1,8 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import textwrap import py @@ -21,7 +16,7 @@ def ConftestWithSetinitial(path): def conftest_setinitial(conftest, args, confcutdir=None): - class Namespace(object): + class Namespace: def __init__(self): self.file_or_dir = args self.confcutdir = str(confcutdir) @@ -32,7 +27,7 @@ def __init__(self): @pytest.mark.usefixtures("_sys_snapshot") -class TestConftestValueAccessGlobal(object): +class TestConftestValueAccessGlobal: @pytest.fixture(scope="module", params=["global", "inpackage"]) def basedir(self, request, tmpdir_factory): tmpdir = tmpdir_factory.mktemp("basedir", numbered=True) @@ -401,7 +396,7 @@ def pytest_addoption(parser): ) -class TestConftestVisibility(object): +class TestConftestVisibility: def _setup_tree(self, testdir): # for issue616 # example mostly taken from: # https://mail.python.org/pipermail/pytest-dev/2014-September/002617.html diff --git a/testing/test_doctest.py b/testing/test_doctest.py index 25a35c3c136..65c8cf3664f 100644 --- a/testing/test_doctest.py +++ b/testing/test_doctest.py @@ -1,9 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import sys import textwrap import pytest @@ -13,7 +7,7 @@ from _pytest.doctest import DoctestTextfile -class TestDoctests(object): +class TestDoctests: def test_collect_testtextfile(self, testdir): w = testdir.maketxtfile(whatever="") checkfile = testdir.maketxtfile( @@ -145,7 +139,7 @@ def test_multiple_patterns(self, testdir): @pytest.mark.parametrize( " test_string, encoding", - [(u"foo", "ascii"), (u"öäü", "latin1"), (u"öäü", "utf-8")], + [("foo", "ascii"), ("öäü", "latin1"), ("öäü", "utf-8")], ) def test_encoding(self, testdir, test_string, encoding): """Test support for doctest_encoding ini option. @@ -158,7 +152,7 @@ def test_encoding(self, testdir, test_string, encoding): encoding ) ) - doctest = u""" + doctest = """ >>> u"{}" {} """.format( @@ -580,14 +574,13 @@ def test_contains_unicode(self, testdir): """Fix internal error with docstrings containing non-ascii characters. """ testdir.makepyfile( - u''' - # -*- coding: utf-8 -*- + '''\ def foo(): """ >>> name = 'с' # not letter 'c' but instead Cyrillic 's'. 'anything' """ - ''' + ''' ) result = testdir.runpytest("--doctest-modules") result.stdout.fnmatch_lines(["Got nothing", "* 1 failed in*"]) @@ -658,9 +651,6 @@ def test_unicode_doctest_module(self, testdir): """ p = testdir.makepyfile( test_unicode_doctest_module=""" - # -*- coding: utf-8 -*- - from __future__ import unicode_literals - def fix_bad_unicode(text): ''' >>> print(fix_bad_unicode('único')) @@ -739,7 +729,7 @@ def test_foo(): result.stdout.fnmatch_lines(["*collected 1 item*"]) -class TestLiterals(object): +class TestLiterals: @pytest.mark.parametrize("config_mode", ["ini", "comment"]) def test_allow_unicode(self, testdir, config_mode): """Test that doctests which output unicode work in all python versions @@ -830,13 +820,11 @@ def test_unicode_string(self, testdir): """ ) reprec = testdir.inline_run() - passed = int(sys.version_info[0] >= 3) - reprec.assertoutcome(passed=passed, failed=int(not passed)) + reprec.assertoutcome(passed=1) def test_bytes_literal(self, testdir): """Test that doctests which output bytes fail in Python 3 when - the ALLOW_BYTES option is not used. The same test should pass - in Python 2 (#1287). + the ALLOW_BYTES option is not used. (#1287). """ testdir.maketxtfile( test_doc=""" @@ -845,11 +833,10 @@ def test_bytes_literal(self, testdir): """ ) reprec = testdir.inline_run() - passed = int(sys.version_info[0] == 2) - reprec.assertoutcome(passed=passed, failed=int(not passed)) + reprec.assertoutcome(failed=1) -class TestDoctestSkips(object): +class TestDoctestSkips: """ If all examples in a doctest are skipped due to the SKIP option, then the tests should be SKIPPED rather than PASSED. (#957) @@ -930,7 +917,7 @@ def test_continue_on_failure(self, testdir): ) -class TestDoctestAutoUseFixtures(object): +class TestDoctestAutoUseFixtures: SCOPES = ["module", "session", "class", "function"] @@ -1074,7 +1061,7 @@ def auto(request): result.stdout.fnmatch_lines(["*=== 1 passed in *"]) -class TestDoctestNamespaceFixture(object): +class TestDoctestNamespaceFixture: SCOPES = ["module", "session", "class", "function"] @@ -1136,7 +1123,7 @@ def foo(): reprec.assertoutcome(passed=1) -class TestDoctestReportingOption(object): +class TestDoctestReportingOption: def _run_doctest_report(self, testdir, format): testdir.makepyfile( """ diff --git a/testing/test_entry_points.py b/testing/test_entry_points.py index ad64d004f1c..9812ce998d9 100644 --- a/testing/test_entry_points.py +++ b/testing/test_entry_points.py @@ -1,8 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import importlib_metadata diff --git a/testing/test_helpconfig.py b/testing/test_helpconfig.py index 75888fe14cb..ec061cad99d 100644 --- a/testing/test_helpconfig.py +++ b/testing/test_helpconfig.py @@ -1,8 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import pytest from _pytest.main import EXIT_NOTESTSCOLLECTED diff --git a/testing/test_junitxml.py b/testing/test_junitxml.py index 4c21c94d365..69b9c09c3f1 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -1,10 +1,4 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import os -import sys from xml.dom import minidom import py @@ -34,7 +28,7 @@ def nodeval(node, name): assert on_node == expected -class DomNode(object): +class DomNode: def __init__(self, dom): self.__node = dom @@ -85,7 +79,7 @@ def next_siebling(self): return type(self)(self.__node.nextSibling) -class TestPython(object): +class TestPython: def test_summing_simple(self, testdir): testdir.makepyfile( """ @@ -573,20 +567,19 @@ def test_collect_error(self, testdir): def test_unicode(self, testdir): value = "hx\xc4\x85\xc4\x87\n" testdir.makepyfile( - """ + """\ # coding: latin1 def test_hello(): print(%r) assert 0 - """ + """ % value ) result, dom = runandparse(testdir) assert result.ret == 1 tnode = dom.find_first_by_tag("testcase") fnode = tnode.find_first_by_tag("failure") - if not sys.platform.startswith("java"): - assert "hx" in fnode.toxml() + assert "hx" in fnode.toxml() def test_assertion_binchars(self, testdir): """this test did fail when the escaping wasnt strict""" @@ -703,7 +696,7 @@ def test_mangle_test_address(): def test_dont_configure_on_slaves(tmpdir): gotten = [] - class FakeConfig(object): + class FakeConfig: def __init__(self): self.pluginmanager = self self.option = self @@ -726,7 +719,7 @@ def getini(self, name): assert len(gotten) == 1 -class TestNonPython(object): +class TestNonPython: def test_summing_simple(self, testdir): testdir.makeconftest( """ @@ -929,7 +922,7 @@ def test_func(self, param): def test_unicode_issue368(testdir): path = testdir.tmpdir.join("test.xml") log = LogXML(str(path), None) - ustr = u"ВНИ!" + ustr = "ВНИ!" class Report(BaseReport): longrepr = ustr @@ -1173,13 +1166,13 @@ def test_pass(): pprint.pprint(items) assert items == [ - u"conftest a", - u"conftest a", - u"conftest b", - u"test_fancy_items_regression a", - u"test_fancy_items_regression a", - u"test_fancy_items_regression b", - u"test_fancy_items_regression test_pass", + "conftest a", + "conftest a", + "conftest b", + "test_fancy_items_regression a", + "test_fancy_items_regression a", + "test_fancy_items_regression b", + "test_fancy_items_regression test_pass", ] diff --git a/testing/test_mark.py b/testing/test_mark.py index 5bd97d547e8..8d97f8b4ef5 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -1,12 +1,6 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import os import sys - -import six +from unittest import mock import pytest from _pytest.main import EXIT_INTERRUPTED @@ -17,17 +11,12 @@ from _pytest.warning_types import PytestDeprecationWarning from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG -try: - import mock -except ImportError: - import unittest.mock as mock - ignore_markinfo = pytest.mark.filterwarnings( "ignore:MarkInfo objects:pytest.RemovedInPytest4Warning" ) -class TestMark(object): +class TestMark: @pytest.mark.parametrize("attr", ["mark", "param"]) @pytest.mark.parametrize("modulename", ["py.test", "pytest"]) def test_pytest_exists_in_namespace_all(self, attr, modulename): @@ -42,7 +31,7 @@ def test_mark_with_param(self): def some_function(abc): pass - class SomeClass(object): + class SomeClass: pass assert pytest.mark.foo(some_function) is some_function @@ -413,7 +402,29 @@ def test_func(a, b): assert result.ret == 0 -class TestFunctional(object): +def test_parametrize_iterator(testdir): + """parametrize should work with generators (#5354).""" + py_file = testdir.makepyfile( + """\ + import pytest + + def gen(): + yield 1 + yield 2 + yield 3 + + @pytest.mark.parametrize('a', gen()) + def test(a): + assert a >= 1 + """ + ) + result = testdir.runpytest(py_file) + assert result.ret == 0 + # should not skip any tests + result.stdout.fnmatch_lines(["*3 passed*"]) + + +class TestFunctional: def test_merging_markers_deep(self, testdir): # issue 199 - propagate markers into nested classes p = testdir.makepyfile( @@ -682,7 +693,7 @@ def test_1(parameter): reprec.assertoutcome(skipped=1) -class TestKeywordSelection(object): +class TestKeywordSelection: def test_select_simple(self, testdir): file_test = testdir.makepyfile( """ @@ -813,7 +824,7 @@ def assert_test_is_not_selected(keyword): assert_test_is_not_selected("()") -class TestMarkDecorator(object): +class TestMarkDecorator: @pytest.mark.parametrize( "lhs, rhs, expected", [ @@ -949,7 +960,6 @@ def test_markers_from_parametrize(testdir): """#3605""" testdir.makepyfile( """ - from __future__ import print_function import pytest first_custom_mark = pytest.mark.custom_marker @@ -987,10 +997,7 @@ def test_pytest_param_id_requires_string(): with pytest.raises(TypeError) as excinfo: pytest.param(id=True) msg, = excinfo.value.args - if six.PY2: - assert msg == "Expected id to be a string, got : True" - else: - assert msg == "Expected id to be a string, got : True" + assert msg == "Expected id to be a string, got : True" @pytest.mark.parametrize("s", (None, "hello world")) diff --git a/testing/test_modimport.py b/testing/test_modimport.py index 5dd057c55e2..3d7a073232c 100644 --- a/testing/test_modimport.py +++ b/testing/test_modimport.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import subprocess import sys diff --git a/testing/test_monkeypatch.py b/testing/test_monkeypatch.py index 9b2f4550240..eee8baf3a69 100644 --- a/testing/test_monkeypatch.py +++ b/testing/test_monkeypatch.py @@ -1,15 +1,8 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import os import re import sys import textwrap -import six - import pytest from _pytest.monkeypatch import MonkeyPatch @@ -24,7 +17,7 @@ def mp(): def test_setattr(): - class A(object): + class A: x = 1 monkeypatch = MonkeyPatch() @@ -47,7 +40,7 @@ class A(object): assert A.x == 5 -class TestSetattrWithImportPath(object): +class TestSetattrWithImportPath: def test_string_expression(self, monkeypatch): monkeypatch.setattr("os.path.abspath", lambda x: "hello2") assert os.path.abspath("123") == "hello2" @@ -89,7 +82,7 @@ def test_delattr(self, monkeypatch): def test_delattr(): - class A(object): + class A: x = 1 monkeypatch = MonkeyPatch() @@ -200,30 +193,14 @@ def test_delenv(): del os.environ[name] -class TestEnvironWarnings(object): +class TestEnvironWarnings: """ os.environ keys and values should be native strings, otherwise it will cause problems with other modules (notably subprocess). On Python 2 os.environ accepts anything without complaining, while Python 3 does the right thing and raises an error. """ - VAR_NAME = u"PYTEST_INTERNAL_MY_VAR" - - @pytest.mark.skipif(six.PY3, reason="Python 2 only test") - def test_setenv_unicode_key(self, monkeypatch): - with pytest.warns( - pytest.PytestWarning, - match="Environment variable name {!r} should be str".format(self.VAR_NAME), - ): - monkeypatch.setenv(self.VAR_NAME, "2") - - @pytest.mark.skipif(six.PY3, reason="Python 2 only test") - def test_delenv_unicode_key(self, monkeypatch): - with pytest.warns( - pytest.PytestWarning, - match="Environment variable name {!r} should be str".format(self.VAR_NAME), - ): - monkeypatch.delenv(self.VAR_NAME, raising=False) + VAR_NAME = "PYTEST_INTERNAL_MY_VAR" def test_setenv_non_str_warning(self, monkeypatch): value = 2 @@ -349,14 +326,12 @@ def test_importerror(monkeypatch): result = testdir.runpytest() result.stdout.fnmatch_lines( """ - *import error in package.a: No module named {0}doesnotexist{0}* - """.format( - "'" if sys.version_info > (3, 0) else "" - ) + *import error in package.a: No module named 'doesnotexist'* + """ ) -class SampleNew(object): +class SampleNew: @staticmethod def hello(): return True @@ -366,7 +341,7 @@ class SampleNewInherit(SampleNew): pass -class SampleOld(object): +class SampleOld: # oldstyle on python2 @staticmethod def hello(): @@ -393,7 +368,7 @@ def test_issue156_undo_staticmethod(Sample): def test_undo_class_descriptors_delattr(): - class SampleParent(object): + class SampleParent: @classmethod def hello(_cls): pass diff --git a/testing/test_nodes.py b/testing/test_nodes.py index e86bd5f793e..b13ce1fe604 100644 --- a/testing/test_nodes.py +++ b/testing/test_nodes.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import py import pytest diff --git a/testing/test_nose.py b/testing/test_nose.py index 6eb5f085e84..8a3ce6454e7 100644 --- a/testing/test_nose.py +++ b/testing/test_nose.py @@ -1,8 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import pytest @@ -36,7 +31,7 @@ def test_setup_func_with_setup_decorator(): values = [] - class A(object): + class A: @pytest.fixture(autouse=True) def f(self): values.append(1) @@ -48,7 +43,7 @@ def f(self): def test_setup_func_not_callable(): from _pytest.nose import call_optional - class A(object): + class A: f = 1 call_optional(A(), "f") @@ -371,13 +366,12 @@ def test_method(self): def test_skip_test_with_unicode(testdir): testdir.makepyfile( - """ - # -*- coding: utf-8 -*- + """\ import unittest class TestClass(): def test_io(self): raise unittest.SkipTest(u'😊') - """ + """ ) result = testdir.runpytest() result.stdout.fnmatch_lines(["* 1 skipped *"]) diff --git a/testing/test_parseopt.py b/testing/test_parseopt.py index c570ed60b2f..7c581cce164 100644 --- a/testing/test_parseopt.py +++ b/testing/test_parseopt.py @@ -1,8 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import argparse import distutils.spawn import os @@ -20,7 +15,7 @@ def parser(): return parseopt.Parser() -class TestParser(object): +class TestParser: def test_no_help_by_default(self): parser = parseopt.Parser(usage="xyz") pytest.raises(UsageError, lambda: parser.parse(["-h"])) @@ -157,7 +152,7 @@ def test_parse_setoption(self, parser): parser.addoption("--hello", dest="hello", action="store") parser.addoption("--world", dest="world", default=42) - class A(object): + class A: pass option = A() diff --git a/testing/test_pastebin.py b/testing/test_pastebin.py index 9491f6d9019..48dea14bdc8 100644 --- a/testing/test_pastebin.py +++ b/testing/test_pastebin.py @@ -1,14 +1,7 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import sys - import pytest -class TestPasteCapture(object): +class TestPasteCapture: @pytest.fixture def pastebinlist(self, monkeypatch, request): pastebinlist = [] @@ -67,17 +60,13 @@ def test_non_ascii_paste_text(self, testdir): correctly. See #1219. """ testdir.makepyfile( - test_unicode=""" - # -*- coding: utf-8 -*- + test_unicode="""\ def test(): assert '☺' == 1 - """ + """ ) result = testdir.runpytest("--pastebin=all") - if sys.version_info[0] == 3: - expected_msg = "*assert '☺' == 1*" - else: - expected_msg = "*assert '\\xe2\\x98\\xba' == 1*" + expected_msg = "*assert '☺' == 1*" result.stdout.fnmatch_lines( [ expected_msg, @@ -87,7 +76,7 @@ def test(): ) -class TestPaste(object): +class TestPaste: @pytest.fixture def pastebin(self, request): return request.config.pluginmanager.getplugin("pastebin") @@ -103,21 +92,16 @@ def mocked_urlopen(self, monkeypatch): def mocked(url, data): calls.append((url, data)) - class DummyFile(object): + class DummyFile: def read(self): # part of html of a normal response return b'View raw.' return DummyFile() - if sys.version_info < (3, 0): - import urllib - - monkeypatch.setattr(urllib, "urlopen", mocked) - else: - import urllib.request + import urllib.request - monkeypatch.setattr(urllib.request, "urlopen", mocked) + monkeypatch.setattr(urllib.request, "urlopen", mocked) return calls def test_create_new_paste(self, pastebin, mocked_urlopen): @@ -126,7 +110,7 @@ def test_create_new_paste(self, pastebin, mocked_urlopen): assert len(mocked_urlopen) == 1 url, data = mocked_urlopen[0] assert type(data) is bytes - lexer = "python3" if sys.version_info[0] == 3 else "python" + lexer = "python3" assert url == "https://bpaste.net" assert "lexer=%s" % lexer in data.decode() assert "code=full-paste-contents" in data.decode() diff --git a/testing/test_pathlib.py b/testing/test_pathlib.py index 541d289f777..8ac4040702a 100644 --- a/testing/test_pathlib.py +++ b/testing/test_pathlib.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import sys import py diff --git a/testing/test_pdb.py b/testing/test_pdb.py index d31d60469cd..f3f7ca70233 100644 --- a/testing/test_pdb.py +++ b/testing/test_pdb.py @@ -1,13 +1,6 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import os import sys -import six - import _pytest._code import pytest from _pytest.debugging import _validate_usepdb_cls @@ -36,7 +29,7 @@ def custom_pdb_calls(): called = [] # install dummy debugger class and track which methods were called on it - class _CustomPdb(object): + class _CustomPdb: quitting = False def __init__(self, *args, **kwargs): @@ -57,7 +50,7 @@ def custom_debugger_hook(): called = [] # install dummy debugger class and track which methods were called on it - class _CustomDebugger(object): + class _CustomDebugger: def __init__(self, *args, **kwargs): called.append("init") @@ -76,7 +69,7 @@ def set_trace(self, frame): del _pytest._CustomDebugger -class TestPDB(object): +class TestPDB: @pytest.fixture def pdblist(self, request): monkeypatch = request.getfixturevalue("monkeypatch") @@ -537,10 +530,7 @@ def do_debug(self, arg): import sys import types - if sys.version_info < (3, ): - do_debug_func = pdb.Pdb.do_debug.im_func - else: - do_debug_func = pdb.Pdb.do_debug + do_debug_func = pdb.Pdb.do_debug newglobals = do_debug_func.__globals__.copy() newglobals['Pdb'] = self.__class__ @@ -676,7 +666,7 @@ def do_continue(self, arg): set_trace() """ ) - child = testdir.spawn_pytest("--tb=short %s %s" % (p1, capture_arg)) + child = testdir.spawn_pytest("--tb=short {} {}".format(p1, capture_arg)) child.expect("=== SET_TRACE ===") before = child.before.decode("utf8") if not capture_arg: @@ -856,7 +846,7 @@ def test_foo(): self.flush(child) -class TestDebuggingBreakpoints(object): +class TestDebuggingBreakpoints: def test_supports_breakpoint_module_global(self): """ Test that supports breakpoint global marks on Python 3.7+ and not on @@ -866,8 +856,6 @@ def test_supports_breakpoint_module_global(self): assert SUPPORTS_BREAKPOINT_BUILTIN is True if sys.version_info.major == 3 and sys.version_info.minor == 5: assert SUPPORTS_BREAKPOINT_BUILTIN is False - if sys.version_info.major == 2 and sys.version_info.minor == 7: - assert SUPPORTS_BREAKPOINT_BUILTIN is False @pytest.mark.skipif( not SUPPORTS_BREAKPOINT_BUILTIN, reason="Requires breakpoint() builtin" @@ -1183,24 +1171,17 @@ def runcall(self, *args, **kwds): def test_raises_bdbquit_with_eoferror(testdir): """It is not guaranteed that DontReadFromInput's read is called.""" - if six.PY2: - builtin_module = "__builtin__" - input_func = "raw_input" - else: - builtin_module = "builtins" - input_func = "input" + p1 = testdir.makepyfile( """ def input_without_read(*args, **kwargs): raise EOFError() def test(monkeypatch): - import {builtin_module} - monkeypatch.setattr({builtin_module}, {input_func!r}, input_without_read) + import builtins + monkeypatch.setattr(builtins, "input", input_without_read) __import__('pdb').set_trace() - """.format( - builtin_module=builtin_module, input_func=input_func - ) + """ ) result = testdir.runpytest(str(p1)) result.stdout.fnmatch_lines(["E *BdbQuit", "*= 1 failed in*"]) diff --git a/testing/test_pluginmanager.py b/testing/test_pluginmanager.py index 23655164343..8afb37fa149 100644 --- a/testing/test_pluginmanager.py +++ b/testing/test_pluginmanager.py @@ -1,8 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import os import sys import types @@ -19,7 +14,7 @@ def pytestpm(): return PytestPluginManager() -class TestPytestPluginInteractions(object): +class TestPytestPluginInteractions: def test_addhooks_conftestplugin(self, testdir, _config_for_test): testdir.makepyfile( newhooks=""" @@ -75,7 +70,7 @@ def test_configure(self, testdir): config = testdir.parseconfig() values = [] - class A(object): + class A: def pytest_configure(self, config): values.append(self) @@ -95,11 +90,11 @@ def test_hook_tracing(self, _config_for_test): pytestpm = _config_for_test.pluginmanager # fully initialized with plugins saveindent = [] - class api1(object): + class api1: def pytest_plugin_registered(self): saveindent.append(pytestpm.trace.root.indent) - class api2(object): + class api2: def pytest_plugin_registered(self): saveindent.append(pytestpm.trace.root.indent) raise ValueError() @@ -154,12 +149,11 @@ def test_importplugin_error_message(testdir, pytestpm): """ testdir.syspathinsert(testdir.tmpdir) testdir.makepyfile( - qwe=""" - # -*- coding: utf-8 -*- + qwe="""\ def test_traceback(): - raise ImportError(u'Not possible to import: ☺') + raise ImportError('Not possible to import: ☺') test_traceback() - """ + """ ) with pytest.raises(ImportError) as excinfo: pytestpm.import_plugin("qwe") @@ -170,7 +164,7 @@ def test_traceback(): assert "in test_traceback" in str(excinfo.traceback[-1]) -class TestPytestPluginManager(object): +class TestPytestPluginManager: def test_register_imported_modules(self): pm = PytestPluginManager() mod = types.ModuleType("x.y.pytest_hello") @@ -300,7 +294,7 @@ def test_consider_conftest_deps(self, testdir, pytestpm): pytestpm.consider_conftest(mod) -class TestPytestPluginManagerBootstrapming(object): +class TestPytestPluginManagerBootstrapming: def test_preparse_args(self, pytestpm): pytest.raises( ImportError, lambda: pytestpm.consider_preparse(["xyz", "-p", "hello123"]) diff --git a/testing/test_pytester.py b/testing/test_pytester.py index 54d364ca1fd..82ff37c139a 100644 --- a/testing/test_pytester.py +++ b/testing/test_pytester.py @@ -1,8 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import os import subprocess import sys @@ -30,7 +25,7 @@ def test_make_hook_recorder(testdir): pytest.xfail("internal reportrecorder tests need refactoring") - class rep(object): + class rep: excinfo = None passed = False failed = True @@ -43,7 +38,7 @@ class rep(object): failures = recorder.getfailures() assert failures == [rep] - class rep(object): + class rep: excinfo = None passed = False failed = False @@ -158,7 +153,7 @@ def test_potato(): def make_holder(): - class apiclass(object): + class apiclass: def pytest_xyz(self, arg): "x" @@ -204,17 +199,17 @@ def test_makepyfile_unicode(testdir): def test_makepyfile_utf8(testdir): """Ensure makepyfile accepts utf-8 bytes as input (#2738)""" - utf8_contents = u""" + utf8_contents = """ def setup_function(function): mixed_encoding = u'São Paulo' """.encode( "utf-8" ) p = testdir.makepyfile(utf8_contents) - assert u"mixed_encoding = u'São Paulo'".encode("utf-8") in p.read("rb") + assert "mixed_encoding = u'São Paulo'".encode() in p.read("rb") -class TestInlineRunModulesCleanup(object): +class TestInlineRunModulesCleanup: def test_inline_run_test_module_not_cleaned_up(self, testdir): test_mod = testdir.makepyfile("def test_foo(): assert True") result = testdir.inline_run(str(test_mod)) @@ -225,7 +220,7 @@ def test_inline_run_test_module_not_cleaned_up(self, testdir): assert result2.ret == EXIT_TESTSFAILED def spy_factory(self): - class SysModulesSnapshotSpy(object): + class SysModulesSnapshotSpy: instances = [] def __init__(self, preserve=None): @@ -308,7 +303,7 @@ def test_cwd_snapshot(tmpdir): assert py.path.local() == foo -class TestSysModulesSnapshot(object): +class TestSysModulesSnapshot: key = "my-test-module" def test_remove_added(self): @@ -371,7 +366,7 @@ def test_preserve_container(self, monkeypatch): @pytest.mark.parametrize("path_type", ("path", "meta_path")) -class TestSysPathsSnapshot(object): +class TestSysPathsSnapshot: other_path = {"path": "meta_path", "meta_path": "path"} @staticmethod @@ -422,7 +417,7 @@ def test_testdir_subprocess(testdir): def test_unicode_args(testdir): - result = testdir.runpytest("-k", u"💩") + result = testdir.runpytest("-k", "💩") assert result.ret == EXIT_NOTESTSCOLLECTED diff --git a/testing/test_recwarn.py b/testing/test_recwarn.py index 2246085f59e..1c68b378752 100644 --- a/testing/test_recwarn.py +++ b/testing/test_recwarn.py @@ -1,8 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import re import warnings @@ -31,7 +26,7 @@ def test_method(recwarn): reprec.assertoutcome(passed=1) -class TestWarningsRecorderChecker(object): +class TestWarningsRecorderChecker: def test_recording(self): rec = WarningsRecorder() with rec: @@ -78,7 +73,7 @@ def test_invalid_enter_exit(self): pass # can't enter twice -class TestDeprecatedCall(object): +class TestDeprecatedCall: """test pytest.deprecated_call()""" def dep(self, i, j=None): @@ -210,7 +205,7 @@ def test_deprecated_call_supports_match(self): warnings.warn("this is not here", DeprecationWarning) -class TestWarns(object): +class TestWarns: def test_strings(self): # different messages, b/c Python suppresses multiple identical warnings source1 = "warnings.warn('w1', RuntimeWarning)" diff --git a/testing/test_reports.py b/testing/test_reports.py index 5c6851e49bd..b8b1a5406d1 100644 --- a/testing/test_reports.py +++ b/testing/test_reports.py @@ -1,11 +1,10 @@ -# -*- coding: utf-8 -*- import pytest from _pytest.pathlib import Path from _pytest.reports import CollectReport from _pytest.reports import TestReport -class TestReportSerialization(object): +class TestReportSerialization: def test_xdist_longrepr_to_str_issue_241(self, testdir): """ Regarding issue pytest-xdist#241 @@ -44,7 +43,7 @@ def test_fail(): reports = reprec.getreports("pytest_runtest_logreport") assert len(reports) == 3 rep = reports[1] - added_section = ("Failure Metadata", str("metadata metadata"), "*") + added_section = ("Failure Metadata", "metadata metadata", "*") rep.longrepr.sections.append(added_section) d = rep._to_json() a = TestReport._from_json(d) diff --git a/testing/test_resultlog.py b/testing/test_resultlog.py index 1ef49d5e2d9..9e8f621355b 100644 --- a/testing/test_resultlog.py +++ b/testing/test_resultlog.py @@ -1,8 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import os import py @@ -55,7 +50,7 @@ def test_write_log_entry(): assert entry_lines[1:] == [" " + line for line in longrepr.splitlines()] -class TestWithFunctionIntegration(object): +class TestWithFunctionIntegration: # XXX (hpk) i think that the resultlog plugin should # provide a Parser object so that one can remain # ignorant regarding formatting details. diff --git a/testing/test_runner.py b/testing/test_runner.py index 6906efb910c..77fdcecc3fa 100644 --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -1,8 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import inspect import os import sys @@ -18,7 +13,7 @@ from _pytest import runner -class TestSetupState(object): +class TestSetupState: def test_setup(self, testdir): ss = runner.SetupState() item = testdir.getitem("def test_func(): pass") @@ -106,7 +101,7 @@ def fin_module(): assert module_teardown -class BaseFunctionalTests(object): +class BaseFunctionalTests: def test_passfunction(self, testdir): reports = testdir.runitem( """ @@ -441,7 +436,7 @@ def test_func(): assert rep.when == "???" -class TestSessionReports(object): +class TestSessionReports: def test_collect_result(self, testdir): col = testdir.getmodulecol( """ @@ -644,20 +639,16 @@ def test_pytest_fail_notrace_non_ascii(testdir, str_prefix): This tests with native and unicode strings containing non-ascii chars. """ testdir.makepyfile( - u""" - # -*- coding: utf-8 -*- + """\ import pytest def test_hello(): pytest.fail(%s'oh oh: ☺', pytrace=False) - """ + """ % str_prefix ) result = testdir.runpytest() - if sys.version_info[0] >= 3: - result.stdout.fnmatch_lines(["*test_hello*", "oh oh: ☺"]) - else: - result.stdout.fnmatch_lines(["*test_hello*", "oh oh: *"]) + result.stdout.fnmatch_lines(["*test_hello*", "oh oh: ☺"]) assert "def test_hello" not in result.stdout.str() @@ -792,8 +783,7 @@ def test_hello(): def test_unicode_in_longrepr(testdir): testdir.makeconftest( - """ - # -*- coding: utf-8 -*- + """\ import pytest @pytest.hookimpl(hookwrapper=True) def pytest_runtest_makereport(): @@ -801,7 +791,7 @@ def pytest_runtest_makereport(): rep = outcome.get_result() if rep.when == "call": rep.longrepr = u'ä' - """ + """ ) testdir.makepyfile( """ @@ -876,7 +866,7 @@ def test_store_except_info_on_error(): sys.last_traceback and friends. """ # Simulate item that might raise a specific exception, depending on `raise_error` class var - class ItemMightRaise(object): + class ItemMightRaise: nodeid = "item_that_raises" raise_error = True @@ -933,7 +923,7 @@ def test(fix): assert "PYTEST_CURRENT_TEST" not in os.environ -class TestReportContents(object): +class TestReportContents: """ Test user-level API of ``TestReport`` objects. """ diff --git a/testing/test_runner_xunit.py b/testing/test_runner_xunit.py index 5be7297f6c5..34a086551fd 100644 --- a/testing/test_runner_xunit.py +++ b/testing/test_runner_xunit.py @@ -1,12 +1,7 @@ -# -*- coding: utf-8 -*- """ test correct setup/teardowns at module, class, and instance level """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import pytest diff --git a/testing/test_session.py b/testing/test_session.py index 9acad5deafd..1a0ae808076 100644 --- a/testing/test_session.py +++ b/testing/test_session.py @@ -1,13 +1,8 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import pytest from _pytest.main import EXIT_NOTESTSCOLLECTED -class SessionTests(object): +class SessionTests: def test_basic_testitem_events(self, testdir): tfile = testdir.makepyfile( """ @@ -76,7 +71,7 @@ def test_syntax_error_module(self, testdir): values = reprec.getfailedcollections() assert len(values) == 1 out = str(values[0].longrepr) - assert out.find(str("not python")) != -1 + assert out.find("not python") != -1 def test_exit_first_problem(self, testdir): reprec = testdir.inline_runsource( diff --git a/testing/test_skipping.py b/testing/test_skipping.py index 55119ae1269..6bb5f7aff7a 100644 --- a/testing/test_skipping.py +++ b/testing/test_skipping.py @@ -1,8 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import sys import pytest @@ -11,7 +6,7 @@ from _pytest.skipping import pytest_runtest_setup -class TestEvaluator(object): +class TestEvaluator: def test_no_marker(self, testdir): item = testdir.getitem("def test_func(): pass") evalskipif = MarkEvaluator(item, "skipif") @@ -49,22 +44,6 @@ def test_func(): expl = ev.getexplanation() assert expl == "condition: hasattr(os, 'sep')" - @pytest.mark.skipif("sys.version_info[0] >= 3") - def test_marked_one_arg_unicode(self, testdir): - item = testdir.getitem( - """ - import pytest - @pytest.mark.xyz(u"hasattr(os, 'sep')") - def test_func(): - pass - """ - ) - ev = MarkEvaluator(item, "xyz") - assert ev - assert ev.istrue() - expl = ev.getexplanation() - assert expl == "condition: hasattr(os, 'sep')" - def test_marked_one_arg_with_reason(self, testdir): item = testdir.getitem( """ @@ -152,7 +131,7 @@ def test_func(self): assert expl == "condition: config._hackxyz" -class TestXFail(object): +class TestXFail: @pytest.mark.parametrize("strict", [True, False]) def test_xfail_simple(self, testdir, strict): item = testdir.getitem( @@ -519,7 +498,7 @@ def test_foo(): assert result.ret == (1 if strict else 0) -class TestXFailwithSetupTeardown(object): +class TestXFailwithSetupTeardown: def test_failing_setup_issue9(self, testdir): testdir.makepyfile( """ @@ -551,7 +530,7 @@ def test_func(): result.stdout.fnmatch_lines(["*1 xfail*"]) -class TestSkip(object): +class TestSkip: def test_skip_class(self, testdir): testdir.makepyfile( """ @@ -648,7 +627,7 @@ def test_hello(): result.stdout.fnmatch_lines(["*unconditional skip*", "*1 skipped*"]) -class TestSkipif(object): +class TestSkipif: def test_skipif_conditional(self, testdir): item = testdir.getitem( """ @@ -893,10 +872,7 @@ def test_func(): ) result = testdir.runpytest() markline = " ^" - if sys.platform.startswith("java"): - # XXX report this to java - markline = "*" + markline[8:] - elif hasattr(sys, "pypy_version_info") and sys.pypy_version_info < (6,): + if hasattr(sys, "pypy_version_info") and sys.pypy_version_info < (6,): markline = markline[5:] elif sys.version_info >= (3, 8) or hasattr(sys, "pypy_version_info"): markline = markline[4:] @@ -1006,7 +982,7 @@ def pytest_runtest_setup(item): ) -class TestBooleanCondition(object): +class TestBooleanCondition: def test_skipif(self, testdir): testdir.makepyfile( """ diff --git a/testing/test_stepwise.py b/testing/test_stepwise.py index 68e5989ecdb..2202bbf1b60 100644 --- a/testing/test_stepwise.py +++ b/testing/test_stepwise.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest diff --git a/testing/test_terminal.py b/testing/test_terminal.py index 1b2e46c7c2b..f53cb6837bf 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -1,11 +1,6 @@ -# -*- coding: utf-8 -*- """ terminal reporting of the full testing process. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import collections import os import sys @@ -27,7 +22,7 @@ DistInfo = collections.namedtuple("DistInfo", ["project_name", "version"]) -class Option(object): +class Option: def __init__(self, verbosity=0, fulltrace=False): self.verbosity = verbosity self.fulltrace = fulltrace @@ -75,7 +70,7 @@ def test_plugin_nameversion(input, expected): assert result == expected -class TestTerminal(object): +class TestTerminal: def test_pass_skip_fail(self, testdir, option): testdir.makepyfile( """ @@ -282,7 +277,7 @@ def test_rewrite(self, testdir, monkeypatch): assert f.getvalue() == "hello" + "\r" + "hey" + (6 * " ") -class TestCollectonly(object): +class TestCollectonly: def test_collectonly_basic(self, testdir): testdir.makepyfile( """ @@ -390,7 +385,7 @@ def test_collectonly_more_quiet(self, testdir): result.stdout.fnmatch_lines(["*test_fun.py: 1*"]) -class TestFixtureReporting(object): +class TestFixtureReporting: def test_setup_fixture_error(self, testdir): testdir.makepyfile( """ @@ -490,7 +485,7 @@ def teardown_function(function): ) -class TestTerminalFunctional(object): +class TestTerminalFunctional: def test_deselected(self, testdir): testpath = testdir.makepyfile( """ @@ -863,8 +858,8 @@ def test_this(i): def test_getreportopt(): - class Config(object): - class Option(object): + class Config: + class Option: reportchars = "" disable_warnings = True @@ -945,7 +940,7 @@ def test_traceconfig(testdir, monkeypatch): assert result.ret == EXIT_NOTESTSCOLLECTED -class TestGenericReporting(object): +class TestGenericReporting: """ this test class can be subclassed with a different option provider to run e.g. distributed tests. """ @@ -1325,7 +1320,7 @@ class DummyReport(BaseReport): assert res == ("1 failed", "red") -class TestClassicOutputStyle(object): +class TestClassicOutputStyle: """Ensure classic output style works as expected (#3883)""" @pytest.fixture @@ -1371,7 +1366,7 @@ def test_quiet(self, testdir, test_files): result.stdout.fnmatch_lines([".F.F.", "*2 failed, 3 passed in*"]) -class TestProgressOutputStyle(object): +class TestProgressOutputStyle: @pytest.fixture def many_tests_files(self, testdir): testdir.makepyfile( @@ -1502,7 +1497,7 @@ def test_capture_no(self, many_tests_files, testdir): assert "%]" not in output.stdout.str() -class TestProgressWithTeardown(object): +class TestProgressWithTeardown: """Ensure we show the correct percentages for tests that fail during teardown (#3088)""" @pytest.fixture @@ -1589,7 +1584,7 @@ def test_skip_reasons_folding(): message = "justso" longrepr = (path, lineno, message) - class X(object): + class X: pass ev1 = X() @@ -1630,10 +1625,10 @@ def mock_get_pos(*args): monkeypatch.setattr(_pytest.terminal, "_get_pos", mock_get_pos) - class config(object): + class config: pass - class rep(object): + class rep: def _get_verbose_word(self, *args): return mocked_verbose_word @@ -1648,7 +1643,7 @@ def check(msg, width, expected): actual = _get_line_with_reprcrash_message(config, rep(), width) assert actual == expected - if actual != "%s %s" % (mocked_verbose_word, mocked_pos): + if actual != "{} {}".format(mocked_verbose_word, mocked_pos): assert len(actual) <= width assert wcswidth(actual) <= width @@ -1670,17 +1665,17 @@ def check(msg, width, expected): check("some\nmessage", 80, "FAILED some::nodeid - some") # Test unicode safety. - check(u"😄😄😄😄😄\n2nd line", 25, u"FAILED some::nodeid - ...") - check(u"😄😄😄😄😄\n2nd line", 26, u"FAILED some::nodeid - ...") - check(u"😄😄😄😄😄\n2nd line", 27, u"FAILED some::nodeid - 😄...") - check(u"😄😄😄😄😄\n2nd line", 28, u"FAILED some::nodeid - 😄...") - check(u"😄😄😄😄😄\n2nd line", 29, u"FAILED some::nodeid - 😄😄...") + check("😄😄😄😄😄\n2nd line", 25, "FAILED some::nodeid - ...") + check("😄😄😄😄😄\n2nd line", 26, "FAILED some::nodeid - ...") + check("😄😄😄😄😄\n2nd line", 27, "FAILED some::nodeid - 😄...") + check("😄😄😄😄😄\n2nd line", 28, "FAILED some::nodeid - 😄...") + check("😄😄😄😄😄\n2nd line", 29, "FAILED some::nodeid - 😄😄...") # NOTE: constructed, not sure if this is supported. # It would fail if not using u"" in Python 2 for mocked_pos. - mocked_pos = u"nodeid::😄::withunicode" - check(u"😄😄😄😄😄\n2nd line", 29, u"FAILED nodeid::😄::withunicode") - check(u"😄😄😄😄😄\n2nd line", 40, u"FAILED nodeid::😄::withunicode - 😄😄...") - check(u"😄😄😄😄😄\n2nd line", 41, u"FAILED nodeid::😄::withunicode - 😄😄...") - check(u"😄😄😄😄😄\n2nd line", 42, u"FAILED nodeid::😄::withunicode - 😄😄😄...") - check(u"😄😄😄😄😄\n2nd line", 80, u"FAILED nodeid::😄::withunicode - 😄😄😄😄😄") + mocked_pos = "nodeid::😄::withunicode" + check("😄😄😄😄😄\n2nd line", 29, "FAILED nodeid::😄::withunicode") + check("😄😄😄😄😄\n2nd line", 40, "FAILED nodeid::😄::withunicode - 😄😄...") + check("😄😄😄😄😄\n2nd line", 41, "FAILED nodeid::😄::withunicode - 😄😄...") + check("😄😄😄😄😄\n2nd line", 42, "FAILED nodeid::😄::withunicode - 😄😄😄...") + check("😄😄😄😄😄\n2nd line", 80, "FAILED nodeid::😄::withunicode - 😄😄😄😄😄") diff --git a/testing/test_tmpdir.py b/testing/test_tmpdir.py index 40ffe98af98..a7c0ed7eaa7 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -1,12 +1,6 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import sys import attr -import six import pytest from _pytest import pathlib @@ -28,7 +22,7 @@ def test_ensuretemp(recwarn): @attr.s -class FakeConfig(object): +class FakeConfig: basetemp = attr.ib() trace = attr.ib(default=None) @@ -44,7 +38,7 @@ def option(self): return self -class TestTempdirHandler(object): +class TestTempdirHandler: def test_mktemp(self, tmp_path): from _pytest.tmpdir import TempdirFactory, TempPathFactory @@ -69,7 +63,7 @@ def test_tmppath_relative_basetemp_absolute(self, tmp_path, monkeypatch): assert t.getbasetemp().resolve() == (tmp_path / "hello").resolve() -class TestConfigTmpdir(object): +class TestConfigTmpdir: def test_getbasetemp_custom_removes_old(self, testdir): mytemp = testdir.tmpdir.join("xyz") p = testdir.makepyfile( @@ -234,7 +228,7 @@ def test_get_user(monkeypatch): assert get_user() is None -class TestNumberedDir(object): +class TestNumberedDir: PREFIX = "fun-" def test_make(self, tmp_path): @@ -348,8 +342,6 @@ def test_removal_accepts_lock(self, tmp_path): def attempt_symlink_to(path, to_path): """Try to make a symlink from "path" to "to_path", skipping in case this platform does not support it or we don't have sufficient privileges (common on Windows).""" - if sys.platform.startswith("win") and six.PY2: - pytest.skip("pathlib for some reason cannot make symlinks on Python 2") try: Path(path).symlink_to(Path(to_path)) except OSError: diff --git a/testing/test_unittest.py b/testing/test_unittest.py index bb41952abbe..a8555b35357 100644 --- a/testing/test_unittest.py +++ b/testing/test_unittest.py @@ -1,8 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import gc import pytest @@ -393,7 +388,7 @@ def test_func1(self): reprec.assertoutcome(skipped=1) -class TestTrialUnittest(object): +class TestTrialUnittest: def setup_class(cls): cls.ut = pytest.importorskip("twisted.trial.unittest") # on windows trial uses a socket for a reactor and apparently doesn't close it properly @@ -459,9 +454,6 @@ def test_method(self): pass """ ) - from _pytest.compat import _is_unittest_unexpected_success_a_failure - - should_fail = _is_unittest_unexpected_success_a_failure() result = testdir.runpytest("-rxs", *self.ignore_unclosed_socket_warning) result.stdout.fnmatch_lines_random( [ @@ -472,12 +464,10 @@ def test_method(self): "*i2wanto*", "*sys.version_info*", "*skip_in_method*", - "*1 failed*4 skipped*3 xfailed*" - if should_fail - else "*4 skipped*3 xfail*1 xpass*", + "*1 failed*4 skipped*3 xfailed*", ] ) - assert result.ret == (1 if should_fail else 0) + assert result.ret == 1 def test_trial_error(self, testdir): testdir.makepyfile( @@ -745,22 +735,17 @@ def test_passing_test_is_fail(self): unittest.main() """ ) - from _pytest.compat import _is_unittest_unexpected_success_a_failure - should_fail = _is_unittest_unexpected_success_a_failure() if runner == "pytest": result = testdir.runpytest("-rxX") result.stdout.fnmatch_lines( - [ - "*MyTestCase*test_passing_test_is_fail*", - "*1 failed*" if should_fail else "*1 xpassed*", - ] + ["*MyTestCase*test_passing_test_is_fail*", "*1 failed*"] ) else: result = testdir.runpython(script) result.stderr.fnmatch_lines(["*1 test in*", "*(unexpected successes=1)*"]) - assert result.ret == (1 if should_fail else 0) + assert result.ret == 1 @pytest.mark.parametrize( @@ -932,7 +917,7 @@ def test_should_not_run(self): @pytest.mark.parametrize( - "base", ["six.moves.builtins.object", "unittest.TestCase", "unittest2.TestCase"] + "base", ["builtins.object", "unittest.TestCase", "unittest2.TestCase"] ) def test_usefixtures_marker_on_unittest(base, testdir): """#3498""" diff --git a/testing/test_warnings.py b/testing/test_warnings.py index 092604d7db0..1654024785f 100644 --- a/testing/test_warnings.py +++ b/testing/test_warnings.py @@ -1,11 +1,5 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -import sys import warnings -import six - import pytest WARNINGS_SUMMARY_HEADER = "warnings summary" @@ -126,14 +120,10 @@ def test_ignore(testdir, pyfile_with_warnings, method): assert WARNINGS_SUMMARY_HEADER not in result.stdout.str() -@pytest.mark.skipif( - sys.version_info < (3, 0), reason="warnings message is unicode is ok in python3" -) @pytest.mark.filterwarnings("always") def test_unicode(testdir, pyfile_with_warnings): testdir.makepyfile( - """ - # -*- coding: utf-8 -*- + """\ import warnings import pytest @@ -145,76 +135,14 @@ def fix(): def test_func(fix): pass - """ - ) - result = testdir.runpytest() - result.stdout.fnmatch_lines( - [ - "*== %s ==*" % WARNINGS_SUMMARY_HEADER, - "*test_unicode.py:8: UserWarning: \u6d4b\u8bd5*", - "* 1 passed, 1 warnings*", - ] - ) - - -@pytest.mark.skipif( - sys.version_info >= (3, 0), - reason="warnings message is broken as it is not str instance", -) -def test_py2_unicode(testdir, pyfile_with_warnings): - if getattr(sys, "pypy_version_info", ())[:2] == (5, 9) and sys.platform.startswith( - "win" - ): - pytest.xfail("fails with unicode error on PyPy2 5.9 and Windows (#2905)") - testdir.makepyfile( - """ - # -*- coding: utf-8 -*- - import warnings - import pytest - - - @pytest.fixture - def fix(): - warnings.warn(u"测试") - yield - - @pytest.mark.filterwarnings('always') - def test_func(fix): - pass - """ - ) - result = testdir.runpytest() - result.stdout.fnmatch_lines( - [ - "*== %s ==*" % WARNINGS_SUMMARY_HEADER, - "*test_py2_unicode.py:8: UserWarning: \\u6d4b\\u8bd5", - '*warnings.warn(u"\u6d4b\u8bd5")', - "*warnings.py:*: UnicodeWarning: Warning is using unicode non*", - "* 1 passed, 2 warnings*", - ] - ) - - -def test_py2_unicode_ascii(testdir): - """Ensure that our warning about 'unicode warnings containing non-ascii messages' - does not trigger with ascii-convertible messages""" - testdir.makeini("[pytest]") - testdir.makepyfile( """ - import pytest - import warnings - - @pytest.mark.filterwarnings('always') - def test_func(): - warnings.warn(u"hello") - """ ) result = testdir.runpytest() result.stdout.fnmatch_lines( [ "*== %s ==*" % WARNINGS_SUMMARY_HEADER, - '*warnings.warn(u"hello")', - "* 1 passed, 1 warnings in*", + "*test_unicode.py:7: UserWarning: \u6d4b\u8bd5*", + "* 1 passed, 1 warnings*", ] ) @@ -564,38 +492,11 @@ def test_hidden_by_cmdline(self, testdir): def test_hidden_by_system(self, testdir, monkeypatch): self.create_file(testdir) - monkeypatch.setenv(str("PYTHONWARNINGS"), str("once::UserWarning")) + monkeypatch.setenv("PYTHONWARNINGS", "once::UserWarning") result = testdir.runpytest_subprocess() assert WARNINGS_SUMMARY_HEADER not in result.stdout.str() -@pytest.mark.skipif(six.PY3, reason="Python 2 only issue") -def test_infinite_loop_warning_against_unicode_usage_py2(testdir): - """ - We need to be careful when raising the warning about unicode usage with "warnings.warn" - because it might be overwritten by users and this itself causes another warning (#3691). - """ - testdir.makepyfile( - """ - # -*- coding: utf-8 -*- - from __future__ import unicode_literals - import warnings - import pytest - - def _custom_showwarning(message, *a, **b): - return "WARNING: {}".format(message) - - warnings.formatwarning = _custom_showwarning - - @pytest.mark.filterwarnings("default") - def test_custom_warning_formatter(): - warnings.warn("¥") - """ - ) - result = testdir.runpytest_subprocess() - result.stdout.fnmatch_lines(["*1 passed, * warnings in*"]) - - @pytest.mark.parametrize("change_default", [None, "ini", "cmdline"]) def test_removed_in_pytest4_warning_as_error(testdir, change_default): testdir.makepyfile( diff --git a/tox.ini b/tox.ini index 0b1be0d33fe..7e5b182f69b 100644 --- a/tox.ini +++ b/tox.ini @@ -5,16 +5,13 @@ distshare = {homedir}/.tox/distshare # make sure to update environment list in travis.yml and appveyor.yml envlist = linting - py27 - py34 py35 py36 py37 py38 pypy pypy3 - {py27,py37}-{pexpect,xdist,twisted,numpy,pluggymaster} - py27-nobyte-xdist + py37-{pexpect,xdist,twisted,numpy,pluggymaster} doctesting py37-freeze docs @@ -56,15 +53,6 @@ deps = {env:_PYTEST_TOX_EXTRA_DEP:} platform = {env:_PYTEST_TOX_PLATFORM:.*} -[testenv:py27-subprocess] -deps = - pytest-xdist>=1.13 - py27: mock - nose -commands = - pytest -n auto --runpytest=subprocess {posargs} - - [testenv:linting] skip_install = True basepython = python3 @@ -109,11 +97,6 @@ commands = rm -rf {envdir}/.pytest_cache make regen -[testenv:jython] -changedir = testing -commands = - {envpython} {envbindir}/py.test-jython {posargs} - [testenv:py37-freeze] changedir = testing/freeze # Disable PEP 517 with pip, which does not work with PyInstaller currently.