diff --git a/.coveragerc b/.coveragerc index b06629a8a8f..a335557d4f1 100644 --- a/.coveragerc +++ b/.coveragerc @@ -25,5 +25,7 @@ exclude_lines = ^\s*raise NotImplementedError\b ^\s*return NotImplemented\b ^\s*assert False(,|$) + ^\s*assert_never\( ^\s*if TYPE_CHECKING: + ^\s*@overload( |$) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE/1_bug_report.md similarity index 52% rename from .github/ISSUE_TEMPLATE.md rename to .github/ISSUE_TEMPLATE/1_bug_report.md index fb81416dd5e..0fc3e06cd2c 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE/1_bug_report.md @@ -1,10 +1,16 @@ +--- +name: 🐛 Bug Report +about: Report errors and problems + +--- + -- [ ] a detailed description of the bug or suggestion +- [ ] a detailed description of the bug or problem you are having - [ ] output of `pip list` from the virtual environment you are using - [ ] pytest and operating system versions - [ ] minimal example if possible diff --git a/.github/ISSUE_TEMPLATE/2_feature_request.md b/.github/ISSUE_TEMPLATE/2_feature_request.md new file mode 100644 index 00000000000..01fe96295ea --- /dev/null +++ b/.github/ISSUE_TEMPLATE/2_feature_request.md @@ -0,0 +1,25 @@ +--- +name: 🚀 Feature Request +about: Ideas for new features and improvements + +--- + + + +#### What's the problem this feature will solve? + + +#### Describe the solution you'd like + + + + +#### Alternative Solutions + + +#### Additional context + diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000000..742d2e4d668 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: ❓ Support Question + url: https://github.com/pytest-dev/pytest/discussions + about: Use GitHub's new Discussions feature for questions diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 9408fceafe3..5e7282bfd77 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -13,7 +13,7 @@ If this change fixes an issue, please: Unless your change is trivial or a small documentation fix (e.g., a typo or reword of a small section) please: -- [ ] Create a new changelog file in the `changelog` folder, with a name like `..rst`. See [changelog/README.rst](https://github.com/pytest-dev/pytest/blob/master/changelog/README.rst) for details. +- [ ] Create a new changelog file in the `changelog` folder, with a name like `..rst`. See [changelog/README.rst](https://github.com/pytest-dev/pytest/blob/main/changelog/README.rst) for details. Write sentences in the **past or present tense**, examples: diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..294b13743e2 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,17 @@ +version: 2 +updates: +- package-ecosystem: pip + directory: "/testing/plugins_integration" + schedule: + interval: weekly + time: "03:00" + open-pull-requests-limit: 10 + allow: + - dependency-type: direct + - dependency-type: indirect +- package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + time: "03:00" + open-pull-requests-limit: 10 diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml new file mode 100644 index 00000000000..38ce7260278 --- /dev/null +++ b/.github/workflows/backport.yml @@ -0,0 +1,51 @@ +name: backport + +on: + # Note that `pull_request_target` has security implications: + # https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ + # In particular: + # - Only allow triggers that can be used only be trusted users + # - Don't execute any code from the target branch + # - Don't use cache + pull_request_target: + types: [labeled] + +# Set permissions at the job level. +permissions: {} + +jobs: + backport: + if: startsWith(github.event.label.name, 'backport ') && github.event.pull_request.merged + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + persist-credentials: true + + - name: Create backport PR + run: | + set -eux + + git config --global user.name "pytest bot" + git config --global user.email "pytestbot@gmail.com" + + label='${{ github.event.label.name }}' + target_branch="${label#backport }" + backport_branch=backport-${{ github.event.number }}-to-"${target_branch}" + subject="[$target_branch] $(gh pr view --json title -q .title ${{ github.event.number }})" + + git checkout origin/"${target_branch}" -b "${backport_branch}" + git cherry-pick -x --mainline 1 ${{ github.event.pull_request.merge_commit_sha }} + git commit --amend --message "$subject" + git push --set-upstream origin --force-with-lease "${backport_branch}" + gh pr create \ + --base "${target_branch}" \ + --title "${subject}" \ + --body "Backport of PR #${{ github.event.number }} to $target_branch branch. PR created by backport workflow." + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 00000000000..b059b24d3fb --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,101 @@ +name: deploy + +on: + workflow_dispatch: + inputs: + version: + description: 'Release version' + required: true + default: '1.2.3' + + +# Set permissions at the job level. +permissions: {} + +jobs: + package: + runs-on: ubuntu-latest + env: + SETUPTOOLS_SCM_PRETEND_VERSION: ${{ github.event.inputs.version }} + timeout-minutes: 10 + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Build and Check Package + uses: hynek/build-and-inspect-python-package@v1.5.4 + + deploy: + if: github.repository == 'pytest-dev/pytest' + needs: [package] + runs-on: ubuntu-latest + environment: deploy + timeout-minutes: 30 + permissions: + id-token: write + contents: write + steps: + - uses: actions/checkout@v4 + + - name: Download Package + uses: actions/download-artifact@v3 + with: + name: Packages + path: dist + + - name: Publish package to PyPI + uses: pypa/gh-action-pypi-publish@v1.8.11 + + - name: Push tag + run: | + git config user.name "pytest bot" + git config user.email "pytestbot@gmail.com" + git tag --annotate --message=v${{ github.event.inputs.version }} ${{ github.event.inputs.version }} ${{ github.sha }} + git push origin ${{ github.event.inputs.version }} + + release-notes: + + # todo: generate the content in the build job + # the goal being of using a github action script to push the release data + # after success instead of creating a complete python/tox env + needs: [deploy] + runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Download Package + uses: actions/download-artifact@v4 + with: + name: Packages + path: dist + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install tox + run: | + python -m pip install --upgrade pip + pip install --upgrade tox + + - name: Generate release notes + run: | + sudo apt-get install pandoc + tox -e generate-gh-release-notes -- ${{ github.event.inputs.version }} scripts/latest-release-notes.md + + - name: Publish GitHub Release + uses: softprops/action-gh-release@v1 + with: + body_path: scripts/latest-release-notes.md + files: dist/* + tag_name: ${{ github.event.inputs.version }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 056c8d3dbd9..00000000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,212 +0,0 @@ -name: main - -on: - push: - branches: - - master - - "[0-9]+.[0-9]+.x" - tags: - - "*" - - pull_request: - branches: - - master - - "[0-9]+.[0-9]+.x" - -jobs: - build: - runs-on: ${{ matrix.os }} - - strategy: - fail-fast: false - matrix: - name: [ - "windows-py35", - "windows-py36", - "windows-py37", - "windows-py37-pluggy", - "windows-py38", - - "ubuntu-py35", - "ubuntu-py36", - "ubuntu-py37", - "ubuntu-py37-pluggy", - "ubuntu-py37-freeze", - "ubuntu-py38", - "ubuntu-py39", - "ubuntu-pypy3", - - "macos-py37", - "macos-py38", - - "docs", - "doctesting", - ] - - include: - - 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" - - name: "windows-py37" - python: "3.7" - os: windows-latest - tox_env: "py37-numpy" - - name: "windows-py37-pluggy" - python: "3.7" - os: windows-latest - tox_env: "py37-pluggymaster-xdist" - - name: "windows-py38" - python: "3.8" - os: windows-latest - tox_env: "py38-unittestextras" - use_coverage: true - - - name: "ubuntu-py35" - python: "3.5" - os: ubuntu-latest - tox_env: "py35-xdist" - - name: "ubuntu-py36" - python: "3.6" - os: ubuntu-latest - tox_env: "py36-xdist" - - name: "ubuntu-py37" - python: "3.7" - os: ubuntu-latest - tox_env: "py37-lsof-numpy-oldattrs-pexpect" - use_coverage: true - - name: "ubuntu-py37-pluggy" - python: "3.7" - os: ubuntu-latest - tox_env: "py37-pluggymaster-xdist" - - name: "ubuntu-py37-freeze" - python: "3.7" - os: ubuntu-latest - tox_env: "py37-freeze" - - name: "ubuntu-py38" - python: "3.8" - os: ubuntu-latest - tox_env: "py38-xdist" - - name: "ubuntu-py39" - python: "3.9-dev" - os: ubuntu-latest - tox_env: "py39-xdist" - - name: "ubuntu-pypy3" - python: "pypy3" - os: ubuntu-latest - tox_env: "pypy3-xdist" - - - name: "macos-py37" - python: "3.7" - os: macos-latest - tox_env: "py37-xdist" - - name: "macos-py38" - python: "3.8" - os: macos-latest - tox_env: "py38-xdist" - use_coverage: true - - - name: "docs" - python: "3.7" - os: ubuntu-latest - tox_env: "docs" - - name: "doctesting" - python: "3.7" - os: ubuntu-latest - tox_env: "doctesting" - use_coverage: true - - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Set up Python ${{ matrix.python }} - uses: actions/setup-python@v2 - if: matrix.python != '3.9-dev' - with: - python-version: ${{ matrix.python }} - - name: Set up Python ${{ matrix.python }} (deadsnakes) - uses: deadsnakes/action@v1.0.0 - if: matrix.python == '3.9-dev' - 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 -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 }} - - linting: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - - name: set PY - run: echo "::set-env name=PY::$(python -c 'import hashlib, sys;print(hashlib.sha256(sys.version.encode()+sys.executable.encode()).hexdigest())')" - - uses: actions/cache@v1 - with: - path: ~/.cache/pre-commit - key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }} - - run: pip install tox - - run: tox -e linting - - 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@v2 - with: - fetch-depth: 0 - - name: Set up Python - uses: actions/setup-python@v2 - 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 diff --git a/.github/workflows/prepare-release-pr.yml b/.github/workflows/prepare-release-pr.yml new file mode 100644 index 00000000000..1bb23fab844 --- /dev/null +++ b/.github/workflows/prepare-release-pr.yml @@ -0,0 +1,52 @@ +name: prepare release pr + +on: + workflow_dispatch: + inputs: + branch: + description: 'Branch to base the release from' + required: true + default: '' + major: + description: 'Major release? (yes/no)' + required: true + default: 'no' + prerelease: + description: 'Prerelease (ex: rc1). Leave empty if not a pre-release.' + required: false + default: '' + +# Set permissions at the job level. +permissions: {} + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.8" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install --upgrade setuptools tox + + - name: Prepare release PR (minor/patch release) + if: github.event.inputs.major == 'no' + run: | + tox -e prepare-release-pr -- ${{ github.event.inputs.branch }} ${{ github.token }} --prerelease='${{ github.event.inputs.prerelease }}' + + - name: Prepare release PR (major release) + if: github.event.inputs.major == 'yes' + run: | + tox -e prepare-release-pr -- ${{ github.event.inputs.branch }} ${{ github.token }} --major --prerelease='${{ github.event.inputs.prerelease }}' diff --git a/.github/workflows/release-on-comment.yml b/.github/workflows/release-on-comment.yml deleted file mode 100644 index 94863d896b9..00000000000 --- a/.github/workflows/release-on-comment.yml +++ /dev/null @@ -1,31 +0,0 @@ -# part of our release process, see `release-on-comment.py` -name: release on comment - -on: - issues: - types: [opened, edited] - issue_comment: - types: [created, edited] - -jobs: - build: - runs-on: ubuntu-latest - - if: (github.event.comment && startsWith(github.event.comment.body, '@pytestbot please')) || (github.event.issue && !github.event.comment && startsWith(github.event.issue.body, '@pytestbot please')) - - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: "3.8" - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install --upgrade setuptools tox - - name: Prepare release - run: | - tox -e release-on-comment -- $GITHUB_EVENT_PATH ${{ secrets.chatops }} diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 00000000000..3f83839cd02 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,23 @@ +name: close needs-information issues +on: + schedule: + - cron: "30 1 * * *" + workflow_dispatch: + +jobs: + close-issues: + runs-on: ubuntu-latest + permissions: + issues: write + steps: + - uses: actions/stale@v9 + with: + debug-only: false + days-before-issue-stale: 14 + days-before-issue-close: 7 + only-labels: "status: needs information" + stale-issue-label: "stale" + stale-issue-message: "This issue is stale because it has been open for 14 days with no activity." + close-issue-message: "This issue was closed because it has been inactive for 7 days since being marked as stale." + days-before-pr-stale: -1 + days-before-pr-close: -1 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000000..9fbd273bcfa --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,213 @@ +name: test + +on: + push: + branches: + - main + - "[0-9]+.[0-9]+.x" + - "test-me-*" + tags: + - "[0-9]+.[0-9]+.[0-9]+" + - "[0-9]+.[0-9]+.[0-9]+rc[0-9]+" + + pull_request: + branches: + - main + - "[0-9]+.[0-9]+.x" + +env: + PYTEST_ADDOPTS: "--color=yes" + +# Cancel running jobs for the same workflow and branch. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +# Set permissions at the job level. +permissions: {} + +jobs: + package: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + persist-credentials: false + - name: Build and Check Package + uses: hynek/build-and-inspect-python-package@v1.5.4 + + build: + needs: [package] + + runs-on: ${{ matrix.os }} + timeout-minutes: 45 + permissions: + contents: read + + strategy: + fail-fast: false + matrix: + name: [ + "windows-py38", + "windows-py38-pluggy", + "windows-py39", + "windows-py310", + "windows-py311", + "windows-py312", + + "ubuntu-py38", + "ubuntu-py38-pluggy", + "ubuntu-py38-freeze", + "ubuntu-py39", + "ubuntu-py310", + "ubuntu-py311", + "ubuntu-py312", + "ubuntu-pypy3", + + "macos-py38", + "macos-py39", + "macos-py310", + "macos-py312", + + "doctesting", + "plugins", + ] + + include: + - name: "windows-py38" + python: "3.8" + os: windows-latest + tox_env: "py38-unittestextras" + use_coverage: true + - name: "windows-py38-pluggy" + python: "3.8" + os: windows-latest + tox_env: "py38-pluggymain-pylib-xdist" + - name: "windows-py39" + python: "3.9" + os: windows-latest + tox_env: "py39-xdist" + - name: "windows-py310" + python: "3.10" + os: windows-latest + tox_env: "py310-xdist" + - name: "windows-py311" + python: "3.11" + os: windows-latest + tox_env: "py311" + - name: "windows-py312" + python: "3.12-dev" + os: windows-latest + tox_env: "py312" + + - name: "ubuntu-py38" + python: "3.8" + os: ubuntu-latest + tox_env: "py38-lsof-numpy-pexpect" + use_coverage: true + - name: "ubuntu-py38-pluggy" + python: "3.8" + os: ubuntu-latest + tox_env: "py38-pluggymain-pylib-xdist" + - name: "ubuntu-py38-freeze" + python: "3.8" + os: ubuntu-latest + tox_env: "py38-freeze" + - name: "ubuntu-py39" + python: "3.9" + os: ubuntu-latest + tox_env: "py39-xdist" + - name: "ubuntu-py310" + python: "3.10" + os: ubuntu-latest + tox_env: "py310-xdist" + - name: "ubuntu-py311" + python: "3.11" + os: ubuntu-latest + tox_env: "py311" + use_coverage: true + - name: "ubuntu-py312" + python: "3.12-dev" + os: ubuntu-latest + tox_env: "py312" + use_coverage: true + - name: "ubuntu-pypy3" + python: "pypy-3.8" + os: ubuntu-latest + tox_env: "pypy3-xdist" + + - name: "macos-py38" + python: "3.8" + os: macos-latest + tox_env: "py38-xdist" + - name: "macos-py39" + python: "3.9" + os: macos-latest + tox_env: "py39-xdist" + use_coverage: true + - name: "macos-py310" + python: "3.10" + os: macos-latest + tox_env: "py310-xdist" + - name: "macos-py312" + python: "3.12-dev" + os: macos-latest + tox_env: "py312-xdist" + + - name: "plugins" + python: "3.12" + os: ubuntu-latest + tox_env: "plugins" + + - name: "doctesting" + python: "3.8" + os: ubuntu-latest + tox_env: "doctesting" + use_coverage: true + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Download Package + uses: actions/download-artifact@v3 + with: + name: Packages + path: dist + + - name: Set up Python ${{ matrix.python }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python }} + check-latest: ${{ endsWith(matrix.python, '-dev') }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install tox coverage + + - name: Test without coverage + if: "! matrix.use_coverage" + shell: bash + run: tox run -e ${{ matrix.tox_env }} --installpkg `find dist/*.tar.gz` + + - name: Test with coverage + if: "matrix.use_coverage" + shell: bash + run: tox run -e ${{ matrix.tox_env }}-coverage --installpkg `find dist/*.tar.gz` + + - name: Generate coverage report + if: "matrix.use_coverage" + run: python -m coverage xml + + - name: Upload coverage to Codecov + if: "matrix.use_coverage" + uses: codecov/codecov-action@v3 + continue-on-error: true + with: + fail_ci_if_error: true + files: ./coverage.xml + verbose: true diff --git a/.github/workflows/update-plugin-list.yml b/.github/workflows/update-plugin-list.yml new file mode 100644 index 00000000000..349d5f52977 --- /dev/null +++ b/.github/workflows/update-plugin-list.yml @@ -0,0 +1,57 @@ +name: Update Plugin List + +on: + schedule: + # At 00:00 on Sunday. + # https://crontab.guru + - cron: '0 0 * * 0' + workflow_dispatch: + +# Set permissions at the job level. +permissions: {} + +jobs: + update-plugin-list: + if: github.repository_owner == 'pytest-dev' + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + cache: pip + - name: requests-cache + uses: actions/cache@v3 + with: + path: ~/.cache/pytest-plugin-list/ + key: plugins-http-cache-${{ github.run_id }} # Can use time based key as well + restore-keys: plugins-http-cache- + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install packaging requests tabulate[widechars] tqdm requests-cache platformdirs + + + - name: Update Plugin List + run: python scripts/update-plugin-list.py + + - name: Create Pull Request + uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 + with: + commit-message: '[automated] Update plugin list' + author: 'pytest bot ' + branch: update-plugin-list/patch + delete-branch: true + branch-suffix: short-commit-hash + title: '[automated] Update plugin list' + body: '[automated] Update plugin list' diff --git a/.gitignore b/.gitignore index faea9eac03f..3cac2474a59 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,7 @@ issue/ env/ .env/ .venv/ +/pythonenv*/ 3rdparty/ .tox .cache @@ -49,6 +50,10 @@ coverage.xml .project .settings .vscode +__pycache__/ # generated by pip pip-wheel-metadata/ + +# pytest debug logs generated via --debug +pytestdebug.log diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index dc371720417..be1f03bd7fa 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,16 +1,16 @@ repos: - repo: https://github.com/psf/black - rev: 19.10b0 + rev: 23.12.1 hooks: - id: black args: [--safe, --quiet] - repo: https://github.com/asottile/blacken-docs - rev: v1.7.0 + rev: 1.16.0 hooks: - id: blacken-docs - additional_dependencies: [black==19.10b0] + additional_dependencies: [black==23.7.0] - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.1.0 + rev: v4.5.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer @@ -20,34 +20,58 @@ repos: - id: debug-statements exclude: _pytest/(debugging|hookspec).py language_version: python3 -- repo: https://gitlab.com/pycqa/flake8 - rev: 3.8.2 +- repo: https://github.com/PyCQA/autoflake + rev: v2.2.1 + hooks: + - id: autoflake + name: autoflake + args: ["--in-place", "--remove-unused-variables", "--remove-all-unused-imports"] + language: python + files: \.py$ +- repo: https://github.com/PyCQA/flake8 + rev: 6.1.0 hooks: - id: flake8 language_version: python3 - additional_dependencies: [flake8-typing-imports==1.9.0] -- repo: https://github.com/asottile/reorder_python_imports - rev: v2.3.0 + additional_dependencies: + - flake8-typing-imports==1.12.0 + - flake8-docstrings==1.5.0 +- repo: https://github.com/asottile/reorder-python-imports + rev: v3.12.0 hooks: - id: reorder-python-imports - args: ['--application-directories=.:src', --py3-plus] + args: ['--application-directories=.:src', --py38-plus] - repo: https://github.com/asottile/pyupgrade - rev: v2.4.4 + rev: v3.15.0 hooks: - id: pyupgrade - args: [--py3-plus] + args: [--py38-plus] - repo: https://github.com/asottile/setup-cfg-fmt - rev: v1.9.0 + rev: v2.5.0 hooks: - id: setup-cfg-fmt - # TODO: when upgrading setup-cfg-fmt this can be removed - args: [--max-py-version=3.9] + args: ["--max-py-version=3.12", "--include-version-classifiers"] +- repo: https://github.com/pre-commit/pygrep-hooks + rev: v1.10.0 + hooks: + - id: python-use-type-annotations - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.780 # NOTE: keep this in sync with setup.cfg. + rev: v1.8.0 hooks: - id: mypy - files: ^(src/|testing/) + files: ^(src/|testing/|scripts/) args: [] + additional_dependencies: + - iniconfig>=1.1.0 + - attrs>=19.2.0 + - pluggy + - packaging + - tomli + - types-pkg_resources + - types-tabulate + # for mypy running on python>=3.11 since exceptiongroup is only a dependency + # on <3.11 + - exceptiongroup>=1.0.0rc8 - repo: local hooks: - id: rst @@ -70,9 +94,17 @@ repos: _code\.| builtin\.| code\.| - io\.(BytesIO|saferepr|TerminalWriter)| + io\.| path\.local\.sysfind| process\.| - std\. + std\.| + error\.| + xml\. ) types: [python] + - id: py-path-deprecated + name: py.path usage is deprecated + exclude: docs|src/_pytest/deprecated.py|testing/deprecated_test.py|src/_pytest/legacypath.py + language: pygrep + entry: \bpy\.path\.local + types: [python] diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 00000000000..266d4e07aea --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,26 @@ +version: 2 + +python: + install: + # Install pytest first, then doc/en/requirements.txt. + # This order is important to honor any pins in doc/en/requirements.txt + # when the pinned library is also a dependency of pytest. + - method: pip + path: . + - requirements: doc/en/requirements.txt + +sphinx: + configuration: doc/en/conf.py + fail_on_warning: true + +build: + os: ubuntu-20.04 + tools: + python: "3.9" + apt_packages: + - inkscape + +formats: + - epub + - pdf + - htmlzip diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 5c85dfe1f59..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,60 +0,0 @@ -language: python -dist: trusty -python: '3.5.1' -cache: false - -env: - global: - - PYTEST_ADDOPTS=-vv - -# setuptools-scm needs all tags in order to obtain a proper version -git: - depth: false - -install: - - python -m pip install --upgrade --pre tox - -jobs: - include: - # Coverage for Python 3.5.{0,1} specific code, mostly typing related. - - env: TOXENV=py35 PYTEST_COVERAGE=1 PYTEST_ADDOPTS="-k test_raises_cyclic_reference" - before_install: - # Work around https://github.com/jaraco/zipp/issues/40. - - python -m pip install -U 'setuptools>=34.4.0' virtualenv==16.7.9 - -before_script: - - | - # Do not (re-)upload coverage with cron runs. - if [[ "$TRAVIS_EVENT_TYPE" = cron ]]; then - PYTEST_COVERAGE=0 - fi - - | - if [[ "$PYTEST_COVERAGE" = 1 ]]; then - export COVERAGE_FILE="$PWD/.coverage" - export COVERAGE_PROCESS_START="$PWD/.coveragerc" - export _PYTEST_TOX_COVERAGE_RUN="coverage run -m" - export _PYTEST_TOX_EXTRA_DEP=coverage-enable-subprocess - fi - -script: tox - -after_success: - - | - if [[ "$PYTEST_COVERAGE" = 1 ]]; then - env CODECOV_NAME="$TOXENV-$TRAVIS_OS_NAME" scripts/report-coverage.sh -F Travis - fi - -notifications: - irc: - channels: - - "chat.freenode.net#pytest" - on_success: change - on_failure: change - skip_join: true - email: - - pytest-commit@python.org - -branches: - only: - - master - - /^\d+\.\d+\.x$/ diff --git a/AUTHORS b/AUTHORS index b28e5613389..a4f4d84105a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -5,14 +5,22 @@ Contributors include:: Aaron Coleman Abdeali JK +Abdelrahman Elbehery Abhijeet Kasurde Adam Johnson +Adam Stewart Adam Uhlir Ahn Ki-Wook +Akhilesh Ramakrishnan Akiomi Kamakura Alan Velasco +Alessio Izzo +Alex Jones +Alex Lambson Alexander Johnson +Alexander King Alexei Kozlenok +Alice Purcell Allan Feldman Aly Sivji Amir Elkess @@ -21,7 +29,9 @@ Anders Hovmöller Andras Mitzki Andras Tim Andrea Cimatoribus +Andreas Motl Andreas Zeidler +Andrew Shapton Andrey Paramonov Andrzej Klajnert Andrzej Ostrowski @@ -29,24 +39,36 @@ Andy Freeland Anthon van der Neut Anthony Shaw Anthony Sottile +Anton Grinevich Anton Lodder Antony Lee Arel Cordero +Arias Emmanuel +Ariel Pillemer Armin Rigo Aron Coyle Aron Curzon +Arthur Richard +Ashish Kurmi Aviral Verma Aviv Palivoda +Babak Keyvani Barney Gale +Ben Brown +Ben Gartner Ben Webb Benjamin Peterson +Benjamin Schubert Bernard Pratz +Bo Wu Bob Ippolito Brian Dorsey +Brian Larsen Brian Maissy Brian Okken Brianna Laugher Bruno Oliveira +Cal Jacobson Cal Leeming Carl Friedrich Bolz Carlos Jenkins @@ -54,24 +76,34 @@ Ceridwen Charles Cloud Charles Machalow Charnjit SiNGH (CCSJ) +Cheuk Ting Ho +Chris Mahoney Chris Lamb +Chris NeJame +Chris Rose +Chris Wheeler Christian Boelsen Christian Fetzer Christian Neumüller Christian Theunert Christian Tismer +Christine Mecklenborg Christoph Buelter Christopher Dignam Christopher Gilling Claire Cecil Claudio Madotto CrazyMerlyn +Cristian Vera Cyrus Maden Damian Skrzypczak Daniel Grana Daniel Hahler Daniel Nuri +Daniel Sánchez Castelló +Daniel Valenzuela Zenteno Daniel Wandschneider +Daniele Procida Danielle Jenkins Daniil Galiev Dave Hunt @@ -83,34 +115,48 @@ David Vierra Daw-Ran Liou Debi Mishra Denis Kirisov +Denivy Braiam Rück Dhiren Serai Diego Russo Dmitry Dygalo Dmitry Pribysh +Dominic Mortlock Duncan Betts Edison Gustavo Muenz Edoardo Batini +Edson Tadeu M. Manoel Eduardo Schettino Eli Boyarski Elizaveta Shashkova +Éloi Rivard Endre Galaczi Eric Hunsberger +Eric Liu Eric Siegerman Erik Aronesty +Erik Hasse Erik M. Bray Evan Kepner +Evgeny Seliverstov +Fabian Sturm Fabien Zarifian Fabio Zadrozny +Felix Hofstätter Felix Nieuwenhuizen Feng Ma Florian Bruhin Florian Dahlitz Floris Bruynooghe +Fraser Stark +Gabriel Landau Gabriel Reis +Garvit Shubham Gene Wood George Kussumoto Georgy Dyuldin +Gergely Kalmár Gleb Nikonorov +Graeme Smecher Graham Horler Greg Price Gregory Lee @@ -119,6 +165,7 @@ Grigorii Eremeev (budulianin) Guido Wesdorp Guoqiang Zhang Harald Armin Massa +Harshna Henk-Jaap Wagenaar Holger Kohr Hugo van Kemenade @@ -127,9 +174,15 @@ Ian Bicking Ian Lesperance Ilya Konstantinov Ionuț Turturică +Isaac Virshup +Israel Fruchter +Itxaso Aizpurua Iwan Briquemont Jaap Broekhuizen +Jake VanderPlas +Jakob van Santen Jakub Mitoraj +James Bourbeau Jan Balster Janne Vanhala Jason R. Coombs @@ -138,8 +191,11 @@ Javier Romero Jeff Rackauckas Jeff Widman Jenni Rinker +Jens Tröger John Eddie Ayson +John Litborn John Towler +Jon Parise Jon Sonesen Jonas Obrist Jordan Guymon @@ -149,16 +205,25 @@ Joseph Hunkeler Josh Karpel Joshua Bronson Jurko Gospodnetić +Justice Ndou Justyna Janczyszyn Kale Kundert +Kamran Ahmad +Kenny Y Karl O. Pinc +Karthikeyan Singaravelan Katarzyna Jachim Katarzyna Król Katerina Koukiou Keri Volans +Kevin C Kevin Cox +Kevin Hierro Carrasco Kevin J. Foley +Kian Eliasi +Kian-Meng Ang Kodi B. Arfer +Kojo Idrissa Kostis Anagnostopoulos Kristoffer Nordström Kyle Altendorf @@ -175,12 +240,14 @@ Maho Maik Figura Mandeep Bhutani Manuel Krebber +Marc Mueller Marc Schlaich Marcelo Duarte Trevisani Marcin Bachry Marco Gorelli Mark Abramowitz Mark Dickinson +Marko Pacak Markus Unterwaditzer Martijn Faassen Martin Altmayer @@ -201,48 +268,68 @@ Michael Goerz Michael Krebs Michael Seifert Michal Wajszczuk +Michał Górny +Michał Zięba +Mickey Pashov Mihai Capotă +Mihail Milushev Mike Hoyle (hoylemd) Mike Lundy +Milan Lesnek Miro Hrončok Nathaniel Compton Nathaniel Waisbrot Ned Batchelder +Neil Martin Neven Mundar Nicholas Devenish Nicholas Murphy Niclas Olofsson Nicolas Delaby Nikolay Kondratyev +Nipunn Koorapati Oleg Pidsadnyi Oleg Sushchenko +Olga Matoula Oliver Bestwalter Omar Kohl Omer Hadari Ondřej Súkup Oscar Benjamin +Parth Patel Patrick Hayes +Patrick Lannigan +Paul Müller +Paul Reece Pauli Virtanen Pavel Karateev Paweł Adamczak Pedro Algarvio +Petter Strandmark Philipp Loose +Pierre Sassoulas Pieter Mulder Piotr Banaszkiewicz Piotr Helm +Prakhar Gurunani Prashant Anand +Prashant Sharma Pulkit Goyal Punyashloka Biswal Quentin Pradet +q0w Ralf Schmitt -Ram Rachum Ralph Giles +Ram Rachum Ran Benita Raphael Castaneda Raphael Pierzina +Rafal Semik Raquel Alegre Ravi Chandra +Reagan Lee Robert Holt +Roberto Aldera Roberto Polli Roland Puntaier Romain Dorgueil @@ -251,21 +338,36 @@ Ronny Pfannschmidt Ross Lawley Ruaridh Williamson Russel Winder +Ryan Puddephatt Ryan Wooden +Sadra Barikbin +Saiprasad Kale +Samuel Colvin Samuel Dion-Girardeau Samuel Searles-Bryant +Samuel Therrien (Avasam) Samuele Pedroni +Sanket Duthade Sankt Petersbug +Saravanan Padmanaban +Sean Malloy Segev Finer Serhii Mozghovyi Seth Junot +Shantanu Jain +Sharad Nair +Shubham Adep +Simon Blanchard Simon Gomizelj +Simon Holesch Simon Kerr Skylar Downes Srinivas Reddy Thatiparthy +Stefaan Lippens Stefan Farmbauer Stefan Scherfke Stefan Zimmermann +Stefanie Molin Stefano Taschini Steffen Allner Stephan Obermann @@ -273,27 +375,40 @@ Sven-Hendrik Haase Sylvain Marié Tadek Teleżyński Takafumi Arakaki +Taneli Hukkinen +Tanvi Mehta +Tanya Agarwal Tarcisio Fischer Tareq Alayan +Tatiana Ovary Ted Xiao +Terje Runde Thomas Grainger Thomas Hisch Tim Hoffmann Tim Strazny +TJ Bruno +Tobias Diez Tom Dalton Tom Viner Tomáš Gavenčiak Tomer Keren +Tony Narlock Tor Colvin Trevor Bekolay +Tushar Sadhwani Tyler Goodlet +Tyler Smart Tzu-ping Chung Vasily Kuznetsov Victor Maryama +Victor Rodriguez Victor Uriarte Vidar T. Fauske +Vijay Arora Virgil Dupras Vitaly Lashmanov +Vivaan Verma Vlad Dragos Vlad Radziuk Vladyslav Rachek @@ -306,6 +421,14 @@ Wouter van Ackooy Xixi Zhao Xuan Luong Xuecong Liao +Yannick Péroux Yoav Caspi +Yuliang Shao +Yusuke Kadowaki +Yuval Shimon Zac Hatfield-Dodds +Zachary Kneupper +Zachary OBrien +Zhouxin Qiu Zoltán Máté +Zsolt Cserna diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3865f250c26..481f277813a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,4 +4,4 @@ Changelog The pytest CHANGELOG is located `here `__. -The source document can be found at: https://github.com/pytest-dev/pytest/blob/master/doc/en/changelog.rst +The source document can be found at: https://github.com/pytest-dev/pytest/blob/main/doc/en/changelog.rst diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 0523c0ece77..6f55c230c92 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -50,6 +50,8 @@ Fix bugs -------- Look through the `GitHub issues for bugs `_. +See also the `"good first issue" issues `_ +that are friendly to new contributors. :ref:`Talk ` to developers to find out how you can fix specific bugs. To indicate that you are going to work on a particular issue, add a comment to that effect on the specific issue. @@ -89,6 +91,38 @@ without using a local copy. This can be convenient for small fixes. The built documentation should be available in ``doc/en/_build/html``, where 'en' refers to the documentation language. +Pytest has an API reference which in large part is +`generated automatically `_ +from the docstrings of the documented items. Pytest uses the +`Sphinx docstring format `_. +For example: + +.. code-block:: python + + def my_function(arg: ArgType) -> Foo: + """Do important stuff. + + More detailed info here, in separate paragraphs from the subject line. + Use proper sentences -- start sentences with capital letters and end + with periods. + + Can include annotated documentation: + + :param short_arg: An argument which determines stuff. + :param long_arg: + A long explanation which spans multiple lines, overflows + like this. + :returns: The result. + :raises ValueError: + Detailed information when this can happen. + + .. versionadded:: 6.0 + + Including types into the annotations above is not necessary when + type-hinting is being used (as in this example). + """ + + .. _submitplugin: Submitting Plugins to pytest-dev @@ -99,8 +133,6 @@ in repositories living under the ``pytest-dev`` organisations: - `pytest-dev on GitHub `_ -- `pytest-dev on Bitbucket `_ - All pytest-dev Contributors team members have write access to all contained repositories. Pytest core and plugins are generally developed using `pull requests`_ to respective repositories. @@ -116,20 +148,21 @@ You can submit your plugin by subscribing to the `pytest-dev mail list mail pointing to your existing pytest plugin repository which must have the following: -- PyPI presence with a ``setup.py`` that contains a license, ``pytest-`` +- PyPI presence with packaging metadata that contains a ``pytest-`` prefixed name, version number, authors, short and long description. -- a ``tox.ini`` for running tests using `tox `_. +- a `tox configuration `_ + for running tests using `tox `_. -- a ``README.txt`` describing how to use the plugin and on which +- a ``README`` describing how to use the plugin and on which platforms it runs. -- a ``LICENSE.txt`` file or equivalent containing the licensing - information, with matching info in ``setup.py``. +- a ``LICENSE`` file containing the licensing information, with + matching info in its packaging metadata. - an issue tracker for bug reports and enhancement requests. -- a `changelog `_. +- a `changelog `_. If no contributor strongly objects and two agree, the repository can then be transferred to the ``pytest-dev`` organisation. @@ -164,11 +197,12 @@ Short version ~~~~~~~~~~~~~ #. Fork the repository. +#. Fetch tags from upstream if necessary (if you cloned only main `git fetch --tags https://github.com/pytest-dev/pytest`). #. Enable and install `pre-commit `_ to ensure style-guides and code checks are followed. -#. Follow **PEP-8** for naming and `black `_ for formatting. +#. Follow `PEP-8 `_ for naming. #. Tests are run using ``tox``:: - tox -e linting,py37 + tox -e linting,py39 The test environments above are usually enough to cover most cases locally. @@ -190,7 +224,7 @@ changes you want to review and merge. Pull requests are stored on Once you send a pull request, we can discuss its potential modifications and even add more commits to it later on. There's an excellent tutorial on how Pull Requests work in the -`GitHub Help Center `_. +`GitHub Help Center `_. Here is a simple overview, with pytest-specific bits: @@ -203,14 +237,20 @@ Here is a simple overview, with pytest-specific bits: $ git clone git@github.com:YOUR_GITHUB_USERNAME/pytest.git $ cd pytest - # now, create your own branch off "master": + $ git fetch --tags https://github.com/pytest-dev/pytest + # now, create your own branch off "main": - $ git checkout -b your-bugfix-branch-name master + $ git checkout -b your-bugfix-branch-name main Given we have "major.minor.micro" version numbers, bug fixes will usually be released in micro releases whereas features will be released in minor releases and incompatible changes in major releases. + You will need the tags to test locally, so be sure you have the tags from the main repository. If you suspect you don't, set the main repository as upstream and fetch the tags:: + + $ git remote add upstream https://github.com/pytest-dev/pytest + $ git fetch upstream --tags + If you need some help with Git, follow this quick start guide: https://git.wiki.kernel.org/index.php/QuickStart @@ -228,30 +268,30 @@ Here is a simple overview, with pytest-specific bits: Tox is used to run all the tests and will automatically setup virtualenvs to run the tests in. - (will implicitly use http://www.virtualenv.org/en/latest/):: + (will implicitly use https://virtualenv.pypa.io/en/latest/):: $ pip install tox #. Run all the tests - You need to have Python 3.7 available in your system. Now + You need to have Python 3.8 or later available in your system. Now running tests is as simple as issuing this command:: - $ tox -e linting,py37 + $ tox -e linting,py39 - This command will run tests via the "tox" tool against Python 3.7 + This command will run tests via the "tox" tool against Python 3.9 and also perform "lint" coding-style checks. -#. You can now edit your local working copy and run the tests again as necessary. Please follow PEP-8 for naming. +#. You can now edit your local working copy and run the tests again as necessary. Please follow `PEP-8 `_ for naming. - You can pass different options to ``tox``. For example, to run tests on Python 3.7 and pass options to pytest + You can pass different options to ``tox``. For example, to run tests on Python 3.9 and pass options to pytest (e.g. enter pdb on failure) to pytest you can do:: - $ tox -e py37 -- --pdb + $ tox -e py39 -- --pdb - Or to only run tests in a particular test module on Python 3.7:: + Or to only run tests in a particular test module on Python 3.9:: - $ tox -e py37 -- testing/test_config.py + $ tox -e py39 -- testing/test_config.py When committing, ``pre-commit`` will re-format the files if necessary. @@ -268,12 +308,6 @@ Here is a simple overview, with pytest-specific bits: $ pytest testing/test_config.py - -#. Commit and push once your tests pass and you are happy with your change(s):: - - $ git commit -a -m "" - $ git push -u - #. Create a new changelog entry in ``changelog``. The file should be named ``..rst``, where *issueid* is the number of the issue related to the change and *type* is one of ``feature``, ``improvement``, ``bugfix``, ``doc``, ``deprecation``, ``breaking``, ``vendor`` @@ -282,32 +316,37 @@ Here is a simple overview, with pytest-specific bits: #. Add yourself to ``AUTHORS`` file if not there yet, in alphabetical order. +#. Commit and push once your tests pass and you are happy with your change(s):: + + $ git commit -a -m "" + $ git push -u + #. Finally, submit a pull request through the GitHub website using this data:: head-fork: YOUR_GITHUB_USERNAME/pytest compare: your-branch-name base-fork: pytest-dev/pytest - base: master + base: main Writing Tests ~~~~~~~~~~~~~ -Writing tests for plugins or for pytest itself is often done using the `testdir fixture `_, as a "black-box" test. +Writing tests for plugins or for pytest itself is often done using the `pytester fixture `_, as a "black-box" test. For example, to ensure a simple test passes you can write: .. code-block:: python - def test_true_assertion(testdir): - testdir.makepyfile( + def test_true_assertion(pytester): + pytester.makepyfile( """ def test_foo(): assert True """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.assert_outcomes(failed=0, passed=1) @@ -316,14 +355,14 @@ Alternatively, it is possible to make checks based on the actual output of the t .. code-block:: python - def test_true_assertion(testdir): - testdir.makepyfile( + def test_true_assertion(pytester): + pytester.makepyfile( """ def test_foo(): assert False """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*assert False*", "*1 failed*"]) When choosing a file where to write a new test, take a look at the existing files and see if there's @@ -348,7 +387,7 @@ them. Backporting bug fixes for the next patch release ------------------------------------------------ -Pytest makes feature release every few weeks or months. In between, patch releases +Pytest makes a feature release every few weeks or months. In between, patch releases are made to the previous feature release, containing bug fixes only. The bug fixes usually fix regressions, but may be any change that should reach users before the next feature release. @@ -357,15 +396,22 @@ Suppose for example that the latest release was 1.2.3, and you want to include a bug fix in 1.2.4 (check https://github.com/pytest-dev/pytest/releases for the actual latest release). The procedure for this is: -#. First, make sure the bug is fixed the ``master`` branch, with a regular pull +#. First, make sure the bug is fixed in the ``main`` branch, with a regular pull request, as described above. An exception to this is if the bug fix is not - applicable to ``master`` anymore. + applicable to ``main`` anymore. + +Automatic method: + +Add a ``backport 1.2.x`` label to the PR you want to backport. This will create +a backport PR against the ``1.2.x`` branch. + +Manual method: -#. ``git checkout origin/1.2.x -b backport-XXXX`` # use the master PR number here +#. ``git checkout origin/1.2.x -b backport-XXXX`` # use the main PR number here #. Locate the merge commit on the PR, in the *merged* message, for example: - nicoddemus merged commit 0f8b462 into pytest-dev:master + nicoddemus merged commit 0f8b462 into pytest-dev:main #. ``git cherry-pick -x -m1 REVISION`` # use the revision you found above (``0f8b462``). @@ -375,6 +421,27 @@ actual latest release). The procedure for this is: * Delete the PR body, it usually contains a duplicate commit message. +Who does the backporting +~~~~~~~~~~~~~~~~~~~~~~~~ + +As mentioned above, bugs should first be fixed on ``main`` (except in rare occasions +that a bug only happens in a previous release). So, who should do the backport procedure described +above? + +1. If the bug was fixed by a core developer, it is the main responsibility of that core developer + to do the backport. +2. However, often the merge is done by another maintainer, in which case it is nice of them to + do the backport procedure if they have the time. +3. For bugs submitted by non-maintainers, it is expected that a core developer will to do + the backport, normally the one that merged the PR on ``main``. +4. If a non-maintainers notices a bug which is fixed on ``main`` but has not been backported + (due to maintainers forgetting to apply the *needs backport* label, or just plain missing it), + they are also welcome to open a PR with the backport. The procedure is simple and really + helps with the maintenance of the project. + +All the above are not rules, but merely some guidelines/suggestions on what we should expect +about backports. + Handling stale issues/PRs ------------------------- @@ -396,7 +463,7 @@ can always reopen the issue/pull request in their own time later if it makes sen When to close ~~~~~~~~~~~~~ -Here are a few general rules the maintainers use to decide when to close issues/PRs because +Here are a few general rules the maintainers use deciding when to close issues/PRs because of lack of inactivity: * Issues labeled ``question`` or ``needs information``: closed after 14 days inactive. @@ -408,15 +475,15 @@ The above are **not hard rules**, but merely **guidelines**, and can be (and oft Closing pull requests ~~~~~~~~~~~~~~~~~~~~~ -When closing a Pull Request, it needs to be acknowledge the time, effort, and interest demonstrated by the person which submitted it. As mentioned previously, it is not the intent of the team to dismiss stalled pull request entirely but to merely to clear up our queue, so a message like the one below is warranted when closing a pull request that went stale: +When closing a Pull Request, it needs to be acknowledging the time, effort, and interest demonstrated by the person which submitted it. As mentioned previously, it is not the intent of the team to dismiss a stalled pull request entirely but to merely to clear up our queue, so a message like the one below is warranted when closing a pull request that went stale: Hi , - First of all we would like to thank you for your time and effort on working on this, the pytest team deeply appreciates it. + First of all, we would like to thank you for your time and effort on working on this, the pytest team deeply appreciates it. We noticed it has been awhile since you have updated this PR, however. pytest is a high activity project, with many issues/PRs being opened daily, so it is hard for us maintainers to track which PRs are ready for merging, for review, or need more attention. - So for those reasons we think it is best to close the PR for now, but with the only intention to cleanup our queue, it is by no means a rejection of your changes. We still encourage you to re-open this PR (it is just a click of a button away) when you are ready to get back to it. + So for those reasons we, think it is best to close the PR for now, but with the only intention to clean up our queue, it is by no means a rejection of your changes. We still encourage you to re-open this PR (it is just a click of a button away) when you are ready to get back to it. Again we appreciate your time for working on this, and hope you might get back to this at a later time! diff --git a/LICENSE b/LICENSE index d14fb7ff4b3..c3f1657fce9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2004-2020 Holger Krekel and others +Copyright (c) 2004 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 diff --git a/README.rst b/README.rst index 00c85ae37ce..bbf41a18399 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,7 @@ -.. image:: https://docs.pytest.org/en/stable/_static/pytest1.png +.. image:: https://github.com/pytest-dev/pytest/raw/main/doc/en/img/pytest_logo_curves.svg :target: https://docs.pytest.org/en/stable/ :align: center + :height: 200 :alt: pytest @@ -15,15 +16,16 @@ .. image:: https://img.shields.io/pypi/pyversions/pytest.svg :target: https://pypi.org/project/pytest/ -.. image:: https://codecov.io/gh/pytest-dev/pytest/branch/master/graph/badge.svg +.. image:: https://codecov.io/gh/pytest-dev/pytest/branch/main/graph/badge.svg :target: https://codecov.io/gh/pytest-dev/pytest :alt: Code coverage Status -.. image:: https://travis-ci.org/pytest-dev/pytest.svg?branch=master - :target: https://travis-ci.org/pytest-dev/pytest +.. image:: https://github.com/pytest-dev/pytest/actions/workflows/test.yml/badge.svg + :target: https://github.com/pytest-dev/pytest/actions?query=workflow%3Atest -.. image:: https://dev.azure.com/pytest-dev/pytest/_apis/build/status/pytest-CI?branchName=master - :target: https://dev.azure.com/pytest-dev/pytest +.. image:: https://results.pre-commit.ci/badge/github/pytest-dev/pytest/main.svg + :target: https://results.pre-commit.ci/latest/github/pytest-dev/pytest/main + :alt: pre-commit.ci status .. image:: https://img.shields.io/badge/code%20style-black-000000.svg :target: https://github.com/psf/black @@ -35,6 +37,15 @@ :target: https://pytest.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status +.. image:: https://img.shields.io/badge/Discord-pytest--dev-blue + :target: https://discord.com/invite/pytest-dev + :alt: Discord + +.. image:: https://img.shields.io/badge/Libera%20chat-%23pytest-orange + :target: https://web.libera.chat/#pytest + :alt: Libera chat + + The ``pytest`` framework makes it easy to write small tests, yet scales to support complex functional testing for applications and libraries. @@ -77,21 +88,21 @@ Due to ``pytest``'s detailed assertion introspection, only plain ``assert`` stat Features -------- -- Detailed info on failing `assert statements `_ (no need to remember ``self.assert*`` names); +- Detailed info on failing `assert statements `_ (no need to remember ``self.assert*`` names) - `Auto-discovery - `_ - of test modules and functions; + `_ + of test modules and functions -- `Modular fixtures `_ for - managing small or parametrized long-lived test resources; +- `Modular fixtures `_ for + managing small or parametrized long-lived test resources -- Can run `unittest `_ (or trial), - `nose `_ test suites out of the box; +- Can run `unittest `_ (or trial), + `nose `_ test suites out of the box -- Python 3.5+ and PyPy3; +- Python 3.8+ or PyPy3 -- Rich plugin architecture, with over 850+ `external plugins `_ and thriving community; +- Rich plugin architecture, with over 850+ `external plugins `_ and thriving community Documentation @@ -149,8 +160,8 @@ Tidelift will coordinate the fix and disclosure. License ------- -Copyright Holger Krekel and others, 2004-2020. +Copyright Holger Krekel and others, 2004. Distributed under the terms of the `MIT`_ license, pytest is free and open source software. -.. _`MIT`: https://github.com/pytest-dev/pytest/blob/master/LICENSE +.. _`MIT`: https://github.com/pytest-dev/pytest/blob/main/LICENSE diff --git a/RELEASING.rst b/RELEASING.rst index f5e2528e3f2..08004a84c00 100644 --- a/RELEASING.rst +++ b/RELEASING.rst @@ -5,37 +5,115 @@ Our current policy for releasing is to aim for a bug-fix release every few weeks is to get fixes and new features out instead of trying to cram a ton of features into a release and by consequence taking a lot of time to make a new one. +The git commands assume the following remotes are setup: + +* ``origin``: your own fork of the repository. +* ``upstream``: the ``pytest-dev/pytest`` official repository. + Preparing: Automatic Method ~~~~~~~~~~~~~~~~~~~~~~~~~~~ We have developed an automated workflow for releases, that uses GitHub workflows and is triggered -by opening an issue or issuing a comment one. +by `manually running `__ +the `prepare-release-pr workflow `__ +on GitHub Actions. + +The automation will decide the new version number based on the following criteria: + +- If the "major release" input is set to "yes", release a new major release + (e.g. 7.0.0 -> 8.0.0) +- If there are any ``.feature.rst`` or ``.breaking.rst`` files in the + ``changelog`` directory, release a new minor release (e.g. 7.0.0 -> 7.1.0) +- Otherwise, release a bugfix release (e.g. 7.0.0 -> 7.0.1) +- If the "prerelease" input is set, append the string to the version number + (e.g. 7.0.0 -> 8.0.0rc1), if "major" is set, and "prerelease" is set to `rc1`) + +Bug-fix and minor releases +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Bug-fix and minor releases are always done from a maintenance branch. First, +consider double-checking the ``changelog`` directory to see if there are any +breaking changes or new features. + +For a new minor release, first create a new maintenance branch from ``main``:: + + git fetch upstream + git branch 7.1.x upstream/main + git push upstream 7.1.x + +Then, trigger the workflow with the following inputs: + +- branch: **7.1.x** +- major release: **no** +- prerelease: empty + +Or via the commandline using `GitHub's cli `__:: + + gh workflow run prepare-release-pr.yml -f branch=7.1.x -f major=no -f prerelease= + +Where ``7.1.x`` is the maintenance branch for the ``7.1`` series. The automated +workflow will publish a PR for a branch ``release-7.1.0``. + +Similarly, for a bug-fix release, use the existing maintenance branch and +trigger the workflow with e.g. ``branch: 7.0.x`` to get a new ``release-7.0.1`` +PR. + +Major releases +^^^^^^^^^^^^^^ -The comment must be in the form:: +1. Create a new maintenance branch from ``main``:: - @pytestbot please prepare release from BRANCH + git fetch upstream + git branch 8.0.x upstream/main + git push upstream 8.0.x -Where ``BRANCH`` is ``master`` or one of the maintenance branches. +2. Trigger the workflow with the following inputs: -For major releases the comment must be in the form:: + - branch: **8.0.x** + - major release: **yes** + - prerelease: empty - @pytestbot please prepare major release from master +Or via the commandline:: -After that, the workflow should publish a PR and notify that it has done so as a comment -in the original issue. + gh workflow run prepare-release-pr.yml -f branch=8.0.x -f major=yes -f prerelease= + +The automated workflow will publish a PR for a branch ``release-8.0.0``. + +At this point on, this follows the same workflow as other maintenance branches: bug-fixes are merged +into ``main`` and ported back to the maintenance branch, even for release candidates. + +Release candidates +^^^^^^^^^^^^^^^^^^ + +To release a release candidate, set the "prerelease" input to the version number +suffix to use. To release a ``8.0.0rc1``, proceed like under "major releases", but set: + +- branch: 8.0.x +- major release: yes +- prerelease: **rc1** + +Or via the commandline:: + + gh workflow run prepare-release-pr.yml -f branch=8.0.x -f major=yes -f prerelease=rc1 + +The automated workflow will publish a PR for a branch ``release-8.0.0rc1``. + +**A note about release candidates** + +During release candidates we can merge small improvements into +the maintenance branch before releasing the final major version, however we must take care +to avoid introducing big changes at this stage. Preparing: Manual Method ~~~~~~~~~~~~~~~~~~~~~~~~ -.. important:: - - pytest releases must be prepared on **Linux** because the docs and examples expect - to be executed on that platform. +**Important**: pytest releases must be prepared on **Linux** because the docs and examples expect +to be executed on that platform. To release a version ``MAJOR.MINOR.PATCH``, follow these steps: -#. For major and minor releases, create a new branch ``MAJOR.MINOR.x`` from the - latest ``master`` and push it to the ``pytest-dev/pytest`` repo. +#. For major and minor releases, create a new branch ``MAJOR.MINOR.x`` from + ``upstream/main`` and push it to ``upstream``. #. Create a branch ``release-MAJOR.MINOR.PATCH`` from the ``MAJOR.MINOR.x`` branch. @@ -55,23 +133,32 @@ Releasing Both automatic and manual processes described above follow the same steps from this point onward. -#. After all tests pass and the PR has been approved, tag the release commit - in the ``MAJOR.MINOR.x`` branch and push it. This will publish to PyPI:: +#. After all tests pass and the PR has been approved, trigger the ``deploy`` job + in https://github.com/pytest-dev/pytest/actions/workflows/deploy.yml, using the ``release-MAJOR.MINOR.PATCH`` branch + as source. + + This job will require approval from ``pytest-dev/core``, after which it will publish to PyPI + and tag the repository. + +#. Merge the PR. **Make sure it's not squash-merged**, so that the tagged commit ends up in the main branch. + +#. Cherry-pick the CHANGELOG / announce files to the ``main`` branch:: - git tag MAJOR.MINOR.PATCH - git push git@github.com:pytest-dev/pytest.git MAJOR.MINOR.PATCH + git fetch upstream + git checkout upstream/main -b cherry-pick-release + git cherry-pick -x -m1 upstream/MAJOR.MINOR.x - Wait for the deploy to complete, then make sure it is `available on PyPI `_. +#. Open a PR for ``cherry-pick-release`` and merge it once CI passes. No need to wait for approvals if there were no conflicts on the previous step. -#. Merge the PR. +#. For major and minor releases (or the first prerelease of it), tag the release cherry-pick merge commit in main with + a dev tag for the next feature release:: -#. Cherry-pick the CHANGELOG / announce files to the ``master`` branch:: + git checkout main + git pull + git tag MAJOR.{MINOR+1}.0.dev0 + git push upstream MAJOR.{MINOR+1}.0.dev0 - git fetch --all --prune - git checkout origin/master -b cherry-pick-release - git cherry-pick --no-commit -m1 origin/MAJOR.MINOR.x - git checkout origin/master -- changelog - git commit # no arguments +#. For major and minor releases, change the default version in the `Read the Docs Settings `_ to the new branch. #. Send an email announcement with the contents from:: diff --git a/TIDELIFT.rst b/TIDELIFT.rst index b18f4793f81..2fe25841c3a 100644 --- a/TIDELIFT.rst +++ b/TIDELIFT.rst @@ -25,6 +25,7 @@ The current list of contributors receiving funding are: * `@asottile`_ * `@nicoddemus`_ +* `@The-Compiler`_ Contributors interested in receiving a part of the funds just need to submit a PR adding their name to the list. Contributors that want to stop receiving the funds should also submit a PR @@ -56,3 +57,4 @@ funds. Just drop a line to one of the `@pytest-dev/tidelift-admins`_ or use the .. _`@asottile`: https://github.com/asottile .. _`@nicoddemus`: https://github.com/nicoddemus +.. _`@The-Compiler`: https://github.com/The-Compiler diff --git a/bench/unit_test.py b/bench/unit_test.py new file mode 100644 index 00000000000..ad52069dbfd --- /dev/null +++ b/bench/unit_test.py @@ -0,0 +1,13 @@ +from unittest import TestCase # noqa: F401 + +for i in range(15000): + exec( + f""" +class Test{i}(TestCase): + @classmethod + def setUpClass(cls): pass + def test_1(self): pass + def test_2(self): pass + def test_3(self): pass +""" + ) diff --git a/bench/xunit.py b/bench/xunit.py new file mode 100644 index 00000000000..3a77dcdce42 --- /dev/null +++ b/bench/xunit.py @@ -0,0 +1,11 @@ +for i in range(5000): + exec( + f""" +class Test{i}: + @classmethod + def setup_class(cls): pass + def test_1(self): pass + def test_2(self): pass + def test_3(self): pass +""" + ) diff --git a/changelog/README.rst b/changelog/README.rst index 6d026f57ef3..88956ef28d8 100644 --- a/changelog/README.rst +++ b/changelog/README.rst @@ -14,7 +14,7 @@ Each file should be named like ``..rst``, where ```` is an issue number, and ```` is one of: * ``feature``: new user facing features, like new command-line options and new behavior. -* ``improvement``: improvement of existing functionality, usually without requiring user intervention (for example, new fields being written in ``--junitxml``, improved colors in terminal, etc). +* ``improvement``: improvement of existing functionality, usually without requiring user intervention (for example, new fields being written in ``--junit-xml``, improved colors in terminal, etc). * ``bugfix``: fixes a bug. * ``doc``: documentation improvement, like rewording an entire session or adding missing docs. * ``deprecation``: feature deprecation. diff --git a/doc/en/Makefile b/doc/en/Makefile index 1cffbd463d8..f2db6891211 100644 --- a/doc/en/Makefile +++ b/doc/en/Makefile @@ -34,6 +34,10 @@ REGENDOC_ARGS := \ regen: REGENDOC_FILES:=*.rst */*.rst regen: - PYTHONDONTWRITEBYTECODE=1 PYTEST_ADDOPTS="-pno:hypothesis -Wignore::pytest.PytestUnknownMarkWarning" COLUMNS=76 regendoc --update ${REGENDOC_FILES} ${REGENDOC_ARGS} +# need to reset cachedir to the non-tox default + PYTHONDONTWRITEBYTECODE=1 \ + PYTEST_ADDOPTS="-pno:hypothesis -p no:hypothesispytest -Wignore::pytest.PytestUnknownMarkWarning -o cache_dir=.pytest_cache" \ + COLUMNS=76 \ + regendoc --update ${REGENDOC_FILES} ${REGENDOC_ARGS} .PHONY: regen diff --git a/doc/en/_templates/globaltoc.html b/doc/en/_templates/globaltoc.html index 4522eb2dec9..09d970b64ed 100644 --- a/doc/en/_templates/globaltoc.html +++ b/doc/en/_templates/globaltoc.html @@ -1,16 +1,22 @@ -

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

+

Contents

+ +

About the project

+ +