diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index 16bbaa36..00000000 --- a/.coveragerc +++ /dev/null @@ -1,2 +0,0 @@ -[run] -source = hamcrest diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 00000000..53404714 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,101 @@ +--- +name: CI + +on: + push: + branches: ["master", "ci-testing"] + + pull_request: + branches: ["master"] + + workflow_dispatch: + +jobs: + tests: + name: "Python ${{ matrix.python-version }}" + runs-on: "ubuntu-latest" + env: + USING_COVERAGE: "3.7,3.8" + + strategy: + matrix: + python-version: + - "3.6" + - "3.7" + - "3.8" + - "3.9" + - "pypy2" + # disabled due to one failing test + # - "pypy3" + + steps: + - uses: "actions/checkout@v2" + - uses: "actions/setup-python@v2" + with: + python-version: "${{ matrix.python-version }}" + - name: "Install dependencies" + run: | + set -xe + python -VV + python -msite + python -m pip install --upgrade pip setuptools wheel + python -m pip install --upgrade coverage[toml] virtualenv tox tox-gh-actions + + - name: "Run tox targets for ${{ matrix.python-version }}" + run: "python -m tox" + + # We always use a modern Python version for combining coverage to prevent + # parsing errors in older versions for modern code. + - uses: "actions/setup-python@v2" + with: + python-version: "3.8" + + - name: "Combine coverage" + run: | + set -xe + python -m pip install coverage[toml] + python -m coverage combine + python -m coverage xml + if: "contains(env.USING_COVERAGE, matrix.python-version)" + - name: "Upload coverage to Codecov" + if: "contains(env.USING_COVERAGE, matrix.python-version)" + uses: "codecov/codecov-action@v1" + with: + fail_ci_if_error: true + + package: + name: "Build & verify package" + runs-on: "ubuntu-latest" + + steps: + - uses: "actions/checkout@v2" + - uses: "actions/setup-python@v1" + with: + python-version: "3.8" + + - name: "Install pep517 and twine" + run: "python -m pip install pep517 twine" + - name: "Build package" + run: "python -m pep517.build --source --binary ." + - name: "List result" + run: "ls -l dist" + - name: "Check long_description" + run: "python -m twine check dist/*" + + install-dev: + strategy: + matrix: + os: ["ubuntu-latest", "windows-latest", "macos-latest"] + + name: "Verify dev env" + runs-on: "${{ matrix.os }}" + + steps: + - uses: "actions/checkout@v2" + - uses: "actions/setup-python@v1" + with: + python-version: "3.8" + - name: "Install in dev mode" + run: "python -m pip install -e .[dev]" + - name: "Import package" + run: "python -c 'import hamcrest; print(hamcrest.__version__)'" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d3ddc33b..99eec3d6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,7 +20,10 @@ repos: hooks: - id: flake8 exclude: >- - (?x)^examples/.*\.py$ + (?x)^( + examples/.*\.py$ + | doc/.*\.py$ + ) - repo: https://github.com/psf/black rev: 19.10b0 diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 00000000..511ae165 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,11 @@ +--- +version: 2 +python: + # Keep version in sync with tox.ini (docs and gh-actions). + version: 3.7 + + install: + - method: pip + path: . + extra_requirements: + - docs diff --git a/.travis.yml b/.travis.yml index 0ed2abc3..3111d958 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,9 +6,6 @@ matrix: - python: 3.6 env: - TOX_ENV=py36 - - python: 3.6 - env: - - TOX_ENV=pypy3.6 - python: 3.6 env: - TOX_ENV=py36-numpy @@ -37,12 +34,12 @@ matrix: - python: 3.6 env: - TOX_ENV=docs-py3 - - python: 3.7 + - python: 3.8 env: - - TOX_ENV=check-format + - TOX_ENV=lint - python: 3.8 env: - - TOX_ENV=mypy + - TOX_ENV=typing before_install: - export EASY_SETUP_URL='http://peak.telecommunity.com/dist/ez_setup.py' diff --git a/CHANGES.txt b/CHANGELOG.rst similarity index 92% rename from CHANGES.txt rename to CHANGELOG.rst index bddf8740..cae1008d 100644 --- a/CHANGES.txt +++ b/CHANGELOG.rst @@ -1,12 +1,18 @@ -=== Version 2.0.2 === +Changelog +========= + +Version 2.0.2 +------------- Various type hint bug fixes. -=== Version 2.0.1 === +Version 2.0.1 +------------- * Make hamcrest package PEP 561 compatible, i.e. supply type hints for external use. -=== Version 2.0.0 == +Version 2.0.0 +------------- Drop formal support for 2.x Drop formal support for 3.x < 3.5 @@ -18,7 +24,8 @@ Fix #128 - raises() grows support for additional matchers on exception object. * Type fixes. * Remove obsolete dependencies. -=== Version 1.10.1 == +Version 1.10.1 +-------------- Add support up to Python 3.8 @@ -26,36 +33,43 @@ Fix #66 - deprecate contains() in favour of contains_exactly(). Fix #72 - make has_properties mismatch description less verbose by adding option to AllOf not to include matcher description in its mismatch messages. Fix #82 - include exception details in mismatch description. -=== Version 1.9.0 == +Version 1.9.0 +------------- Drop formal support for 2.x < 2.7 Drop formal support for 3.x < 3.4 Fix #62 - Return result of a deferred call -=== Version 1.8.5 === +Version 1.8.5 +------------- Fix #56 - incorrect handling of () in is_ matcher Fix #60 - correct calling API call with args -=== Version 1.8.4 == +Version 1.8.4 +------------- * Fix #54 - Make instance_of work with tuple like isinstance and unittest's assertIsInstance -=== Version 1.8.3 === +Version 1.8.3 +------------- * Fix #52 - bad handling when reporting mismatches for byte arrays in Python 3 -=== Version 1.8.2 === +Version 1.8.2 +------------- * [Bug] Fix unicode syntax via u() introduction (puppsman) -=== Version 1.8.1 === +Version 1.8.1 +------------- * Added not_ alias for is_not [Matteo Bertini] * Added doc directory to the sdist [Alex Brandt] -=== Version 1.8 == +Version 1.8 +----------- * Supported versions - Support for Python 2.5 and Jython 2.5 has been dropped. They may still work, but no promises. @@ -67,7 +81,8 @@ Fix #60 - correct calling API call with args - Support for numpy numeric values in iscloseto (Alexander Beedie) - A matcher targeting exceptions and call results (Per Fagrell) -=== Version 1.7 == +Version 1.7 +----------- 2 Sep 2013 (Version 1.7.2) * Supported versions @@ -100,7 +115,8 @@ Fix #60 - correct calling API call with args - README enhancements by ming13 -=== Version 1.6 == +Version 1.6 +----------- 27 Sep 2011 (All changes by Chris Rose unless otherwise noted.) @@ -119,7 +135,8 @@ Fix #60 - correct calling API call with args - Rewrote documentation. (Jon Reid) -== Version 1.5 == +Version 1.5 +----------- 29 Apr 2011 * Packaging: @@ -138,7 +155,8 @@ Fix #60 - correct calling API call with args None. -== Version 1.4 == +Version 1.4 +----------- 13 Feb 2011 * New matchers: @@ -152,7 +170,8 @@ Fix #60 - correct calling API call with args - Consistently use articles to begin descriptions, such as "a sequence containing" instead of "sequence containing". -== Version 1.3 == +Version 1.3 +----------- 04 Feb 2011 * PyHamcrest is now compatible with Python 3! To install PyHamcrest on Python 3: @@ -171,7 +190,8 @@ Fix #60 - correct calling API call with args - Improved readability of several matchers. -== Version 1.2.1 == +Version 1.2.1 +------------- 04 Jan 2011 * Fixed "assert_that" to describe the diagnosis of the mismatch, not just the @@ -188,7 +208,8 @@ mismatched value. PyHamcrest will now give even more useful information. - Corrected manifest so install works. Thanks to: Jeong-Min Lee -== Version 1.1 == +Version 1.1 +----------- 28 Dec 2010 * New matchers: @@ -198,7 +219,8 @@ mismatched value. PyHamcrest will now give even more useful information. * Added Sphinx documentation support. -== Version 1.0 == +Version 1.0 +----------- 04 Dec 2010 * First official release diff --git a/MANIFEST.in b/MANIFEST.in index 3f81fd20..4fc5a7a7 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,26 @@ -include CHANGES.txt -include LICENSE.txt -include README.md +include LICENSE.txt *.rst *.md *.toml *.yml *.yaml *.ini +include requirements-* +graft .github + +# Tests +include tox.ini conftest.py +recursive-include tests *.py +recursive-include tests *.yml + +# Documentation +include doc/Makefile doc/docutils.conf recursive-include examples *.py -recursive-include doc * +recursive-include doc *.png +recursive-include doc *.svg +recursive-include doc *.py +recursive-include doc *.rst +prune doc/_build + +# remove some of the random source +prune docker +exclude release.sh + +# Just to keep check-manifest happy; on releases those files are gone. +# Last rule wins! +exclude changelog.d/*.rst +include changelog.d/towncrier_template.rst diff --git a/changelog.d/towncrier_template.rst b/changelog.d/towncrier_template.rst new file mode 100644 index 00000000..3125265a --- /dev/null +++ b/changelog.d/towncrier_template.rst @@ -0,0 +1,20 @@ +{% for section, _ in sections.items() %} {% set underline = underlines[0] %}{% if section %}{{section}} {{ underline * section|length }}{% set underline = underlines[1] %} + +{% endif %} + +{% if sections[section] %} {% for category, val in definitions.items() if category in sections[section]%} {{ definitions[category]['name'] }} {{ underline * definitions[category]['name']|length }} + +{% if definitions[category]['showcontent'] %} {% for text, values in sections[section][category].items() %} - {{ text }} + +{{ values|join(',n ') }} +{% endfor %} + +{% else %} - {{ sections[section][category]['']|join(', ') }} + +{% endif %} {% if sections[section][category]|length == 0 %} No significant changes. + +{% else %} {% endif %} + +{% endfor %} {% else %} No significant changes. + +{% endif %} {% endfor %} ---- diff --git a/pyproject.toml b/pyproject.toml index 2e917d2f..ffb17d07 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,2 +1,59 @@ +[build-system] +requires = ["setuptools>=40.6.0", "wheel"] +build-backend = "setuptools.build_meta" + + +[tool.coverage.run] +parallel = true +branch = true +source = ["hamcrest"] + +[tool.coverage.paths] +source = ["src", ".tox/*/site-packages"] + +[tool.coverage.report] +show_missing = true + [tool.black] line_length = 100 + +[tool.interrogate] +verbose = 2 +fail-under = 100 +whitelist-regex = ["test_.*"] + + +[tool.isort] +profile = "hamcrests" + +known_first_party = "hamcrest" +known_third_party = ["hypothesis", "pytest", "setuptools", "six"] + + +[tool.towncrier] + package = "hamcrest" + package_dir = "src" + filename = "CHANGELOG.rst" + template = "changelog.d/towncrier_template.rst" + issue_format = "`#{issue} `_" + directory = "changelog.d" + title_format = "{version} ({project_date})" + underlines = ["-", "^"] + + [[tool.towncrier.section]] + path = "" + + [[tool.towncrier.type]] + directory = "breaking" + name = "Backward-incompatible Changes" + showcontent = true + + [[tool.towncrier.type]] + directory = "deprecation" + name = "Deprecations" + showcontent = true + + [[tool.towncrier.type]] + directory = "change" + name = "Changes" + showcontent = true diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 7d320b96..00000000 --- a/setup.cfg +++ /dev/null @@ -1,3 +0,0 @@ -[egg_info] -;tag_build = .dev -;tag_svn_revision = true diff --git a/setup.py b/setup.py index ef918784..8e16e646 100755 --- a/setup.py +++ b/setup.py @@ -31,6 +31,10 @@ def read(fname): assert __version__ is not None +TESTS_BASIC = ["pytest>=5.0", "pytest-sugar", "coverage"] +TESTS_NUMPY = ["numpy"] + + params = dict( name="PyHamcrest", version=__version__, # flake8:noqa @@ -50,6 +54,11 @@ def read(fname): long_description=read("README.rst"), python_requires=">=3.5", install_requires=[], + extras_require={ + "docs": ["sphinx~=3.0", "sphinx_rtd_theme~=0.4"], + "tests": TESTS_BASIC, + "tests-numpy": TESTS_BASIC + TESTS_NUMPY, + }, classifiers=[ "Development Status :: 5 - Production/Stable", "Environment :: Console", diff --git a/tox.ini b/tox.ini index 867bfb73..d3ce223b 100644 --- a/tox.ini +++ b/tox.ini @@ -1,108 +1,130 @@ +[pytest] +addopts = -ra +testpaths = tests +xfail_strict = true +filterwarnings = + once::Warning + ignore:::pympler[.*] + + +# Keep docs in sync with docs env and .readthedocs.yml. +[gh-actions] +python = + 3.6: py36, py36-numpy + 3.7: py37, docs + 3.8: py38, lint, manifest, typing, changelog + 3.9: py39 + pypy2: pypy2 + pypy3: pypy3 + + [tox] -envlist = py36,py37,py38,pypy2.7,pypy3.6,test-hinting,docs -tox_pyenv_fallback = False -# Jython is not testable, but there's no reason it should not work. +envlist = typing,lint,py36,py37,py38,py39,pypy,pypy3,manifest,docs,pypi-description,changelog,coverage-report +isolated_build = True + [testenv] -commands = {envbindir}/py.test [] - {envpython} tests/object_import.py -deps = -r requirements-test.txt +# Prevent random setuptools/pip breakages like +# https://github.com/pypa/setuptools/issues/1042 from breaking our builds. +setenv = + VIRTUALENV_NO_DOWNLOAD=1 +extras = {env:TOX_AP_TEST_EXTRAS:tests} +commands = python -m pytest {posargs} + + +[testenv:py27] +extras = {env:TOX_AP_TEST_EXTRAS:tests} +commands = coverage run -m pytest {posargs} [testenv:py36-numpy] -basepython = python3.6 -deps = {[testenv]deps} - numpy -setenv = - PYTHONHASHSEED = 4 +extras = tests-numpy +commands = python -m pytest {posargs} -[testenv:docs] -basepython = python3 -deps = -r requirements.docs.txt -changedir = {toxinidir}/doc -commands = sphinx-build -b html -d {envtmpdir}/doctrees . {envtmpdir}/html +[testenv:py37] +# Python 3.6+ has a number of compile-time warnings on invalid string escapes. +# PYTHONWARNINGS=d and --no-compile below make them visible during the Tox run. +install_command = pip install --no-compile {opts} {packages} +setenv = + PYTHONWARNINGS=d +extras = {env:TOX_AP_TEST_EXTRAS:tests} +commands = coverage run -m pytest {posargs} -[testenv:format] -basepython = python3 -skip_install = true -deps = - black~=19.10b0 - isort~=4.0 -commands = - isort {toxinidir}/setup.py - isort -rc {toxinidir}/src/ - isort -rc {toxinidir}/tests/ - black -l100 -tpy35 src/ tests/ setup.py -[testenv:check-format] -basepython = python3 +[testenv:py38] +# Python 3.6+ has a number of compile-time warnings on invalid string escapes. +# PYTHONWARNINGS=d and --no-compile below make them visible during the Tox run. +basepython = python3.8 +install_command = pip install --no-compile {opts} {packages} +setenv = + PYTHONWARNINGS=d +extras = {env:TOX_AP_TEST_EXTRAS:tests} +commands = coverage run -m pytest {posargs} + + +[testenv:coverage-report] +basepython = python3.7 skip_install = true -deps = {[testenv:format]deps} +deps = coverage[toml]>=5.0.2 commands = - isort --check-only {toxinidir}/setup.py - isort --check-only -rc {toxinidir}/src/ - isort --check-only -rc {toxinidir}/tests/ - black --check -l100 -tpy35 src/ tests/ setup.py - -[tool:isort] -multi_line_output=3 -include_trailing_comma=True -force_grid_wrap=0 -use_parentheses=True -line_length=100 - -[testenv:flake8] -basepython = python3 + coverage combine + coverage report + + +[testenv:lint] +basepython = python3.8 skip_install = true deps = - flake8~=3.0 - flake8-bugbear~=18.0 - flake8-comprehensions~=1.0 - flake8-mutable~=1.0 - mccabe~=0.6 - flake8-blind-except~=0.1 - flake8-builtins~=1.0 - flake8-pep3101~=1.0 - flake8-print~=3.0 - flake8-string-format~=0.2 - flake8-logging-format~=0.5 + pre-commit +passenv = HOMEPATH # needed on Windows +commands = + pre-commit run --all-files + +[testenv:docs] +# Keep basepython in sync with gh-actions and .readthedocs.yml. +basepython = python3.7 +extras = docs commands = - flake8 src/ tests/ setup.py + sphinx-build -n -T -b html -d {envtmpdir}/doctrees doc doc/_build/html -[flake8] -max-complexity = 5 -max-line-length = 100 -show-source = True -enable-extensions = M,B,C,T,P -ignore = C812,W503,P103,E1,E2,E3,E5 -statistics = True -[testenv:mypy] +[testenv:manifest] basepython = python3.8 +deps = check-manifest skip_install = true -deps = - mypy~=0.6 -commands = - mypy src/ tests/ --ignore-missing-imports {posargs} +commands = check-manifest + -[testenv:test-hinting] -basepython = python3 +[testenv:pypi-description] +basepython = python3.8 +skip_install = true deps = - pytest-mypy-plugins~=1.2 + twine + pip >= 18.0.0 commands = - pytest --mypy-ini-file=tox.ini tests/type-hinting/ {posargs} + pip wheel -w {envtmpdir}/build --no-deps . + twine check {envtmpdir}/build/* -[testenv:pyre] -basepython = python3.7 + +[testenv:changelog] +basepython = python3.8 +deps = towncrier skip_install = true -deps = - pyre-check +commands = towncrier --draft + + +[testenv:typing] +basepython = python3.8 +deps = mypy commands = - pyre --source-directory src/ check {posargs} - pyre --source-directory tests/ --search-path src/ check {posargs} - -[mypy] -check_untyped_defs = True -ignore_errors = False -#ignore_missing_imports = True -strict_optional = True + mypy src/ + + + +[flake8] +max-complexity = 15 +max-line-length = 100 +show-source = True +enable-extensions = M,B,C,T,P +ignore = C812,W503,P103,E1,E2,E3,E5,F405,F401,F403,E713,E722,W605 +statistics = True