Compare commits

..

62 Commits

Author SHA1 Message Date
Bruno Oliveira
180f93158e Introduce missing remark as commented in original PR
https://github.com/pytest-dev/pytest/pull/6870/files#r390667966
2020-05-08 12:59:26 -03:00
Bruno Oliveira
f1d7aa60b1 Preparing release version 4.6.10 2020-05-08 11:14:53 -04:00
Bruno Oliveira
ded772b288 Merge pull request #6870 from fermezz/backport-invocation-args 2020-05-08 08:04:49 -03:00
Bruno Oliveira
3d470555e8 Merge pull request #7190 from hroncok/backport-7179 2020-05-07 22:25:40 -03:00
Bruno Oliveira
2a5ca51fe8 [4.6] Merge pull request #7179 from asottile/py39 2020-05-07 23:22:29 +02:00
Fernando Mez
a6029ff2b7 BACKPORT: Introduction of Config.invocation_args 2020-03-26 16:41:08 -03:00
Bruno Oliveira
020831d868 Merge pull request #6884 from fermezz/use-github-actions
Fix travis config and coverage report.
2020-03-26 12:42:55 -03:00
Fernando Mez
c5831ac98f Fix CI config and coverage report 2020-03-12 16:58:33 -03:00
Bruno Oliveira
f606fef19d [4.6] Remove usage of parser module, deprecated in Python 3.9 (#6408)
[4.6] Remove usage of parser module, deprecated in Python 3.9
2020-01-06 13:07:40 -03:00
Bruno Oliveira
24898e0640 Remove usage of parser module, deprecated in Python 3.9
Fix #6404
2020-01-06 08:58:39 -03:00
Anthony Sottile
b39b867967 Merge pull request #6391 from asottile/release-4.6.9
Preparing release version 4.6.9
2020-01-04 15:59:26 -05:00
Anthony Sottile
f6a5578d5c Preparing release version 4.6.9 2020-01-04 04:40:51 -08:00
Bruno Oliveira
3f94cc9e35 Merge pull request #6392 from hugovk/4.6-maintenance-2020
4.6: Update copyright year to 2020
2020-01-04 07:21:11 -03:00
Hugo
897f1a3ef4 Update copyright year 2020-01-04 09:53:49 +02:00
Hugo
035f51ab71 Update copyright year to 2020 2020-01-03 01:01:32 +02:00
Bruno Oliveira
621028c58d [4.6] Fix assertion rewriting module detection for egg dists (#6368)
[4.6] Fix assertion rewriting module detection for egg dists
2019-12-26 13:37:00 -03:00
Felix Yan
d622f12f69 Fix compatibility for Python 2 2019-12-25 19:44:39 +08:00
Bruno Oliveira
e49282f72c Fix assertion rewriting module detection for egg dists
Fix #6301
2019-12-25 19:43:28 +08:00
Anthony Sottile
197c996345 Merge pull request #6360 from asottile/release-4.6.8
Preparing release version 4.6.8
2019-12-19 15:55:16 -08:00
Anthony Sottile
2d398d8706 Preparing release version 4.6.8 2019-12-19 14:42:09 -08:00
Anthony Sottile
9ab4032f74 Merge pull request #6345 from nightlark/patch-1
Pin the colorama version only for Python 3.4
2019-12-19 14:38:59 -08:00
Ryan Mast
53b08730e4 Pin the colorama version only for Python 3.4 2019-12-14 10:10:16 -03:00
Bruno Oliveira
1deb60f02f Ensure colorama version is no newer than 0.4.1 (#6340)
Ensure colorama version is no newer than 0.4.1
2019-12-13 08:42:59 -03:00
Bruno Oliveira
fb8395d93f Create CHANGELOG for #6340 2019-12-13 07:46:12 -03:00
Ryan Mast
b08c599bad Ensure colorama versions is no newer than 0.4.1 2019-12-12 21:09:21 -08:00
Bruno Oliveira
51fd451dc9 [4.6] Bugfix 5430 pass logs to junit report (#6338)
[4.6] Bugfix 5430 pass logs to junit report
2019-12-12 19:03:58 -03:00
Bruno Oliveira
1d021540a3 Drop 3.4 testing on Azure
Azure no longer supports testing on Python 3.4
2019-12-12 16:47:47 -03:00
Bruno Oliveira
8bfe434f75 Drop validation against multiple xmlfamilies
This was not backported to 4.6 before, so had to adapt the test slightly
to the old method of validation.
2019-12-12 16:44:07 -03:00
Bruno Oliveira
f9ebe3c607 Bugfix 5430 pass logs to junit report (#6274)
Bugfix 5430 pass logs to junit report
2019-12-12 16:39:19 -03:00
Bruno Oliveira
bd54116d03 [4.6] Add hostname and timestamp to JUnit XML testsuite tag (#5… (#6332)
[4.6] Add hostname and timestamp to JUnit XML testsuite tag (#5692)
2019-12-10 18:11:28 -03:00
Bruno Oliveira
8b9482e39c Add hostname and timestamp to JUnit XML testsuite tag (#5692)
Add hostname and timestamp to JUnit XML testsuite tag

Conflicts:
  	testing/test_junitxml.py
2019-12-10 17:27:22 -03:00
Bruno Oliveira
943f4ac236 Release version 4.6.7 (#6318)
Release version 4.6.7
2019-12-05 22:44:59 -03:00
Bruno Oliveira
6f43eee106 Preparing release version 4.6.7 2019-12-05 15:29:25 -05:00
Bruno Oliveira
e1f3c0f9c3 [4.6] Include <testsuites> root tag in generated XML (#5550) (#6295)
[4.6] Include <testsuites> root tag in generated XML (#5550)
2019-11-30 12:13:57 -03:00
Bruno Oliveira
192f6992d2 Include <testsuites> root tag in generated XML (#5550)
Include <testsuites> root tag in generated XML
2019-11-30 11:30:43 -03:00
Daniel Hahler
6465244269 [4.6] Upgrade black (#6209) 2019-11-16 19:25:01 +01:00
Daniel Hahler
097acaf11b re-run black 2019-11-16 19:17:01 +01:00
Daniel Hahler
3d8649b206 Remove (now) unnecessary fmt: off 2019-11-16 19:15:56 +01:00
Daniel Hahler
a8c16d9b75 pre-commit: upgrade black
This brings https://github.com/psf/black/pull/826, which helps with
https://github.com/psf/black/issues/601.
2019-11-16 19:15:54 +01:00
Bruno Oliveira
3edf417969 [4.6] Review rm_rf handling of FileNotFoundErrors (#6050)
[4.6] Review rm_rf handling of FileNotFoundErrors
2019-10-23 20:19:05 -03:00
Bruno Oliveira
0084fd9783 Review rm_rf handling of FileNotFoundErrors (#6044)
Review rm_rf handling of FileNotFoundErrors
Conflicts:
 	 src/_pytest/pathlib.py
  	testing/test_tmpdir.py
2019-10-23 19:36:35 -03:00
Daniel Hahler
e89efa8325 Merge pull request #5957 from blueyed/4.6-comment-off
[4.6] ci: add codecov.yml to turn comments off
2019-10-15 02:14:31 +02:00
Daniel Hahler
3edcc71c41 ci: add codecov.yml to turn comments off
The only benefit for me is to get notified about finished builds, but
that might happen to early anyway.  Apart from that they are rather big
and distract from actual comments.

(cherry picked from commit d50198a3ff)
2019-10-14 22:32:27 +02:00
Bruno Oliveira
866daf57fe Release 4.6.6 (#5947)
Release 4.6.6
2019-10-13 11:55:23 -03:00
Bruno Oliveira
5b499bafb2 Preparing release version 4.6.6 2019-10-11 17:02:08 -04:00
Bruno Oliveira
62c0d82d64 Use 'python3' instead of 'python3.6' on tox
This allows us to use python3.7+ to use tox
2019-10-11 16:58:40 -04:00
Bruno Oliveira
d526053af3 Add changelog entry for #5902 2019-10-11 16:57:32 -04:00
Bruno Oliveira
2c7614a0e1 [4.6] Replace importlib_metadata with importlib.metadata on Pyt… (#5945)
[4.6] Replace importlib_metadata with importlib.metadata on Python 3.8+ (#5539)
2019-10-11 12:54:28 -03:00
Bruno Oliveira
b9a8465ce4 Replace importlib_metadata with importlib.metadata on Python 3.8+ (#5539) 2019-10-11 17:10:01 +02:00
Bruno Oliveira
1cc974c95d [4.6] Fix warnings with attrs 19.2 and fix object assertions (#… (#5944)
[4.6] Fix warnings with attrs 19.2 and fix object assertions (#5902)
2019-10-11 11:13:37 -03:00
Bruno Oliveira
c03e46f1ad Fix warnings with attrs 19.2 and fix object assertions (#5902)
Fix warnings with attrs 19.2 and fix object assertions
2019-10-11 15:26:23 +02:00
Bruno Oliveira
f2d87dcf6c Merge pull request #5809 from goerz/pastebin
Fix "lexer" being used when uploading to bpaste.net
2019-09-01 09:34:16 -03:00
Michael Goerz
914441557c Fix "lexer" being used when uploading to bpaste.net
Closes #5806.
2019-09-01 00:38:11 -04:00
Anthony Sottile
8aba863a63 Merge pull request #5801 from asottile/flake8_2020
[4.6] fixes for python4
2019-08-29 10:30:54 -07:00
Anthony Sottile
aa79b1c00c [4.6] fixes for python4 2019-08-29 09:52:35 -07:00
Bruno Oliveira
117f52dcf3 Merge pull request #5767 from nicoddemus/backport-5723-5740-5750
[4.6] Publish GitHub release notes after deployment
2019-08-20 19:34:49 -03:00
Bruno Oliveira
9191857b5f Do not update pip on Azure
Avoid upgrading pip because it is giving this error on py34:

Requirement already up-to-date: pip in c:\hostedtoolcache\windows\python\3.4.4\x64\lib\site-packages (19.2.1)
ERROR: Package 'pip' requires a different Python: 3.4.4 not in '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*'
[error]Cmd.exe exited with code '1'.
2019-08-20 19:11:14 -03:00
Bruno Oliveira
7718d8c972 Fix linting 2019-08-19 16:58:47 -03:00
Bruno Oliveira
7a96f3f970 Merge pull request #5750 from nicoddemus/fix-gh-publish-notes
Forward $TRAVIS_REPO_SLUG for GH publish notes
2019-08-19 16:39:16 -03:00
Bruno Oliveira
2fbea0e5e4 Merge pull request #5740 from nicoddemus/use-repo-env-var
Use TRAVIS_REPO_SLUG instead of hard-coding pytest-dev/pytest
2019-08-19 16:39:07 -03:00
Bruno Oliveira
4910036b76 Publish GitHub release notes after deployment (#5723)
Publish GitHub release notes after deployment
2019-08-19 16:38:57 -03:00
Bruno Oliveira
0b039b14aa Preparing release version 4.6.5 (#5696)
Preparing release version 4.6.5
2019-08-05 15:05:28 -03:00
57 changed files with 952 additions and 226 deletions

191
.github/workflows/main.yml vendored Normal file
View File

@@ -0,0 +1,191 @@
name: main
on:
push:
branches:
- 4.6.x
tags:
- "*"
pull_request:
branches:
- 4.6.x
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
name: [
"windows-py27",
"windows-py35",
"windows-py36",
"windows-py37",
"windows-py37-pluggy",
"windows-py38",
"ubuntu-py27-pluggy",
"ubuntu-py27-nobyte",
"ubuntu-py37",
"ubuntu-py37-pluggy",
"ubuntu-py37-pexpect-py37-twisted",
"ubuntu-py37-freeze",
"ubuntu-pypy",
"ubuntu-pypy3",
"macos-py27",
"macos-py38",
]
include:
# Windows jobs
- name: "windows-py27"
python: "2.7"
os: windows-latest
tox_env: "py27-xdist"
use_coverage: true
- name: "windows-py35"
python: "3.5"
os: windows-latest
tox_env: "py35-xdist"
use_coverage: true
- name: "windows-py36"
python: "3.6"
os: windows-latest
tox_env: "py36-xdist"
use_coverage: true
- name: "windows-py37"
python: "3.7"
os: windows-latest
tox_env: "py37-twisted-numpy"
use_coverage: true
- name: "windows-py37-pluggy"
python: "3.7"
os: windows-latest
tox_env: "py37-pluggymaster-xdist"
use_coverage: true
- name: "windows-py38"
python: "3.8"
os: windows-latest
tox_env: "py38-xdist"
use_coverage: true
# Ubuntu jobs find the rest of them in .travis.yml
- name: "ubuntu-py27-pluggy"
python: "2.7"
os: ubuntu-latest
tox_env: "py27-pluggymaster-xdist"
use_coverage: true
- name: "ubuntu-py27-nobyte"
python: "2.7"
os: ubuntu-latest
tox_env: "py27-nobyte-numpy-xdist"
use_coverage: true
- name: "ubuntu-py37"
python: "3.7"
os: ubuntu-latest
tox_env: "py37-lsof-numpy-xdist"
use_coverage: true
- name: "ubuntu-py37-pluggy"
python: "3.7"
os: ubuntu-latest
tox_env: "py37-pluggymaster-xdist"
use_coverage: true
- name: "ubuntu-py37-pexpect-py37-twisted"
python: "3.7"
os: ubuntu-latest
tox_env: "py37-pexpect,py37-twisted"
use_coverage: true
- name: "ubuntu-py37-freeze"
python: "3.7"
os: ubuntu-latest
tox_env: "py37-freeze"
- name: "ubuntu-pypy"
python: "pypy2"
os: ubuntu-latest
tox_env: "pypy-xdist"
use_coverage: true
- name: "ubuntu-pypy3"
python: "pypy3"
os: ubuntu-latest
tox_env: "pypy3-xdist"
use_coverage: true
# MacOS jobs
- name: "macos-py27"
python: "2.7"
os: macos-latest
tox_env: "py27-xdist"
use_coverage: true
- name: "macos-py38"
python: "3.8"
os: macos-latest
tox_env: "py38-xdist"
use_coverage: true
steps:
- uses: actions/checkout@v1
- name: Set up Python ${{ matrix.python }} on ${{ matrix.os }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install tox coverage
- name: Test without coverage
if: "! matrix.use_coverage"
run: "tox -e ${{ matrix.tox_env }}"
- name: Test with coverage
if: "matrix.use_coverage"
env:
_PYTEST_TOX_COVERAGE_RUN: "coverage run -m"
COVERAGE_PROCESS_START: ".coveragerc"
_PYTEST_TOX_EXTRA_DEP: "coverage-enable-subprocess"
run: "tox -vv -e ${{ matrix.tox_env }}"
- name: Prepare coverage token
if: (matrix.use_coverage && ( github.repository == 'pytest-dev/pytest' || github.event_name == 'pull_request' ))
run: |
python scripts/append_codecov_token.py
- name: Report coverage
if: (matrix.use_coverage)
env:
CODECOV_NAME: ${{ matrix.name }}
run: bash scripts/report-coverage.sh -F GHA,${{ runner.os }}
deploy:
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') && github.repository == 'pytest-dev/pytest'
runs-on: ubuntu-latest
needs: [build]
steps:
- uses: actions/checkout@v1
- name: Set up Python
uses: actions/setup-python@v1
with:
python-version: "3.7"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install --upgrade wheel setuptools tox
- name: Build package
run: |
python setup.py sdist bdist_wheel
- name: Publish package to PyPI
uses: pypa/gh-action-pypi-publish@master
with:
user: __token__
password: ${{ secrets.pypi_token }}
- name: Publish GitHub release notes
env:
GH_RELEASE_NOTES_TOKEN: ${{ secrets.release_notes }}
run: |
sudo apt-get install pandoc
tox -e publish-gh-release-notes

View File

@@ -1,7 +1,7 @@
exclude: doc/en/example/py2py3/test_py2.py
repos:
- repo: https://github.com/python/black
rev: 19.3b0
- repo: https://github.com/psf/black
rev: 19.10b0
hooks:
- id: black
args: [--safe, --quiet]

View File

@@ -1,17 +1,12 @@
language: python
dist: xenial
stages:
- baseline
- name: test
if: repo = pytest-dev/pytest AND tag IS NOT present
- name: deploy
if: repo = pytest-dev/pytest AND tag IS present
python: '3.7'
python: '3.7.4'
cache: false
env:
global:
- PYTEST_ADDOPTS=-vv
- PYTEST_ADDOPTS="-vv --showlocals --durations=100 --exitfirst"
- PYTEST_COVERAGE=1
# setuptools-scm needs all tags in order to obtain a proper version
git:
@@ -22,104 +17,44 @@ install:
jobs:
include:
# OSX tests - first (in test stage), since they are the slower ones.
- &test-macos
os: osx
osx_image: xcode10.1
language: generic
# Coverage for:
# - py2 with symlink in test_cmdline_python_package_symlink.
env: TOXENV=py27-xdist PYTEST_COVERAGE=1
before_install:
- python -V
- test $(python -c 'import sys; print("%d%d" % sys.version_info[0:2])') = 27
- <<: *test-macos
env: TOXENV=py37-pexpect,py37-xdist PYTEST_COVERAGE=1
before_install:
- which python3
- python3 -V
- ln -sfn "$(which python3)" /usr/local/bin/python
- python -V
- test $(python -c 'import sys; print("%d%d" % sys.version_info[0:2])') = 37
# Full run of latest (major) supported versions, without xdist.
- env: TOXENV=py27
python: '2.7'
- env: TOXENV=py37
python: '3.7'
# Coverage tracking is slow with pypy, skip it.
- env: TOXENV=pypy-xdist
python: 'pypy'
- env: TOXENV=pypy3-xdist
python: 'pypy3'
- env: TOXENV=py34-xdist
python: '3.4'
- env: TOXENV=py35-xdist
python: '3.5'
# Coverage for:
# - pytester's LsofFdLeakChecker
# - TestArgComplete (linux only)
# - numpy
# 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).
- env: TOXENV=py37-pexpect,py37-twisted PYTEST_COVERAGE=1
- env: TOXENV=py37-pluggymaster-xdist
- env: TOXENV=py37-freeze
# Jobs only run via Travis cron jobs (currently daily).
- env: TOXENV=py38-xdist
python: '3.8-dev'
if: type = cron
# - verbose=0
- stage: baseline
# Coverage for:
# - _pytest.unittest._handle_skip (via pexpect).
env: TOXENV=py27-pexpect,py27-twisted PYTEST_COVERAGE=1
env: TOXENV=py27-xdist
python: '2.7'
# Use py36 here for faster baseline.
- env: TOXENV=py36-xdist
python: '3.6'
- env: TOXENV=linting,docs,doctesting PYTEST_COVERAGE=1
- env: TOXENV=py38-xdist
python: '3.8'
- stage: tests
# - _pytest.unittest._handle_skip (via pexpect).
env: TOXENV=py27-pexpect,py27-twisted
python: '2.7'
- env: TOXENV=py35-xdist
python: '3.5.9'
- env: TOXENV=py36-xdist PYTEST_REORDER_TESTS=0
python: '3.6.9'
- env: TOXENV=py37-numpy-pexpect-twisted
python: '3.7.4'
# - test_sys_breakpoint_interception (via pexpect).
- env: TOXENV=py37-pexpect,py37-twisted
python: '3.7.4'
# Run also non-verbosely, to gain coverage
- env: TOXENV=py38-xdist PYTEST_ADDOPTS=""
python: '3.8'
- env: TOXENV=linting,docs,doctesting
cache:
directories:
- $HOME/.cache/pre-commit
- stage: deploy
python: '3.6'
install: pip install -U setuptools setuptools_scm
script: skip
deploy:
provider: pypi
user: nicoddemus
distributions: sdist bdist_wheel
skip_upload_docs: true
password:
secure: xanTgTUu6XDQVqB/0bwJQXoDMnU5tkwZc5koz6mBkkqZhKdNOi2CLoC1XhiSZ+ah24l4V1E0GAqY5kBBcy9d7NVe4WNg4tD095LsHw+CRU6/HCVIFfyk2IZ+FPAlguesCcUiJSXOrlBF+Wj68wEvLoK7EoRFbJeiZ/f91Ww1sbtDlqXABWGHrmhPJL5Wva7o7+wG7JwJowqdZg1pbQExsCc7b53w4v2RBu3D6TJaTAzHiVsW+nUSI67vKI/uf+cR/OixsTfy37wlHgSwihYmrYLFls3V0bSpahCim3bCgMaFZx8S8xrdgJ++PzBCof2HeflFKvW+VCkoYzGEG4NrTWJoNz6ni4red9GdvfjGH3YCjAKS56h9x58zp2E5rpsb/kVq5/45xzV+dq6JRuhQ1nJWjBC6fSKAc/bfwnuFK3EBxNLkvBssLHvsNjj5XG++cB8DdS9wVGUqjpoK4puaXUWFqy4q3S9F86HEsKNgExtieA9qNx+pCIZVs6JCXZNjr0I5eVNzqJIyggNgJG6RyravsU35t9Zd9doL5g4Y7UKmAGTn1Sz24HQ4sMQgXdm2SyD8gEK5je4tlhUvfGtDvMSlstq71kIn9nRpFnqB6MFlbYSEAZmo8dGbCquoUc++6Rum208wcVbrzzVtGlXB/Ow9AbFMYeAGA0+N/K1e59c=
on:
tags: true
repo: pytest-dev/pytest
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:
- |
# Do not (re-)upload coverage with cron runs.
@@ -133,27 +68,13 @@ before_script:
export _PYTEST_TOX_COVERAGE_RUN="coverage run -m"
export _PYTEST_TOX_EXTRA_DEP=coverage-enable-subprocess
fi
script: tox
script: env COLUMNS=120 python -m tox
after_success:
- |
if [[ "$PYTEST_COVERAGE" = 1 ]]; then
set -e
# Add last TOXENV to $PATH.
PATH="$PWD/.tox/${TOXENV##*,}/bin:$PATH"
coverage combine
coverage xml
coverage report -m
bash <(curl -s https://codecov.io/bash) -Z -X gcov -X coveragepy -X search -X xcode -X gcovout -X fix -f coverage.xml -n $TOXENV-$TRAVIS_OS_NAME
env CODECOV_NAME="$TOXENV-$TRAVIS_OS_NAME" scripts/report-coverage.sh
fi
notifications:
irc:
channels:
- "chat.freenode.net#pytest"
on_success: change
on_failure: change
skip_join: true
email:
- pytest-commit@python.org
branches:
only:
- 4.6.x

View File

@@ -58,6 +58,7 @@ Christian Theunert
Christian Tismer
Christopher Gilling
Christopher Dignam
Claudio Madotto
CrazyMerlyn
Cyrus Maden
Damian Skrzypczak
@@ -91,6 +92,7 @@ Evan Kepner
Fabien Zarifian
Fabio Zadrozny
Feng Ma
Fernando Mezzabotta Rey
Florian Bruhin
Floris Bruynooghe
Gabriel Reis

View File

@@ -18,6 +18,94 @@ with advance notice in the **Deprecations** section of releases.
.. towncrier release notes start
pytest 4.6.10 (2020-05-08)
==========================
Features
--------
- `#6870 <https://github.com/pytest-dev/pytest/issues/6870>`_: New ``Config.invocation_args`` attribute containing the unchanged arguments passed to ``pytest.main()``.
Remark: while this is technically a new feature and according to our `policy <https://docs.pytest.org/en/latest/py27-py34-deprecation.html#what-goes-into-4-6-x-releases>`_ it should not have been backported, we have opened an exception in this particular case because it fixes a serious interaction with ``pytest-xdist``, so it can also be considered a bugfix.
Trivial/Internal Changes
------------------------
- `#6404 <https://github.com/pytest-dev/pytest/issues/6404>`_: Remove usage of ``parser`` module, deprecated in Python 3.9.
pytest 4.6.9 (2020-01-04)
=========================
Bug Fixes
---------
- `#6301 <https://github.com/pytest-dev/pytest/issues/6301>`_: Fix assertion rewriting for egg-based distributions and ``editable`` installs (``pip install --editable``).
pytest 4.6.8 (2019-12-19)
=========================
Features
--------
- `#5471 <https://github.com/pytest-dev/pytest/issues/5471>`_: JUnit XML now includes a timestamp and hostname in the testsuite tag.
Bug Fixes
---------
- `#5430 <https://github.com/pytest-dev/pytest/issues/5430>`_: junitxml: Logs for failed test are now passed to junit report in case the test fails during call phase.
Trivial/Internal Changes
------------------------
- `#6345 <https://github.com/pytest-dev/pytest/issues/6345>`_: Pin ``colorama`` to ``0.4.1`` only for Python 3.4 so newer Python versions can still receive colorama updates.
pytest 4.6.7 (2019-12-05)
=========================
Bug Fixes
---------
- `#5477 <https://github.com/pytest-dev/pytest/issues/5477>`_: The XML file produced by ``--junitxml`` now correctly contain a ``<testsuites>`` root element.
- `#6044 <https://github.com/pytest-dev/pytest/issues/6044>`_: Properly ignore ``FileNotFoundError`` (``OSError.errno == NOENT`` in Python 2) exceptions when trying to remove old temporary directories,
for instance when multiple processes try to remove the same directory (common with ``pytest-xdist``
for example).
pytest 4.6.6 (2019-10-11)
=========================
Bug Fixes
---------
- `#5523 <https://github.com/pytest-dev/pytest/issues/5523>`_: Fixed using multiple short options together in the command-line (for example ``-vs``) in Python 3.8+.
- `#5537 <https://github.com/pytest-dev/pytest/issues/5537>`_: Replace ``importlib_metadata`` backport with ``importlib.metadata`` from the
standard library on Python 3.8+.
- `#5806 <https://github.com/pytest-dev/pytest/issues/5806>`_: Fix "lexer" being used when uploading to bpaste.net from ``--pastebin`` to "text".
- `#5902 <https://github.com/pytest-dev/pytest/issues/5902>`_: Fix warnings about deprecated ``cmp`` attribute in ``attrs>=19.2``.
Trivial/Internal Changes
------------------------
- `#5801 <https://github.com/pytest-dev/pytest/issues/5801>`_: Fixes python version checks (detected by ``flake8-2020``) in case python4 becomes a thing.
pytest 4.6.5 (2019-08-05)
=========================

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2004-2019 Holger Krekel and others
Copyright (c) 2004-2020 Holger Krekel and others
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in

View File

@@ -131,7 +131,7 @@ Tidelift will coordinate the fix and disclosure.
License
-------
Copyright Holger Krekel and others, 2004-2019.
Copyright Holger Krekel and others, 2004-2020.
Distributed under the terms of the `MIT`_ license, pytest is free and open source software.

View File

@@ -48,12 +48,6 @@ jobs:
# 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'
@@ -91,7 +85,7 @@ jobs:
condition: eq(variables['python.needs_vc'], True)
displayName: 'Install VC for py27'
- script: python -m pip install --upgrade pip && python -m pip install tox
- script: python -m pip install tox
displayName: 'Install tox'
- script: |

7
codecov.yml Normal file
View File

@@ -0,0 +1,7 @@
coverage:
status:
project: true
patch: true
changes: true
comment: off

View File

@@ -6,6 +6,11 @@ Release announcements
:maxdepth: 2
release-4.6.10
release-4.6.9
release-4.6.8
release-4.6.7
release-4.6.6
release-4.6.5
release-4.6.4
release-4.6.3

View File

@@ -0,0 +1,20 @@
pytest-4.6.10
=======================================
pytest 4.6.10 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
* Fernando Mez
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,20 @@
pytest-4.6.6
=======================================
pytest 4.6.6 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
* Michael Goerz
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,19 @@
pytest-4.6.7
=======================================
pytest 4.6.7 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:
* Bruno Oliveira
* Daniel Hahler
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,20 @@
pytest-4.6.8
=======================================
pytest 4.6.8 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
* Ryan Mast
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,21 @@
pytest-4.6.9
=======================================
pytest 4.6.9 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
* Felix Yan
* Hugo
Happy testing,
The pytest Development Team

View File

@@ -65,7 +65,7 @@ master_doc = "contents"
# General information about the project.
project = u"pytest"
year = datetime.datetime.utcnow().year
copyright = u"20152019 , holger krekel and pytest-dev team"
copyright = u"20152020, holger krekel and pytest-dev team"
# The language for content autogenerated by Sphinx. Refer to documentation
@@ -275,7 +275,7 @@ man_pages = [("usage", "pytest", u"pytest usage", [u"holger krekel at merlinux e
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_copyright = u"2013-2020, holger krekel et alii"
# The language of the text. It defaults to the language option
# or en if the language is not set.

View File

@@ -436,7 +436,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
items = [1, 2, 3]
print("items is %r" % items)
> a, b = items.pop()
E TypeError: 'int' object is not iterable
E TypeError: cannot unpack non-iterable int object
failure_demo.py:182: TypeError
--------------------------- Captured stdout call ---------------------------
@@ -515,7 +515,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
def test_z2_type_error(self):
items = 3
> a, b = items
E TypeError: 'int' object is not iterable
E TypeError: cannot unpack non-iterable int object
failure_demo.py:222: TypeError
______________________ TestMoreErrors.test_startswith ______________________

View File

@@ -440,7 +440,7 @@ Now we can profile which test functions execute the slowest:
test_some_are_slow.py ... [100%]
========================= slowest 3 test durations =========================
0.31s call test_some_are_slow.py::test_funcslow2
0.30s call test_some_are_slow.py::test_funcslow2
0.20s call test_some_are_slow.py::test_funcslow1
0.10s call test_some_are_slow.py::test_funcfast
========================= 3 passed in 0.12 seconds =========================

View File

@@ -28,7 +28,7 @@ Install ``pytest``
.. code-block:: bash
$ pytest --version
This is pytest version 4.x.y, imported from $PYTHON_PREFIX/lib/python3.6/site-packages/pytest.py
This is pytest version 4.x.y, imported from $PYTHON_PREFIX/lib/python3.7/site-packages/pytest.py
.. _`simpletest`:

View File

@@ -87,7 +87,7 @@ Consult the :ref:`Changelog <changelog>` page for fixes and enhancements of each
License
-------
Copyright Holger Krekel and others, 2004-2017.
Copyright Holger Krekel and others, 2004-2020.
Distributed under the terms of the `MIT`_ license, pytest is free and open source software.

View File

@@ -9,7 +9,7 @@ Distributed under the terms of the `MIT`_ license, pytest is free and open sourc
The MIT License (MIT)
Copyright (c) 2004-2017 Holger Krekel and others
Copyright (c) 2004-2020 Holger Krekel and others
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in

View File

@@ -17,9 +17,9 @@ are available on PyPI.
While pytest ``5.0`` will be the new mainstream and development version, until **January 2020**
the pytest core team plans to make bug-fix releases of the pytest ``4.6`` series by
back-porting patches to the ``4.6-maintenance`` branch that affect Python 2 users.
back-porting patches to the ``4.6.x`` branch that affect Python 2 users.
**After 2020**, the core team will no longer actively backport patches, but the ``4.6-maintenance``
**After 2020**, the core team will no longer actively backport patches, but the ``4.6.x``
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**.

View File

@@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
"""
Appends the codecov token to the 'codecov.yml' file at the root of the repository.
This is done by CI during PRs and builds on the pytest-dev repository so we can upload coverage, at least
until codecov grows some native integration like it has with Travis and AppVeyor.
See discussion in https://github.com/pytest-dev/pytest/pull/6441 for more information.
"""
import os.path
from textwrap import dedent
def main():
this_dir = os.path.dirname(__file__)
cov_file = os.path.join(this_dir, "..", "codecov.yml")
assert os.path.isfile(cov_file), "{cov_file} does not exist".format(
cov_file=cov_file
)
with open(cov_file, "a") as f:
# token from: https://codecov.io/gh/pytest-dev/pytest/settings
# use same URL to regenerate it if needed
text = dedent(
"""
codecov:
token: "1eca3b1f-31a2-4fb8-a8c3-138b441b50a7"
"""
)
f.write(text)
print("Token updated:", cov_file)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,96 @@
# -*- coding: utf-8 -*-
"""
Script used to publish GitHub release notes extracted from CHANGELOG.rst.
This script is meant to be executed after a successful deployment in Travis.
Uses the following environment variables:
* GIT_TAG: the name of the tag of the current commit.
* GH_RELEASE_NOTES_TOKEN: a personal access token with 'repo' permissions. It should be encrypted using:
$travis encrypt GH_RELEASE_NOTES_TOKEN=<token> -r pytest-dev/pytest
And the contents pasted in the ``deploy.env.secure`` section in the ``travis.yml`` file.
The script also requires ``pandoc`` to be previously installed in the system.
Requires Python3.6+.
"""
import os
import re
import sys
from pathlib import Path
import github3
import pypandoc
def publish_github_release(slug, token, tag_name, body):
github = github3.login(token=token)
owner, repo = slug.split("/")
repo = github.repository(owner, repo)
return repo.create_release(tag_name=tag_name, body=body)
def parse_changelog(tag_name):
p = Path(__file__).parent.parent / "CHANGELOG.rst"
changelog_lines = p.read_text(encoding="UTF-8").splitlines()
title_regex = re.compile(r"pytest (\d\.\d+\.\d+) \(\d{4}-\d{2}-\d{2}\)")
consuming_version = False
version_lines = []
for line in changelog_lines:
m = title_regex.match(line)
if m:
# found the version we want: start to consume lines until we find the next version title
if m.group(1) == tag_name:
consuming_version = True
# found a new version title while parsing the version we want: break out
elif consuming_version:
break
if consuming_version:
version_lines.append(line)
return "\n".join(version_lines)
def convert_rst_to_md(text):
return pypandoc.convert_text(text, "md", format="rst")
def main(argv):
if len(argv) > 1:
tag_name = argv[1]
else:
tag_name = os.environ.get("TRAVIS_TAG")
if not tag_name:
print("tag_name not given and $TRAVIS_TAG not set", file=sys.stderr)
return 1
token = os.environ.get("GH_RELEASE_NOTES_TOKEN")
if not token:
print("GH_RELEASE_NOTES_TOKEN not set", file=sys.stderr)
return 1
slug = os.environ.get("TRAVIS_REPO_SLUG")
if not slug:
print("TRAVIS_REPO_SLUG not set", file=sys.stderr)
return 1
rst_body = parse_changelog(tag_name)
md_body = convert_rst_to_md(rst_body)
if not publish_github_release(slug, token, tag_name, md_body):
print("Could not publish release notes:", file=sys.stderr)
print(md_body, file=sys.stderr)
return 5
print()
print(f"Release notes for {tag_name} published successfully:")
print(f"https://github.com/{slug}/releases/tag/{tag_name}")
print()
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv))

18
scripts/report-coverage.sh Executable file
View File

@@ -0,0 +1,18 @@
#!/usr/bin/env bash
set -e
set -x
if [ -z "$TOXENV" ]; then
python -m pip install coverage
else
# Add last TOXENV to $PATH.
PATH="$PWD/.tox/${TOXENV##*,}/bin:$PATH"
fi
python -m coverage combine
python -m coverage xml
python -m coverage report -m
# Set --connect-timeout to work around https://github.com/curl/curl/issues/4461
curl -S -L --connect-timeout 5 --retry 6 -s https://codecov.io/bash -o codecov-upload.sh
bash codecov-upload.sh -Z -X fix -f coverage.xml "$@"

View File

@@ -30,6 +30,8 @@ classifiers =
Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
platforms = unix, linux, osx, cygwin, win32
[options]

View File

@@ -13,9 +13,10 @@ INSTALL_REQUIRES = [
"atomicwrites>=1.0",
'funcsigs>=1.0;python_version<"3.0"',
'pathlib2>=2.2.0;python_version<"3.6"',
'colorama;sys_platform=="win32"',
'colorama<=0.4.1;sys_platform=="win32" and python_version=="3.4"',
'colorama;sys_platform=="win32" and python_version!="3.4"',
"pluggy>=0.12,<1.0",
"importlib-metadata>=0.12",
'importlib-metadata>=0.12;python_version<"3.8"',
"wcwidth",
]

View File

@@ -123,18 +123,13 @@ class Source(object):
""" return True if source is parseable, heuristically
deindenting it by default.
"""
from parser import suite as syntax_checker
if deindent:
source = str(self.deindent())
else:
source = str(self)
try:
# compile(source+'\n', "x", "exec")
syntax_checker(source + "\n")
except KeyboardInterrupt:
raise
except Exception:
ast.parse(source)
except (SyntaxError, ValueError, TypeError):
return False
else:
return True

View File

@@ -12,6 +12,7 @@ import _pytest._code
from ..compat import Sequence
from _pytest import outcomes
from _pytest._io.saferepr import saferepr
from _pytest.compat import ATTRS_EQ_FIELD
# The _reprcompare attribute on the util module is used by the new assertion
# interpretation code and assertion rewriter to detect this plugin was
@@ -374,7 +375,9 @@ def _compare_eq_cls(left, right, verbose, type_fns):
fields_to_check = [field for field, info in all_fields.items() if info.compare]
elif isattrs(left):
all_fields = left.__attrs_attrs__
fields_to_check = [field.name for field in all_fields if field.cmp]
fields_to_check = [
field.name for field in all_fields if getattr(field, ATTRS_EQ_FIELD)
]
same = []
diff = []

View File

@@ -13,6 +13,7 @@ import re
import sys
from contextlib import contextmanager
import attr
import py
import six
from six import text_type
@@ -61,6 +62,12 @@ else:
return None
if sys.version_info >= (3, 8):
from importlib import metadata as importlib_metadata # noqa
else:
import importlib_metadata # noqa
def _format_args(func):
return str(signature(func))
@@ -406,8 +413,8 @@ def _setup_collect_fakemodule():
pytest.collect = ModuleType("pytest.collect")
pytest.collect.__all__ = [] # used for setns
for attr in COLLECT_FAKEMODULE_ATTRIBUTES:
setattr(pytest.collect, attr, getattr(pytest, attr))
for attribute in COLLECT_FAKEMODULE_ATTRIBUTES:
setattr(pytest.collect, attribute, getattr(pytest, attribute))
if _PY2:
@@ -455,3 +462,9 @@ if six.PY2:
else:
from functools import lru_cache # noqa: F401
if getattr(attr, "__version_info__", ()) >= (19, 2):
ATTRS_EQ_FIELD = "eq"
else:
ATTRS_EQ_FIELD = "cmp"

View File

@@ -13,7 +13,7 @@ import sys
import types
import warnings
import importlib_metadata
import attr
import py
import six
from packaging.version import Version
@@ -31,10 +31,12 @@ from .findpaths import exists
from _pytest import deprecated
from _pytest._code import ExceptionInfo
from _pytest._code import filter_traceback
from _pytest.compat import importlib_metadata
from _pytest.compat import lru_cache
from _pytest.compat import safe_str
from _pytest.outcomes import fail
from _pytest.outcomes import Skipped
from _pytest.pathlib import Path
from _pytest.warning_types import PytestConfigWarning
hookimpl = HookimplMarker("pytest")
@@ -116,13 +118,13 @@ def directory_arg(path, optname):
# Plugins that cannot be disabled via "-p no:X" currently.
essential_plugins = ( # fmt: off
essential_plugins = (
"mark",
"main",
"runner",
"fixtures",
"helpconfig", # Provides -p.
) # fmt: on
)
default_plugins = essential_plugins + (
"python",
@@ -154,10 +156,15 @@ builtin_plugins = set(default_plugins)
builtin_plugins.add("pytester")
def get_config(args=None):
def get_config(args=None, plugins=None):
# subsequent calls to main will create a fresh instance
pluginmanager = PytestPluginManager()
config = Config(pluginmanager)
config = Config(
pluginmanager,
invocation_params=Config.InvocationParams(
args=args, plugins=plugins, dir=Path().resolve()
),
)
if args is not None:
# Handle any "-p no:plugin" args.
@@ -190,7 +197,7 @@ def _prepareconfig(args=None, plugins=None):
msg = "`args` parameter expected to be a list or tuple of strings, got: {!r} (type: {})"
raise TypeError(msg.format(args, type(args)))
config = get_config(args)
config = get_config(args, plugins)
pluginmanager = config.pluginmanager
try:
if plugins:
@@ -622,25 +629,116 @@ notset = Notset()
def _iter_rewritable_modules(package_files):
"""
Given an iterable of file names in a source distribution, return the "names" that should
be marked for assertion rewrite (for example the package "pytest_mock/__init__.py" should
be added as "pytest_mock" in the assertion rewrite mechanism.
This function has to deal with dist-info based distributions and egg based distributions
(which are still very much in use for "editable" installs).
Here are the file names as seen in a dist-info based distribution:
pytest_mock/__init__.py
pytest_mock/_version.py
pytest_mock/plugin.py
pytest_mock.egg-info/PKG-INFO
Here are the file names as seen in an egg based distribution:
src/pytest_mock/__init__.py
src/pytest_mock/_version.py
src/pytest_mock/plugin.py
src/pytest_mock.egg-info/PKG-INFO
LICENSE
setup.py
We have to take in account those two distribution flavors in order to determine which
names should be considered for assertion rewriting.
More information:
https://github.com/pytest-dev/pytest-mock/issues/167
"""
package_files = list(package_files)
seen_some = False
for fn in package_files:
is_simple_module = "/" not in fn and fn.endswith(".py")
is_package = fn.count("/") == 1 and fn.endswith("__init__.py")
if is_simple_module:
module_name, _ = os.path.splitext(fn)
yield module_name
# we ignore "setup.py" at the root of the distribution
if module_name != "setup":
seen_some = True
yield module_name
elif is_package:
package_name = os.path.dirname(fn)
seen_some = True
yield package_name
if not seen_some:
# at this point we did not find any packages or modules suitable for assertion
# rewriting, so we try again by stripping the first path component (to account for
# "src" based source trees for example)
# this approach lets us have the common case continue to be fast, as egg-distributions
# are rarer
new_package_files = []
for fn in package_files:
parts = fn.split("/")
new_fn = "/".join(parts[1:])
if new_fn:
new_package_files.append(new_fn)
if new_package_files:
for _module in _iter_rewritable_modules(new_package_files):
yield _module
class Config(object):
""" access to configuration values, pluginmanager and plugin hooks. """
"""
Access to configuration values, pluginmanager and plugin hooks.
:ivar PytestPluginManager pluginmanager: the plugin manager handles plugin registration and hook invocation.
:ivar argparse.Namespace option: access to command line option as attributes.
:ivar InvocationParams invocation_params:
Object containing the parameters regarding the ``pytest.main``
invocation.
Contains the followinig read-only attributes:
* ``args``: list of command-line arguments as passed to ``pytest.main()``.
* ``plugins``: list of extra plugins, might be None
* ``dir``: directory where ``pytest.main()`` was invoked from.
"""
@attr.s(frozen=True)
class InvocationParams(object):
"""Holds parameters passed during ``pytest.main()``
.. note::
Currently the environment variable PYTEST_ADDOPTS is also handled by
pytest implicitly, not being part of the invocation.
Plugins accessing ``InvocationParams`` must be aware of that.
"""
args = attr.ib()
plugins = attr.ib()
dir = attr.ib()
def __init__(self, pluginmanager, invocation_params=None, *args):
from .argparsing import Parser, FILE_OR_DIR
if invocation_params is None:
invocation_params = self.InvocationParams(
args=(), plugins=None, dir=Path().resolve()
)
def __init__(self, pluginmanager):
#: access to command line option as attributes.
#: (deprecated), use :py:func:`getoption() <_pytest.config.Config.getoption>` instead
self.option = argparse.Namespace()
from .argparsing import Parser, FILE_OR_DIR
self.invocation_params = invocation_params
_a = FILE_OR_DIR
self._parser = Parser(
@@ -657,9 +755,13 @@ class Config(object):
self._cleanup = []
self.pluginmanager.register(self, "pytestconfig")
self._configured = False
self.invocation_dir = py.path.local()
self.hook.pytest_addoption.call_historic(kwargs=dict(parser=self._parser))
@property
def invocation_dir(self):
"""Backward compatibility"""
return py.path.local(str(self.invocation_params.dir))
def add_cleanup(self, func):
""" Add a function to be called when the config object gets out of
use (usually coninciding with pytest_unconfigure)."""

View File

@@ -15,9 +15,11 @@ from __future__ import print_function
import functools
import os
import platform
import re
import sys
import time
from datetime import datetime
import py
import six
@@ -595,6 +597,8 @@ class LogXML(object):
if report.when == "call":
reporter.append_failure(report)
self.open_reports.append(report)
if not self.log_passing_tests:
reporter.write_captured_output(report)
else:
reporter.append_error(report)
elif report.skipped:
@@ -667,18 +671,19 @@ class LogXML(object):
)
logfile.write('<?xml version="1.0" encoding="utf-8"?>')
logfile.write(
Junit.testsuite(
self._get_global_properties_node(),
[x.to_xml() for x in self.node_reporters_ordered],
name=self.suite_name,
errors=self.stats["error"],
failures=self.stats["failure"],
skipped=self.stats["skipped"],
tests=numtests,
time="%.3f" % suite_time_delta,
).unicode(indent=0)
suite_node = Junit.testsuite(
self._get_global_properties_node(),
[x.to_xml() for x in self.node_reporters_ordered],
name=self.suite_name,
errors=self.stats["error"],
failures=self.stats["failure"],
skipped=self.stats["skipped"],
tests=numtests,
time="%.3f" % suite_time_delta,
timestamp=datetime.fromtimestamp(self.suite_start_time).isoformat(),
hostname=platform.node(),
)
logfile.write(Junit.testsuites([suite_node]).unicode(indent=0))
logfile.close()
def pytest_terminal_summary(self, terminalreporter):

View File

@@ -8,6 +8,7 @@ import attr
import six
from ..compat import ascii_escaped
from ..compat import ATTRS_EQ_FIELD
from ..compat import getfslineno
from ..compat import MappingMixin
from ..compat import NOTSET
@@ -377,7 +378,8 @@ class NodeKeywords(MappingMixin):
return "<NodeKeywords for node %s>" % (self.node,)
@attr.s(cmp=False, hash=False)
# mypy cannot find this overload, remove when on attrs>=19.2
@attr.s(hash=False, **{ATTRS_EQ_FIELD: False}) # type: ignore
class NodeMarkers(object):
"""
internal structure for storing marks belonging to a node

View File

@@ -77,11 +77,7 @@ def create_new_paste(contents):
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": "text", "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"))

View File

@@ -48,24 +48,38 @@ def ensure_reset_dir(path):
def on_rm_rf_error(func, path, exc, **kwargs):
"""Handles known read-only errors during rmtree."""
"""Handles known read-only errors during rmtree.
The returned value is used only by our own tests.
"""
start_path = kwargs["start_path"]
excvalue = exc[1]
exctype, excvalue = exc[:2]
# another process removed the file in the middle of the "rm_rf" (xdist for example)
# more context: https://github.com/pytest-dev/pytest/issues/5974#issuecomment-543799018
if isinstance(excvalue, OSError) and excvalue.errno == errno.ENOENT:
return False
if not isinstance(excvalue, OSError) or excvalue.errno not in (
errno.EACCES,
errno.EPERM,
):
warnings.warn(
PytestWarning("(rm_rf) error removing {}: {}".format(path, excvalue))
PytestWarning(
"(rm_rf) error removing {}\n{}: {}".format(path, exctype, excvalue)
)
)
return
return False
if func not in (os.rmdir, os.remove, os.unlink):
warnings.warn(
PytestWarning("(rm_rf) error removing {}: {}".format(path, excvalue))
PytestWarning(
"(rm_rf) unknown function {} when removing {}:\n{}: {}".format(
path, func, exctype, excvalue
)
)
)
return
return False
# Chmod + retry.
import stat
@@ -86,6 +100,7 @@ def on_rm_rf_error(func, path, exc, **kwargs):
chmod_rw(str(path))
func(path)
return True
def rm_rf(path):

View File

@@ -1124,7 +1124,7 @@ class Testdir(object):
if timeout is None:
ret = popen.wait()
elif six.PY3:
elif not six.PY2:
try:
ret = popen.wait(timeout)
except subprocess.TimeoutExpired:

View File

@@ -694,7 +694,7 @@ def raises(expected_exception, *args, **kwargs):
return RaisesContext(expected_exception, message, match_expr)
elif isinstance(args[0], str):
warnings.warn(deprecated.RAISES_EXEC, stacklevel=2)
code, = args
(code,) = args
assert isinstance(code, str)
frame = sys._getframe(1)
loc = frame.f_locals.copy()

View File

@@ -95,7 +95,7 @@ def warns(expected_warning, *args, **kwargs):
return WarningsChecker(expected_warning, match_expr=match_expr)
elif isinstance(args[0], str):
warnings.warn(WARNS_EXEC, stacklevel=2)
code, = args
(code,) = args
assert isinstance(code, str)
frame = sys._getframe(1)
loc = frame.f_locals.copy()

View File

@@ -9,11 +9,11 @@ import textwrap
import types
import attr
import importlib_metadata
import py
import six
import pytest
from _pytest.compat import importlib_metadata
from _pytest.main import EXIT_NOTESTSCOLLECTED
from _pytest.main import EXIT_USAGEERROR
from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG
@@ -223,7 +223,7 @@ class TestGeneralUsage(object):
"conftest.py:2: in foo",
" import qwerty",
"E {}: No module named {q}qwerty{q}".format(
exc_name, q="'" if six.PY3 else ""
exc_name, q="" if six.PY2 else "'"
),
]
)

View File

@@ -501,7 +501,7 @@ def test_getfslineno():
class B(object):
pass
B.__name__ = "B2"
B.__name__ = B.__qualname__ = "B2"
assert getfslineno(B)[1] == -1

View File

@@ -152,7 +152,7 @@ def test_pytest_plugins_in_non_top_level_conftest_unsupported_pyargs(
def test_pytest_plugins_in_non_top_level_conftest_unsupported_no_top_level_conftest(
testdir
testdir,
):
from _pytest.deprecated import PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST
@@ -181,7 +181,7 @@ def test_pytest_plugins_in_non_top_level_conftest_unsupported_no_top_level_conft
def test_pytest_plugins_in_non_top_level_conftest_unsupported_no_false_positives(
testdir
testdir,
):
from _pytest.deprecated import PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST

View File

@@ -464,7 +464,7 @@ class TestRequestBasic(object):
assert repr(req).find(req.function.__name__) != -1
def test_request_attributes_method(self, testdir):
item, = testdir.getitems(
(item,) = testdir.getitems(
"""
import pytest
class TestB(object):
@@ -492,7 +492,7 @@ class TestRequestBasic(object):
pass
"""
)
item1, = testdir.genitems([modcol])
(item1,) = testdir.genitems([modcol])
assert item1.name == "test_method"
arg2fixturedefs = fixtures.FixtureRequest(item1)._arg2fixturedefs
assert len(arg2fixturedefs) == 1
@@ -756,7 +756,7 @@ class TestRequestBasic(object):
def test_request_getmodulepath(self, testdir):
modcol = testdir.getmodulecol("def test_somefunc(): pass")
item, = testdir.genitems([modcol])
(item,) = testdir.genitems([modcol])
req = fixtures.FixtureRequest(item)
assert req.fspath == modcol.fspath

View File

@@ -289,7 +289,7 @@ class TestUnicodeHandling:
success = dummy_context_manager
py2_only = pytest.mark.skipif(
six.PY3, reason="bytes in raises only supported in Python 2"
not six.PY2, reason="bytes in raises only supported in Python 2"
)
@pytest.mark.parametrize(

View File

@@ -14,6 +14,7 @@ import pytest
from _pytest import outcomes
from _pytest.assertion import truncate
from _pytest.assertion import util
from _pytest.compat import ATTRS_EQ_FIELD
PY3 = sys.version_info >= (3, 0)
@@ -179,7 +180,8 @@ class TestImportHookInstallation(object):
return check
""",
"mainwrapper.py": """\
import pytest, importlib_metadata
import pytest
from _pytest.compat import importlib_metadata
class DummyEntryPoint(object):
name = 'spam'
@@ -687,7 +689,7 @@ class TestAssert_reprcompare_attrsclass(object):
@attr.s
class SimpleDataObject(object):
field_a = attr.ib()
field_b = attr.ib(cmp=False)
field_b = attr.ib(**{ATTRS_EQ_FIELD: False})
left = SimpleDataObject(1, "b")
right = SimpleDataObject(1, "b")

View File

@@ -494,7 +494,7 @@ class TestSession(object):
p = testdir.makepyfile("def test_func(): pass")
id = "::".join([p.basename, "test_func"])
items, hookrec = testdir.inline_genitems(id)
item, = items
(item,) = items
assert item.name == "test_func"
newid = item.nodeid
assert newid == id
@@ -613,9 +613,9 @@ class TestSession(object):
testdir.makepyfile("def test_func(): pass")
items, hookrec = testdir.inline_genitems()
assert len(items) == 1
item, = items
(item,) = items
items2, hookrec = testdir.inline_genitems(item.nodeid)
item2, = items2
(item2,) = items2
assert item2.name == item.name
assert item2.fspath == item.fspath
@@ -630,7 +630,7 @@ class TestSession(object):
arg = p.basename + "::TestClass::test_method"
items, hookrec = testdir.inline_genitems(arg)
assert len(items) == 1
item, = items
(item,) = items
assert item.nodeid.endswith("TestClass::test_method")
# ensure we are reporting the collection of the single test item (#2464)
assert [x.name for x in self.get_reported_items(hookrec)] == ["test_method"]

View File

@@ -6,10 +6,9 @@ from __future__ import print_function
import sys
import textwrap
import importlib_metadata
import _pytest._code
import pytest
from _pytest.compat import importlib_metadata
from _pytest.config import _iter_rewritable_modules
from _pytest.config.exceptions import UsageError
from _pytest.config.findpaths import determine_setup
@@ -20,6 +19,7 @@ from _pytest.main import EXIT_NOTESTSCOLLECTED
from _pytest.main import EXIT_OK
from _pytest.main import EXIT_TESTSFAILED
from _pytest.main import EXIT_USAGEERROR
from _pytest.pathlib import Path
class TestParseIni(object):
@@ -432,15 +432,21 @@ class TestConfigAPI(object):
@pytest.mark.parametrize(
"names, expected",
[
# dist-info based distributions root are files as will be put in PYTHONPATH
(["bar.py"], ["bar"]),
(["foo", "bar.py"], []),
(["foo", "bar.pyc"], []),
(["foo", "__init__.py"], ["foo"]),
(["foo", "bar", "__init__.py"], []),
(["foo/bar.py"], ["bar"]),
(["foo/bar.pyc"], []),
(["foo/__init__.py"], ["foo"]),
(["bar/__init__.py", "xz.py"], ["bar", "xz"]),
(["setup.py"], []),
# egg based distributions root contain the files from the dist root
(["src/bar/__init__.py"], ["bar"]),
(["src/bar/__init__.py", "setup.py"], ["bar"]),
(["source/python/bar/__init__.py", "setup.py"], ["bar"]),
],
)
def test_iter_rewritable_modules(self, names, expected):
assert list(_iter_rewritable_modules(["/".join(names)])) == expected
assert list(_iter_rewritable_modules(names)) == expected
class TestConfigFromdictargs(object):
@@ -1217,6 +1223,29 @@ def test_config_does_not_load_blocked_plugin_from_args(testdir):
assert result.ret == EXIT_USAGEERROR
def test_invocation_args(testdir):
"""Ensure that Config.invocation_* arguments are correctly defined"""
class DummyPlugin(object):
pass
p = testdir.makepyfile("def test(): pass")
plugin = DummyPlugin()
rec = testdir.inline_run(p, "-v", plugins=[plugin])
calls = rec.getcalls("pytest_runtest_protocol")
assert len(calls) == 1
call = calls[0]
config = call.item.config
assert config.invocation_params.args == [p, "-v"]
assert config.invocation_params.dir == Path(str(testdir.tmpdir))
plugins = config.invocation_params.plugins
assert len(plugins) == 2
assert plugins[0] is plugin
assert type(plugins[1]).__name__ == "Collect" # installed by testdir.inline_run()
@pytest.mark.parametrize(
"plugin",
[

View File

@@ -3,7 +3,7 @@ from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import importlib_metadata
from _pytest.compat import importlib_metadata
def test_pytest_entry_points_are_identical():

View File

@@ -4,7 +4,9 @@ from __future__ import division
from __future__ import print_function
import os
import platform
import sys
from datetime import datetime
from xml.dom import minidom
import py
@@ -47,6 +49,16 @@ class DomNode(object):
def _by_tag(self, tag):
return self.__node.getElementsByTagName(tag)
@property
def children(self):
return [type(self)(x) for x in self.__node.childNodes]
@property
def get_unique_child(self):
children = self.children
assert len(children) == 1
return children[0]
def find_nth_by_tag(self, tag, n):
items = self._by_tag(tag)
try:
@@ -81,7 +93,7 @@ class DomNode(object):
return self.__node.tagName
@property
def next_siebling(self):
def next_sibling(self):
return type(self)(self.__node.nextSibling)
@@ -135,6 +147,30 @@ class TestPython(object):
node = dom.find_first_by_tag("testsuite")
node.assert_attr(name="pytest", errors=1, failures=2, skipped=1, tests=5)
def test_hostname_in_xml(self, testdir):
testdir.makepyfile(
"""
def test_pass():
pass
"""
)
result, dom = runandparse(testdir)
node = dom.find_first_by_tag("testsuite")
node.assert_attr(hostname=platform.node())
def test_timestamp_in_xml(self, testdir):
testdir.makepyfile(
"""
def test_pass():
pass
"""
)
start_time = datetime.now()
result, dom = runandparse(testdir)
node = dom.find_first_by_tag("testsuite")
timestamp = datetime.strptime(node["timestamp"], "%Y-%m-%dT%H:%M:%S.%f")
assert start_time <= timestamp < datetime.now()
def test_timing_function(self, testdir):
testdir.makepyfile(
"""
@@ -390,11 +426,11 @@ class TestPython(object):
fnode = tnode.find_first_by_tag("failure")
fnode.assert_attr(message="ValueError: 42")
assert "ValueError" in fnode.toxml()
systemout = fnode.next_siebling
systemout = fnode.next_sibling
assert systemout.tag == "system-out"
assert "hello-stdout" in systemout.toxml()
assert "info msg" not in systemout.toxml()
systemerr = systemout.next_siebling
systemerr = systemout.next_sibling
assert systemerr.tag == "system-err"
assert "hello-stderr" in systemerr.toxml()
assert "info msg" not in systemerr.toxml()
@@ -1101,6 +1137,20 @@ def test_random_report_log_xdist(testdir, monkeypatch):
assert failed == ["test_x[22]"]
def test_root_testsuites_tag(testdir):
testdir.makepyfile(
"""
def test_x():
pass
"""
)
_, dom = runandparse(testdir)
root = dom.get_unique_child
assert root.tag == "testsuites"
suite_node = root.get_unique_child
assert suite_node.tag == "testsuite"
def test_runs_twice(testdir):
f = testdir.makepyfile(
"""
@@ -1359,3 +1409,39 @@ def test_logging_passing_tests_disabled_does_not_log_test_output(testdir):
node = dom.find_first_by_tag("testcase")
assert len(node.find_by_tag("system-err")) == 0
assert len(node.find_by_tag("system-out")) == 0
@pytest.mark.parametrize("junit_logging", ["no", "system-out", "system-err"])
def test_logging_passing_tests_disabled_logs_output_for_failing_test_issue5430(
testdir, junit_logging
):
testdir.makeini(
"""
[pytest]
junit_log_passing_tests=False
"""
)
testdir.makepyfile(
"""
import pytest
import logging
import sys
def test_func():
logging.warning('hello')
assert 0
"""
)
result, dom = runandparse(testdir, "-o", "junit_logging=%s" % junit_logging)
assert result.ret == 1
node = dom.find_first_by_tag("testcase")
if junit_logging == "system-out":
assert len(node.find_by_tag("system-err")) == 0
assert len(node.find_by_tag("system-out")) == 1
elif junit_logging == "system-err":
assert len(node.find_by_tag("system-err")) == 1
assert len(node.find_by_tag("system-out")) == 0
else:
assert junit_logging == "no"
assert len(node.find_by_tag("system-err")) == 0
assert len(node.find_by_tag("system-out")) == 0

View File

@@ -1008,7 +1008,7 @@ def test_markers_from_parametrize(testdir):
def test_pytest_param_id_requires_string():
with pytest.raises(TypeError) as excinfo:
pytest.param(id=True)
msg, = excinfo.value.args
(msg,) = excinfo.value.args
if six.PY2:
assert msg == "Expected id to be a string, got <type 'bool'>: True"
else:
@@ -1025,7 +1025,7 @@ def test_pytest_param_warning_on_unknown_kwargs():
# typo, should be marks=
pytest.param(1, 2, mark=pytest.mark.xfail())
assert warninfo[0].filename == __file__
msg, = warninfo[0].message.args
(msg,) = warninfo[0].message.args
assert msg == (
"pytest.param() got unexpected keyword arguments: ['mark'].\n"
"This will be an error in future versions."

View File

@@ -209,7 +209,7 @@ class TestEnvironWarnings(object):
VAR_NAME = u"PYTEST_INTERNAL_MY_VAR"
@pytest.mark.skipif(six.PY3, reason="Python 2 only test")
@pytest.mark.skipif(not six.PY2, reason="Python 2 only test")
def test_setenv_unicode_key(self, monkeypatch):
with pytest.warns(
pytest.PytestWarning,
@@ -217,7 +217,7 @@ class TestEnvironWarnings(object):
):
monkeypatch.setenv(self.VAR_NAME, "2")
@pytest.mark.skipif(six.PY3, reason="Python 2 only test")
@pytest.mark.skipif(not six.PY2, reason="Python 2 only test")
def test_delenv_unicode_key(self, monkeypatch):
with pytest.warns(
pytest.PytestWarning,

View File

@@ -74,7 +74,7 @@ class TestPasteCapture(object):
"""
)
result = testdir.runpytest("--pastebin=all")
if sys.version_info[0] == 3:
if sys.version_info[0] >= 3:
expected_msg = "*assert '' == 1*"
else:
expected_msg = "*assert '\\xe2\\x98\\xba' == 1*"
@@ -126,7 +126,7 @@ class TestPaste(object):
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 = "text"
assert url == "https://bpaste.net"
assert "lexer=%s" % lexer in data.decode()
assert "code=full-paste-contents" in data.decode()

View File

@@ -225,7 +225,7 @@ class TestWarns(object):
assert len(warninfo) == 3
for w in warninfo:
assert w.filename == __file__
msg, = w.message.args
(msg,) = w.message.args
assert msg.startswith("warns(..., 'code(as_a_string)') is deprecated")
def test_function(self):

View File

@@ -136,7 +136,7 @@ class TestEvaluator(object):
)
def test_skipif_class(self, testdir):
item, = testdir.getitems(
(item,) = testdir.getitems(
"""
import pytest
class TestClass(object):

View File

@@ -273,7 +273,7 @@ class TestNumberedDir(object):
registry = []
register_cleanup_lock_removal(lock, register=registry.append)
cleanup_func, = registry
(cleanup_func,) = registry
assert lock.is_file()
@@ -398,9 +398,14 @@ class TestRmRf:
on_rm_rf_error(os.unlink, str(fn), exc_info, start_path=tmp_path)
assert fn.is_file()
# we ignore FileNotFoundError
file_not_found = OSError()
file_not_found.errno = errno.ENOENT
exc_info = (None, file_not_found, None)
assert not on_rm_rf_error(None, str(fn), exc_info, start_path=tmp_path)
permission_error = OSError()
permission_error.errno = errno.EACCES
# unknown function
with pytest.warns(pytest.PytestWarning):
exc_info = (None, permission_error, None)

View File

@@ -388,7 +388,7 @@ def test_testcase_custom_exception_info(testdir, type):
def test_testcase_totally_incompatible_exception_info(testdir):
item, = testdir.getitems(
(item,) = testdir.getitems(
"""
from unittest import TestCase
class MyTestCase(TestCase):

View File

@@ -569,7 +569,7 @@ class TestDeprecationWarningsByDefault:
assert WARNINGS_SUMMARY_HEADER not in result.stdout.str()
@pytest.mark.skipif(six.PY3, reason="Python 2 only issue")
@pytest.mark.skipif(not six.PY2, 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"

17
tox.ini
View File

@@ -11,6 +11,7 @@ envlist =
py36
py37
py38
py39
pypy
pypy3
{py27,py37}-{pexpect,xdist,twisted,numpy,pluggymaster}
@@ -93,7 +94,7 @@ commands =
[testenv:regen]
changedir = doc/en
skipsdist = True
basepython = python3.6
basepython = python3
deps =
sphinx
PyYAML
@@ -119,13 +120,14 @@ changedir = testing/freeze
# Disable PEP 517 with pip, which does not work with PyInstaller currently.
deps =
pyinstaller
setuptools < 45.0.0
commands =
{envpython} create_executable.py
{envpython} tox_run.py
[testenv:release]
decription = do a release, required posarg of the version number
basepython = python3.6
basepython = python3
usedevelop = True
passenv = *
deps =
@@ -136,6 +138,17 @@ deps =
wheel
commands = python scripts/release.py {posargs}
[testenv:publish_gh_release_notes]
description = create GitHub release after deployment
basepython = python3
usedevelop = True
passenv = GH_RELEASE_NOTES_TOKEN TRAVIS_TAG TRAVIS_REPO_SLUG
deps =
github3.py
pypandoc
commands = python scripts/publish_gh_release_notes.py
[pytest]
minversion = 2.0
addopts = -ra -p pytester --strict-markers