diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 08400496b9..ed17fdb161 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -73,12 +73,6 @@ jobs: vmImage: 'windows-2019' strategy: matrix: - check-py27-x64: - python.version: '2.7' - python.architecture: 'x64' - check-py27-x86: - python.version: '2.7' - python.architecture: 'x86' check-py36-x64: python.version: '3.6' python.architecture: 'x64' @@ -97,7 +91,33 @@ jobs: displayName: Install dependencies - script: | cd hypothesis-python - pytest tests/cover/ tests/datetime/ tests/dpcontracts/ tests/lark/ tests/numpy/ tests/pandas/ tests/pytest/ + pytest + displayName: Run tests + + - job: windows_py2 + pool: + vmImage: 'windows-2019' + strategy: + matrix: + check-py27-x64: + python.version: '2.7' + python.architecture: 'x64' + check-py27-x86: + python.version: '2.7' + python.architecture: 'x86' + steps: + - task: UsePythonVersion@0 + inputs: + versionSpec: '$(python.version)' + architecture: '$(python.architecture)' + - script: | + pip install --upgrade setuptools pip wheel + pip install setuptools -r requirements/test2.txt + pip install hypothesis-python/[all] + displayName: Install dependencies + - script: | + cd hypothesis-python + pytest displayName: Run tests - job: osx diff --git a/hypothesis-python/setup.py b/hypothesis-python/setup.py index cf215118d8..a2108a410a 100644 --- a/hypothesis-python/setup.py +++ b/hypothesis-python/setup.py @@ -88,6 +88,11 @@ def local_file(name): package_dir={"": SOURCE}, package_data={"hypothesis": ["py.typed"]}, url="https://github.com/HypothesisWorks/hypothesis/tree/master/hypothesis-python", + project_urls={ + "Website": "https://hypothesis.works", + "Documentation": "https://hypothesis.readthedocs.io", + "Issues": "https://github.com/HypothesisWorks/hypothesis/issues", + }, license="MPL v2", description="A library for property based testing", zip_safe=False, @@ -115,5 +120,6 @@ def local_file(name): ], entry_points={"pytest11": ["hypothesispytest = hypothesis.extra.pytestplugin"]}, long_description=open(README).read(), + long_description_content_type="text/x-rst", keywords="python testing fuzzing property-based-testing", ) diff --git a/hypothesis-python/tests/conftest.py b/hypothesis-python/tests/conftest.py index b5def4f738..ed80ccf539 100644 --- a/hypothesis-python/tests/conftest.py +++ b/hypothesis-python/tests/conftest.py @@ -28,6 +28,10 @@ run() +# Skip collection of tests which require the Django test runner, +# or that don't work on the current major version of Python. +collect_ignore_glob = ["django/*", "py3/*" if sys.version_info[0] == 2 else "py2/*"] + def pytest_configure(config): config.addinivalue_line("markers", "slow: pandas expects this marker to exist.") diff --git a/hypothesis-python/tox.ini b/hypothesis-python/tox.ini index 425072c793..699bbbd359 100644 --- a/hypothesis-python/tox.ini +++ b/hypothesis-python/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py{27,35,36,37,py27}-{brief,prettyquick,full,custom} +envlist = py{35,36,37}-{brief,prettyquick,full,custom} toxworkdir={env:TOX_WORK_DIR:.tox} [testenv] @@ -20,6 +20,22 @@ commands = prettyquick: python -m pytest tests/cover/ custom: python -m pytest {posargs} +[testenv:py27-full] +deps = + -r../requirements/test2.txt +whitelist_externals= + bash +commands = + bash scripts/basic-test.sh + +[testenv:pypy-full] +deps = + -r../requirements/test2.txt +whitelist_externals= + bash +commands = + bash scripts/basic-test.sh + [testenv:quality] deps= -r../requirements/test.txt @@ -30,14 +46,14 @@ commands= [testenv:quality2] basepython=python2.7 deps= - -r../requirements/test.txt + -r../requirements/test2.txt commands= python -m pytest tests/quality/ [testenv:py27typing] basepython=python2.7 deps= - -r../requirements/test.txt + -r../requirements/test2.txt -r../requirements/typing.txt commands= python -m pytest tests/cover/ -n2 @@ -155,7 +171,7 @@ setenv= HYPOTHESIS_PROFILE=with_coverage basepython=pypy deps = - -r../requirements/test.txt + -r../requirements/test2.txt commands = python -m pytest tests/cover/test_testdecorators.py tests/nocover/test_coverage.py -n 0 {posargs} @@ -170,6 +186,6 @@ commands= [testenv:examples2] basepython=python2.7 deps= - -r../requirements/test.txt + -r../requirements/test2.txt commands= python -m pytest examples diff --git a/requirements/test.in b/requirements/test.in index 7e1e6bec18..b1d0c33551 100644 --- a/requirements/test.in +++ b/requirements/test.in @@ -1,5 +1,4 @@ attrs mock -more-itertools<6 # Py3-only in 6.0+ pytest pytest-xdist diff --git a/requirements/test.txt b/requirements/test.txt index d4a554df35..416d3df34e 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -10,14 +10,14 @@ attrs==19.1.0 execnet==1.6.0 # via pytest-xdist importlib-metadata==0.18 # via pluggy, pytest mock==3.0.5 -more-itertools==5.0.0 +more-itertools==5.0.0 # via pytest packaging==19.0 # via pytest pluggy==0.12.0 # via pytest py==1.8.0 # via pytest pyparsing==2.4.0 # via packaging pytest-forked==1.0.2 # via pytest-xdist pytest-xdist==1.29.0 -pytest==4.6.3 -six==1.12.0 # via mock, more-itertools, packaging, pytest, pytest-xdist +pytest==5.0.0 +six==1.12.0 # via mock, more-itertools, packaging, pytest-xdist wcwidth==0.1.7 # via pytest zipp==0.5.1 # via importlib-metadata diff --git a/requirements/test2.txt b/requirements/test2.txt new file mode 100644 index 0000000000..c5301e1481 --- /dev/null +++ b/requirements/test2.txt @@ -0,0 +1,22 @@ +# +# This file is no longer being updated, as several of +# the packages below no longer support Python 2. +# +apipkg==1.5 # via execnet +atomicwrites==1.3.0 # via pytest +attrs==19.1.0 +colorama==0.4.1 # via pytest +execnet==1.6.0 # via pytest-xdist +importlib-metadata==0.18 # via pluggy, pytest +mock==3.0.5 +more-itertools==5.0.0 +packaging==19.0 # via pytest +pluggy==0.12.0 # via pytest +py==1.8.0 # via pytest +pyparsing==2.4.0 # via packaging +pytest-forked==1.0.2 # via pytest-xdist +pytest-xdist==1.29.0 +pytest==4.6.4 +six==1.12.0 # via mock, more-itertools, packaging, pytest-xdist +wcwidth==0.1.7 # via pytest +zipp==0.5.1 # via importlib-metadata diff --git a/requirements/tools.in b/requirements/tools.in index cbc8c4565c..270a390103 100644 --- a/requirements/tools.in +++ b/requirements/tools.in @@ -12,7 +12,6 @@ ipython isort lark-parser mock -more-itertools<6 # Py3-only in 6.0+ mypy numpy pip-tools diff --git a/requirements/tools.txt b/requirements/tools.txt index a98202ca23..b21018b908 100644 --- a/requirements/tools.txt +++ b/requirements/tools.txt @@ -12,7 +12,7 @@ attrs==19.1.0 autoflake==1.3 babel==2.7.0 # via sphinx backcall==0.1.0 # via ipython -bandit==1.6.1 +bandit==1.6.2 black==19.3b0 bleach==3.1.0 # via readme-renderer certifi==2019.6.16 # via requests @@ -21,7 +21,7 @@ click==7.0 # via black, pip-tools, pyupio, safety coverage==4.5.3 decorator==4.4.0 # via ipython, traitlets deprecated==1.2.5 # via pygithub -django==2.2.2 +django==2.2.3 docutils==0.14 # via readme-renderer, restructuredtext-lint, sphinx dparse==0.4.1 # via pyupio, safety dpcontracts==0.6.0 @@ -35,10 +35,10 @@ gitdb2==2.0.5 # via gitpython gitpython==2.1.11 # via bandit idna==2.8 # via requests imagesize==1.1.0 # via sphinx -importlib-metadata==0.18 # via pluggy, pytest +importlib-metadata==0.18 # via pluggy, pytest, tox ipython-genutils==0.2.0 # via traitlets -ipython==7.5.0 -isort==4.3.20 +ipython==7.6.0 +isort==4.3.21 jedi==0.14.0 # via ipython jinja2==2.10.1 # via pyupio, sphinx lark-parser==0.7.1 @@ -46,11 +46,11 @@ lazy-object-proxy==1.4.1 # via astroid markupsafe==1.1.1 # via jinja2 mccabe==0.6.1 # via flake8, pylint mock==3.0.5 -more-itertools==5.0.0 +more-itertools==7.1.0 # via pytest mypy-extensions==0.4.1 # via mypy -mypy==0.710 +mypy==0.711 numpy==1.16.4 -packaging==19.0 # via dparse, pytest, pyupio, safety, sphinx +packaging==19.0 # via dparse, pytest, pyupio, safety, sphinx, tox parso==0.5.0 # via jedi pbr==5.3.1 # via stevedore pexpect==4.7.0 # via ipython @@ -69,7 +69,7 @@ pygments==2.4.2 # via ipython, readme-renderer, sphinx pyjwt==1.7.1 # via pygithub pylint==2.3.1 pyparsing==2.4.0 # via packaging -pytest==4.6.3 +pytest==5.0.0 python-dateutil==2.8.0 python-gitlab==1.9.0 # via pyupio pytz==2019.1 # via babel, django @@ -81,9 +81,9 @@ requests-toolbelt==0.9.1 # via twine requests==2.22.0 restructuredtext-lint==1.3.0 safety==1.8.5 # via pyupio -six==1.12.0 # via astroid, bandit, bleach, dparse, mock, more-itertools, packaging, pip-tools, prompt-toolkit, pydocstyle, pytest, python-dateutil, python-gitlab, pyupio, readme-renderer, stevedore, tox, traitlets +six==1.12.0 # via astroid, bandit, bleach, dparse, mock, packaging, pip-tools, prompt-toolkit, pydocstyle, python-dateutil, python-gitlab, pyupio, readme-renderer, stevedore, tox, traitlets smmap2==2.0.5 # via gitdb2 -snowballstemmer==1.2.1 # via pydocstyle, sphinx +snowballstemmer==1.9.0 # via pydocstyle, sphinx sphinx-rtd-theme==0.4.3 sphinx==2.1.2 sphinxcontrib-applehelp==1.0.1 # via sphinx @@ -96,7 +96,7 @@ sqlparse==0.3.0 # via django stevedore==1.30.1 # via bandit tokenize-rt==3.0.1 # via pyupgrade toml==0.10.0 -tox==3.12.1 +tox==3.13.2 tqdm==4.32.2 # via pyupio, twine traitlets==4.3.2 # via ipython twine==1.13.0 @@ -109,4 +109,4 @@ wrapt==1.11.2 # via astroid, deprecated zipp==0.5.1 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: -# setuptools==41.0.1 # via ipython, safety, sphinx, tox, twine +# setuptools==41.0.1 # via ipython, safety, sphinx, twine diff --git a/tooling/src/hypothesistooling/__main__.py b/tooling/src/hypothesistooling/__main__.py index a4146e6638..92b9ef32da 100644 --- a/tooling/src/hypothesistooling/__main__.py +++ b/tooling/src/hypothesistooling/__main__.py @@ -347,20 +347,7 @@ def documentation(): try: if hp.has_release(): hp.update_changelog_and_version() - pip_tool( - # See https://www.sphinx-doc.org/en/stable/man/sphinx-build.html - "sphinx-build", - "-n", - "-W", - "--keep-going", - "-T", - "-E", - "-b", - "html", - "docs", - "docs/_build/html", - cwd=hp.HYPOTHESIS_PYTHON, - ) + hp.build_docs() finally: subprocess.check_call( ["git", "checkout", "docs/changes.rst", "src/hypothesis/version.py"], diff --git a/tooling/src/hypothesistooling/projects/hypothesispython.py b/tooling/src/hypothesistooling/projects/hypothesispython.py index 33b84054c2..f2c508c07d 100644 --- a/tooling/src/hypothesistooling/projects/hypothesispython.py +++ b/tooling/src/hypothesistooling/projects/hypothesispython.py @@ -68,6 +68,24 @@ def has_source_changes(): return tools.has_changes([PYTHON_SRC]) +def build_docs(builder="html"): + # See https://www.sphinx-doc.org/en/stable/man/sphinx-build.html + # (unfortunately most options only have the short flag version) + tools.scripts.pip_tool( + "sphinx-build", + "-n", + "-W", + "--keep-going", + "-T", + "-E", + "-b", + builder, + "docs", + "docs/_build/" + builder, + cwd=HYPOTHESIS_PYTHON, + ) + + CHANGELOG_ANCHOR = re.compile(r"^\.\. _v\d+\.\d+\.\d+:$") CHANGELOG_BORDER = re.compile(r"^-+$") CHANGELOG_HEADER = re.compile(r"^\d+\.\d+\.\d+ - \d\d\d\d-\d\d-\d\d$") @@ -159,11 +177,26 @@ def upload_distribution(): "-m", "twine", "upload", + "--skip-existing", "--config-file", tools.PYPIRC, os.path.join(DIST, "*"), ] ) + + # Construct plain-text + markdown version of this changelog entry, + # with link to canonical source. + build_docs(builder="text") + textfile = os.path.join(HYPOTHESIS_PYTHON, "docs", "_build", "text", "changes.txt") + with open(textfile) as f: + lines = f.readlines() + entries = [i for i, l in enumerate(lines) if CHANGELOG_HEADER.match(l)] + changelog_body = "".join(lines[entries[0] + 2 : entries[1]]).strip() + ( + "\n\n*[The canonical version of these notes (with links) is on readthedocs.]" + "(https://hypothesis.readthedocs.io/en/latest/changes.html#v%s).*" + % (current_version().replace(".", "-"),) + ) + # Create a GitHub release, to trigger Zenodo DOI minting. See # https://developer.github.com/v3/repos/releases/#create-a-release requests.post( @@ -171,17 +204,22 @@ def upload_distribution(): json=dict( tag_name=tag_name(), name="Hypothesis for Python - version " + current_version(), - body=( - "You can [read the changelog for this release here](" - "https://hypothesis.readthedocs.io/en/latest/changes.html#v" - "%s)." % (current_version().replace(".", "-"),) - ), + body=changelog_body, ), timeout=120, # seconds # Scoped personal access token, stored in Travis environ variable auth=("Zac-HD", os.environ["Zac_release_token"]), ).raise_for_status() + # Post the release notes to Tidelift too - see https://tidelift.com/docs/api + requests.post( + "https://api.tidelift.com/external-api/lifting/pypi/hypothesis/release-notes/" + + current_version(), + json={"body": changelog_body}, + headers={"Authorization": "Bearer {}".format(os.environ["TIDELIFT_API_TOKEN"])}, + timeout=120, # seconds + ).raise_for_status() + def current_version(): return __version__