From 52c67af63cc164a966a0dc5b49d1fb7965867152 Mon Sep 17 00:00:00 2001 From: David Giese Date: Tue, 9 May 2017 10:02:08 -0400 Subject: [PATCH 1/3] Clarify opening paragraph of parameterization docs - Fix a few small grammar mistakes - Rewrite a few sentences to make them shorter and easier to read --- doc/en/parametrize.rst | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/doc/en/parametrize.rst b/doc/en/parametrize.rst index b93099e7d..c261fb0ed 100644 --- a/doc/en/parametrize.rst +++ b/doc/en/parametrize.rst @@ -9,17 +9,16 @@ Parametrizing fixtures and test functions ========================================================================== -pytest supports test parametrization in several well-integrated ways: +pytest enables test parametrization at several levels: -- :py:func:`pytest.fixture` allows to define :ref:`parametrization - at the level of fixture functions `. +- :py:func:`pytest.fixture` allows one to :ref:`parametrize fixture + functions `. -* `@pytest.mark.parametrize`_ allows to define parametrization at the - function or class level, provides multiple argument/fixture sets - for a particular test function or class. +* `@pytest.mark.parametrize`_ allows one to define multiple sets of + arguments and fixtures at the test function or class. -* `pytest_generate_tests`_ enables implementing your own custom - dynamic parametrization scheme or extensions. +* `pytest_generate_tests`_ allows one to define custom parametrization + schemes or extensions. .. _parametrizemark: .. _`@pytest.mark.parametrize`: From 00e7ee532e1f692d508a2cb9241f4b78921f6f4c Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 15 May 2017 20:25:53 -0300 Subject: [PATCH 2/3] Fix minor regendoc issues --- doc/en/Makefile | 2 +- doc/en/unittest.rst | 3 ++- doc/en/warnings.rst | 2 +- tox.ini | 1 + 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/en/Makefile b/doc/en/Makefile index 5b534da09..286bbd8e7 100644 --- a/doc/en/Makefile +++ b/doc/en/Makefile @@ -41,7 +41,7 @@ clean: -rm -rf $(BUILDDIR)/* regen: - PYTHONDONTWRITEBYTECODE=1 PYTEST_ADDOPT=-p\ no:hypothesis COLUMNS=76 regendoc --update *.rst */*.rst ${REGENDOC_ARGS} + PYTHONDONTWRITEBYTECODE=1 PYTEST_ADDOPT=-pno:hypothesis COLUMNS=76 regendoc --update *.rst */*.rst ${REGENDOC_ARGS} html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html diff --git a/doc/en/unittest.rst b/doc/en/unittest.rst index 6ee7d505e..436e64e07 100644 --- a/doc/en/unittest.rst +++ b/doc/en/unittest.rst @@ -171,7 +171,8 @@ creation of a per-test temporary directory:: tmpdir.join("samplefile.ini").write("# testdata") def test_method(self): - s = open("samplefile.ini").read() + with open("samplefile.ini") as f: + s = f.read() assert "testdata" in s Due to the ``autouse`` flag the ``initdir`` fixture function will be diff --git a/doc/en/warnings.rst b/doc/en/warnings.rst index fb18c4a2f..64316cf6b 100644 --- a/doc/en/warnings.rst +++ b/doc/en/warnings.rst @@ -39,7 +39,7 @@ Running pytest now produces this output:: The ``-W`` flag can be passed to control which warnings will be displayed or even turn them into errors:: - $ pytest -q test_show_warning.py -W error::DeprecationWarning + $ pytest -q test_show_warnings.py -W error::DeprecationWarning no tests ran in 0.12 seconds ERROR: file not found: test_show_warning.py diff --git a/tox.ini b/tox.ini index 52bd471c4..a2d4af9ed 100644 --- a/tox.ini +++ b/tox.ini @@ -134,6 +134,7 @@ commands= [testenv:regen] changedir=doc/en +skipsdist=True basepython = python3.5 deps=sphinx PyYAML From feab3ba70ffcdca646d00080c1bdf32b4f1ed308 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 15 May 2017 20:23:04 -0300 Subject: [PATCH 3/3] Implement tasks to improve release automation --- HOWTORELEASE.rst | 27 ++++++++------- tasks/generate.py | 78 ++++++++++++++++++++++++++++++++++++------ tasks/requirements.txt | 3 ++ 3 files changed, 84 insertions(+), 24 deletions(-) create mode 100644 tasks/requirements.txt diff --git a/HOWTORELEASE.rst b/HOWTORELEASE.rst index 0d63cdbd4..21834c672 100644 --- a/HOWTORELEASE.rst +++ b/HOWTORELEASE.rst @@ -1,27 +1,28 @@ How to release pytest -------------------------------------------- -Note: this assumes you have already registered on PyPI and you have -`invoke `_ installed. +.. important:: -#. Check and finalize ``CHANGELOG.rst``. + pytest releases must be prepared on **linux** because the docs and examples expect + to be executed in that platform. -#. Generate a new release announcement:: +#. Install development dependencies in a virtual environment with:: - invoke generate.announce VERSION + pip3 install -r tasks/requirements.txt -Feel free to modify the generated files before committing. +#. Create a branch ``release-X.Y.Z`` with the version for the release. Make sure it is up to date + with the latest ``master`` (for patch releases) and with the latest ``features`` merged with + the latest ``master`` (for minor releases). Ensure your are in a clean work tree. -#. Regenerate the docs examples using tox:: +#. Check and finalize ``CHANGELOG.rst`` (will be automated soon). - tox -e regen +#. Execute to automatically generate docs, announcements and upload a package to + your ``devpi`` staging server:: -#. At this point, open a PR named ``release-X`` so others can help find regressions or provide suggestions. + invoke generate.pre_release --password -#. Use devpi for uploading a release tarball to a staging area:: - - devpi use https://devpi.net/USER/dev - devpi upload --formats sdist,bdist_wheel + If ``--password`` is not given, it is assumed the user is already logged in. If you don't have + an account, please ask for one! #. Run from multiple machines:: diff --git a/tasks/generate.py b/tasks/generate.py index 082cbe8cb..deb77ed4f 100644 --- a/tasks/generate.py +++ b/tasks/generate.py @@ -1,5 +1,6 @@ +import os from pathlib import Path -from subprocess import check_output +from subprocess import check_output, check_call import invoke @@ -9,11 +10,7 @@ import invoke }) def announce(ctx, version): """Generates a new release announcement entry in the docs.""" - print("[generate.announce] Generating Announce") - # Get our list of authors - print("[generate.announce] Collecting author names") - stdout = check_output(["git", "describe", "--abbrev=0", '--tags']) stdout = stdout.decode('utf-8') last_version = stdout.strip() @@ -29,12 +26,12 @@ def announce(ctx, version): contributors_text = '\n'.join('* {}'.format(name) for name in sorted(contributors)) + '\n' text = template_text.format(version=version, contributors=contributors_text) - target = Path(__file__).joinpath('../../doc/en/announce/release-{}.rst'.format(version)) + target = Path(__file__).parent.joinpath('../doc/en/announce/release-{}.rst'.format(version)) target.write_text(text, encoding='UTF-8') print("[generate.announce] Generated {}".format(target.name)) # Update index with the new release entry - index_path = Path(__file__).joinpath('../../doc/en/announce/index.rst') + index_path = Path(__file__).parent.joinpath('../doc/en/announce/index.rst') lines = index_path.read_text(encoding='UTF-8').splitlines() indent = ' ' for index, line in enumerate(lines): @@ -48,9 +45,68 @@ def announce(ctx, version): print("[generate.announce] Skip {} (already contains release)".format(index_path.name)) break + check_call(['git', 'add', str(target)]) + + +@invoke.task() +def regen(ctx): + """Call regendoc tool to update examples and pytest output in the docs.""" + print("[generate.regen] Updating docs") + check_call(['tox', '-e', 'regen']) + + +@invoke.task() +def make_tag(ctx, version): + """Create a new (local) tag for the release, only if the repository is clean.""" + from git import Repo + + repo = Repo('.') + if repo.is_dirty(): + print('Current repository is dirty. Please commit any changes and try again.') + raise invoke.Exit(code=2) + + tag_names = [x.name for x in repo.tags] + if version in tag_names: + print("[generate.make_tag] Delete existing tag {}".format(version)) + repo.delete_tag(version) + + print("[generate.make_tag] Create tag {}".format(version)) + repo.create_tag(version) + + +@invoke.task() +def devpi_upload(ctx, version, user, password=None): + """Creates and uploads a package to devpi for testing.""" + if password: + print("[generate.devpi_upload] devpi login {}".format(user)) + check_call(['devpi', 'login', user, '--password', password]) + + check_call(['devpi', 'use', 'https://devpi.net/{}/dev'.format(user)]) + + env = os.environ.copy() + env['SETUPTOOLS_SCM_PRETEND_VERSION'] = version + check_call(['devpi', 'upload', '--formats', 'sdist,bdist_wheel'], env=env) + print("[generate.devpi_upload] package uploaded") + + +@invoke.task(help={ + 'version': 'version being released', + 'user': 'name of the user on devpi to stage the generated package', + 'password': 'user password on devpi to stage the generated package ' + '(if not given assumed logged in)', +}) +def pre_release(ctx, version, user, password=None): + """Generates new docs, release announcements and uploads a new release to devpi for testing.""" + announce(ctx, version) + regen(ctx) + + msg = 'Preparing release version {}'.format(version) + check_call(['git', 'commit', '-a', '-m', msg]) + + make_tag(ctx, version) + + devpi_upload(ctx, version=version, user=user, password=password) + print() - print('Please review the generated files and commit with:') - print(' git commit -a -m "Generate new release announcement for {}'.format(version)) - - + print('[generate.pre_release] Please push your branch and open a PR.') diff --git a/tasks/requirements.txt b/tasks/requirements.txt new file mode 100644 index 000000000..35afc29c8 --- /dev/null +++ b/tasks/requirements.txt @@ -0,0 +1,3 @@ +invoke +tox +gitpython