diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 75ee2e6..a9a392b 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -2,116 +2,123 @@ name: check on: push: pull_request: + schedule: + - cron: "0 8 * * *" concurrency: group: check-${{ github.ref }} cancel-in-progress: true jobs: - pre-commit: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v1 - - uses: pre-commit/action@v2.0.0 - test: - name: test ${{ matrix.py }} - ${{ matrix.os }} - runs-on: ${{ matrix.os }}-latest + name: test ${{ matrix.py }} + runs-on: ubuntu-20.04 strategy: fail-fast: false matrix: - os: - - Ubuntu - - Windows - - macOS py: - - 3.10.0-rc.2 - - 3.9 - - 3.8 - - 3.7 - - 3.6 + - "3.10" + - "3.9" + - "3.8" + - "3.7" steps: - - name: Setup python for tox + - name: setup python for tox uses: actions/setup-python@v2 with: - python-version: 3.10.0-rc.2 - - name: Install tox + python-version: "3.10" + - name: install tox run: python -m pip install tox - uses: actions/checkout@v2 - - name: Setup python for test ${{ matrix.py }} + - name: setup python for test ${{ matrix.py }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.py }} - - name: Pick environment to run + - name: pick environment to run run: | - import platform; import os; import sys; import codecs - cpy = platform.python_implementation() == "CPython" - base =("{}{}{}" if cpy else "{}{}").format("py" if cpy else "pypy", *sys.version_info[0:2]) - env = "TOXENV={}\n".format(base) - print("Picked:\n{}for{}".format(env, sys.version)) - with codecs.open(os.environ["GITHUB_ENV"], "a", "utf-8") as file_handler: - file_handler.write(env) + import os + with open(os.environ['GITHUB_ENV'], 'a') as file_handler: + file_handler.write('TOXENV=py${{matrix.py}}'.replace('.', '')) shell: python - - name: Setup test suite + - name: setup test suite run: tox -vv --notest - - name: Run test suite + - name: run test suite run: tox --skip-pkg-install - env: - PYTEST_ADDOPTS: "-vv --durations=20" - CI_RUN: "yes" - DIFF_AGAINST: HEAD - - name: rename coverage report file - run: | - import os; os.rename('.tox/coverage.{}.xml'.format(os.environ['TOXENV']), '.tox/coverage.xml') - shell: python - - uses: codecov/codecov-action@v1 + - name: Upload coverage data + uses: actions/upload-artifact@v2 with: - file: ./.tox/coverage.xml - flags: tests - name: ${{ matrix.py }} - ${{ matrix.os }} + name: coverage-data + path: ".tox/.coverage.*" + coverage: + name: Combine coverage + runs-on: ubuntu-20.04 + needs: test + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - uses: actions/setup-python@v2 + with: + python-version: "3.10" + - name: Install tox + run: python -m pip install tox + - name: Setup coverage tool + run: tox -e coverage --notest + - name: Install package builder + run: python -m pip install build + - name: Build package + run: pyproject-build --wheel . + - name: Download coverage data + uses: actions/download-artifact@v2 + with: + name: coverage-data + path: .tox + - name: Combine and report coverage + run: tox -e coverage + - name: Upload HTML report + uses: actions/upload-artifact@v2 + with: + name: html-report + path: .tox/htmlcov check: - name: check ${{ matrix.tox_env }} - ${{ matrix.os }} - runs-on: ${{ matrix.os }}-latest + name: tox env ${{ matrix.tox_env }} + runs-on: ubuntu-20.04 strategy: fail-fast: false matrix: - os: - - Ubuntu tox_env: - - merge - type - - package_readme + - merge - dev + - readme steps: - uses: actions/checkout@v2 - - name: Setup Python 3.10.0-rc.2 + - name: setup Python 3.10 uses: actions/setup-python@v2 with: - python-version: 3.10.0-rc.2 - - name: Install tox + python-version: "3.10" + - name: install tox run: python -m pip install tox - - name: Run check for ${{ matrix.tox_env }} + - name: run check for ${{ matrix.tox_env }} run: python -m tox -e ${{ matrix.tox_env }} env: UPGRADE_ADVISORY: "yes" publish: if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') - needs: [check, test, pre-commit] - runs-on: ubuntu-latest + needs: [check, coverage] + runs-on: ubuntu-20.04 steps: - - name: Setup python to build package + - name: setup python to build package uses: actions/setup-python@v2 with: - python-version: 3.10.0-rc.2 - - name: Install build + python-version: "3.10" + - name: install build run: python -m pip install build - uses: actions/checkout@v2 - - name: Build package + - name: build package run: python -m build --sdist --wheel . -o dist - - name: Publish to PyPI + - name: publish to PyPI uses: pypa/gh-action-pypi-publish@master with: skip_existing: true diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6fd3dc1..9c65cf9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,34 +2,58 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.0.1 hooks: - - id: trailing-whitespace - - id: end-of-file-fixer + - id: check-ast + - id: check-builtin-literals + - id: check-docstring-first + - id: check-merge-conflict - id: check-yaml + - id: check-toml - id: debug-statements - - repo: https://github.com/PyCQA/flake8 - rev: 3.9.2 - hooks: - - id: flake8 - additional_dependencies: [ "flake8-bugbear", "flake8-pyi" ] + - id: end-of-file-fixer + - id: trailing-whitespace - repo: https://github.com/asottile/pyupgrade - rev: v2.29.0 + rev: v2.30.0 hooks: - id: pyupgrade + args: [ "--py36-plus" ] - repo: https://github.com/PyCQA/isort rev: 5.9.3 hooks: - id: isort - - repo: https://github.com/python/black + - repo: https://github.com/psf/black rev: 21.9b0 hooks: - id: black args: [ --safe ] - - repo: https://github.com/asottile/setup-cfg-fmt - rev: v1.17.0 + - repo: https://github.com/asottile/blacken-docs + rev: v1.11.0 hooks: - - id: setup-cfg-fmt - args: ['--max-py-version', '3.10'] + - id: blacken-docs + additional_dependencies: [ black==21.9b0 ] + - repo: https://github.com/pre-commit/pygrep-hooks + rev: v1.9.0 + hooks: + - id: rst-backticks - repo: https://github.com/tox-dev/tox-ini-fmt rev: "0.5.1" hooks: - id: tox-ini-fmt + args: [ "-p", "fix" ] + - repo: https://github.com/asottile/setup-cfg-fmt + rev: v1.17.0 + hooks: + - id: setup-cfg-fmt + args: [ --min-py3-version, "3.7", "--max-py-version", "3.10" ] + - repo: https://github.com/PyCQA/flake8 + rev: 3.9.2 + hooks: + - id: flake8 + additional_dependencies: + - flake8-bugbear==21.9.1 + - flake8-comprehensions==3.6.1 + - flake8-pytest-style==1.5 + - flake8-spellcheck==0.24 + - flake8-unused-arguments==0.0.6 + - flake8-noqa==1.1.0 + - flake8-eradicate==1.1.0 + - pep8-naming==0.12.1 diff --git a/setup.cfg b/setup.cfg index c8fe924..21aca4b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -17,7 +17,6 @@ classifiers = Programming Language :: Python Programming Language :: Python :: 3 Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 @@ -35,7 +34,7 @@ install_requires = click>=7 pathspec>=0.5.9,<1 typed-ast>=1.3 -python_requires = >=3.6 +python_requires = >=3.7 package_dir = =src zip_safe = False @@ -48,9 +47,10 @@ console_scripts = retype=retype.__main__:main [options.extras_require] -testing = +test = + covdefaults>=2 pytest>=6 - pytest-cov>=2.5.1 + pytest-cov>=3 [flake8] ignore = @@ -60,24 +60,36 @@ ignore = E225, E302, E203, + E800, E741, max-line-length = 88 max-complexity = 32 select = B,C,E,F,T4,W,B9 +noqa-require-code = true [coverage:run] -branch = true +plugins = covdefaults parallel = true +[coverage:paths] +src = + src + .tox/*/lib/python*/site-packages + .tox/pypy*/site-packages + .tox\*\Lib\site-packages\ + */src + *\src +other = + . + */retype + *\retype + [coverage:report] -skip_covered = True -show_missing = True +fail_under = 88 -[coverage:paths] -source = - src/retype - .tox/*/lib/python*/site-packages/retype - .tox/pypy*/site-packages/retype - .tox\*\Lib\site-packages\retype - */src/retype - *\src\retype +[coverage:html] +show_contexts = true +skip_covered = false + +[coverage:covdefaults] +subtract_omit = */.tox/* diff --git a/setup.py b/setup.py index 7792b3d..f22df39 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,6 @@ """Handle the package management of the module""" +from __future__ import annotations + from setuptools import setup setup() diff --git a/src/retype/__init__.py b/src/retype/__init__.py index 558d45c..23066d2 100644 --- a/src/retype/__init__.py +++ b/src/retype/__init__.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 """Re-apply type annotations from .pyi stubs to your codebase.""" +from __future__ import annotations import os import re @@ -37,7 +38,7 @@ def retype_path( extra_ignore = [] for folder in [pyi_dir, targets]: try: - extra_ignore.append("/{}".format(folder.relative_to(src))) + extra_ignore.append(f"/{folder.relative_to(src)}") except ValueError: pass for file in walk_not_git_ignored( diff --git a/src/retype/__main__.py b/src/retype/__main__.py index e7b16bd..c0d8167 100644 --- a/src/retype/__main__.py +++ b/src/retype/__main__.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import sys from functools import partial from pathlib import Path diff --git a/src/retype/config.py b/src/retype/config.py index 1784f2a..8a25196 100644 --- a/src/retype/config.py +++ b/src/retype/config.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + class ReApplyFlags: __slots__ = ("replace_any", "incremental") diff --git a/src/retype/retype_hgext.py b/src/retype/retype_hgext.py index d0b9f59..91cedd4 100644 --- a/src/retype/retype_hgext.py +++ b/src/retype/retype_hgext.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import io import token import tokenize diff --git a/tests/test_discovery.py b/tests/test_discovery.py index e34aa33..cf6c0eb 100644 --- a/tests/test_discovery.py +++ b/tests/test_discovery.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from collections import namedtuple from pathlib import Path diff --git a/tests/test_entry_points.py b/tests/test_entry_points.py index 14f8657..ac1b4e8 100644 --- a/tests/test_entry_points.py +++ b/tests/test_entry_points.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import subprocess import sys from pathlib import Path diff --git a/tests/test_retype.py b/tests/test_retype.py index af732b4..7ad6395 100644 --- a/tests/test_retype.py +++ b/tests/test_retype.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 +from __future__ import annotations import os from contextlib import contextmanager from pathlib import Path from textwrap import dedent -from typing import Iterator, Optional +from typing import Iterator from unittest import TestCase, main from typed_ast import ast3 @@ -103,7 +104,7 @@ def assertReapplyRaises( class ImportTestCase(RetypeTestCase): IMPORT = "import x" - def _test_matched(self, matched: str, expected: Optional[str] = None) -> None: + def _test_matched(self, matched: str, expected: str | None = None) -> None: pyi = f"{self.IMPORT}\n" src = f"{matched}\n" expected = f"{expected if expected is not None else matched}\n" diff --git a/tox.ini b/tox.ini index 6485bc3..f53e8cf 100644 --- a/tox.ini +++ b/tox.ini @@ -1,40 +1,49 @@ [tox] envlist = + fix py310 py39 py38 py37 - py36 coverage merge type - fix_lint - package_readme + readme isolated_build = true skip_missing_interpreters = true minversion = 3.14 [testenv] -description = run tests with {basepython} -passenv = - CI_RUN - HOME - PIP_* - PYTEST_* - TERM +description = run the unit tests with pytest under {basepython} setenv = COVERAGE_FILE = {toxworkdir}/.coverage.{envname} + COVERAGE_PROCESS_START = {toxinidir}/setup.cfg + _COVERAGE_SRC = {envsitepackagesdir}/sphinx_argparse_cli extras = - testing + test commands = - pytest \ - --cov "{envsitepackagesdir}/retype" \ - --cov-report xml:{toxworkdir}/coverage.{envname}.xml \ - --junitxml {toxworkdir}/junit.{envname}.xml \ - tests {posargs} + pytest {tty:--color=yes} {posargs: \ + --junitxml {toxworkdir}{/}junit.{envname}.xml --cov {envsitepackagesdir}{/}retype \ + --cov {toxinidir}{/}tests \ + --cov-config=setup.cfg --no-cov-on-fail --cov-report term-missing:skip-covered --cov-context=test \ + --cov-report html:{envtmpdir}{/}htmlcov --cov-report xml:{toxworkdir}{/}coverage.{envname}.xml \ + tests} package = wheel wheel_build_env = .pkg +[testenv:fix] +description = run static analysis and style check using flake8 +passenv = + HOMEPATH + PROGRAMDATA +basepython = python3.10 +skip_install = true +deps = + pre-commit>=2 +commands = + pre-commit run --all-files --show-diff-on-failure + python -c 'print("hint: run {envdir}/bin/pre-commit install to add checks as pre-commit hook")' + [testenv:coverage] description = [run locally after tests]: combine coverage data and create report; generates a diff coverage against origin/master (can be changed by setting DIFF_AGAINST env var) @@ -44,69 +53,63 @@ setenv = COVERAGE_FILE = {toxworkdir}/.coverage skip_install = true deps = - coverage>=5 - diff_cover -extras = + covdefaults>=2 + coverage>=6.2 + diff-cover>=6.4 parallel_show_output = true commands = coverage combine - coverage report --show-missing + coverage report -m coverage xml -o {toxworkdir}/coverage.xml coverage html -d {toxworkdir}/htmlcov - diff-cover --compare-branch {env:DIFF_AGAINST:origin/master} {toxworkdir}/coverage.xml + diff-cover --compare-branch {env:DIFF_AGAINST:origin/main} {toxworkdir}/coverage.xml depends = py310 py39 py38 py37 - py36 [testenv:merge] description = try to merge our types against our types deps = - {[testenv:type]deps} + mypy==0.910 + types-typed-ast changedir = {envtmpdir} commands = python -m retype -p {toxinidir}/types -t {envtmpdir} {toxinidir} mypy {envtmpdir} --strict --ignore-missing-imports {posargs} [testenv:type] -description = run the type checker -skip_install = true +description = run type check on code base +setenv = + {tty:MYPY_FORCE_COLOR = 1} deps = - mypy==0.910 + mypy==0.930 types-typed-ast commands = - mypy types --strict + mypy --strict --python-version 3.10 types -[testenv:fix_lint] -description = format the code base to adhere to our styles, and complain about what we cannot do automatically -passenv = - {[testenv]passenv} - PROGRAMDATA +[testenv:readme] +description = check that the long description is valid +basepython = python3.10 skip_install = true deps = - pre-commit>=2 -extras = - lint + build[virtualenv]>=0.7 + twine>=3.7 commands = - pre-commit run --all-files --show-diff-on-failure - python -c 'import pathlib; print("hint: run \{\} install to add checks as pre-commit hook".format(pathlib.Path(r"{envdir}") / "bin" / "pre-commit"))' - -[testenv:package_readme] -description = check that the long description is valid (need for PyPi) -skip_install = true -deps = - build>=0.5 - twine>=3 -extras = -commands = - python -m build -o {envtmpdir}/build --sdist --wheel . - twine check {envtmpdir}/build/* + python -m build --sdist --wheel -o {envtmpdir} . + twine check {envtmpdir}/* [testenv:dev] description = generate a DEV environment +basepython = python3.10 usedevelop = true +extras = + docs + test commands = python -m pip list --format=columns python -c 'import sys; print(sys.executable)' + +[pytest] +junit_family = xunit2