diff --git a/.travis.yml b/.travis.yml index 5694cf355..21fb6c7db 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,11 +20,11 @@ env: - TOXENV=py27-trial - TOXENV=py27-numpy - TOXENV=py27-pluggymaster - - TOXENV=py35-pexpect - - TOXENV=py35-xdist - - TOXENV=py35-trial - - TOXENV=py35-numpy - - TOXENV=py35-pluggymaster + - TOXENV=py36-pexpect + - TOXENV=py36-xdist + - TOXENV=py36-trial + - TOXENV=py36-numpy + - TOXENV=py36-pluggymaster - TOXENV=py27-nobyte - TOXENV=doctesting - TOXENV=docs diff --git a/HOWTORELEASE.rst b/HOWTORELEASE.rst index c85fd9b99..48a3461d4 100644 --- a/HOWTORELEASE.rst +++ b/HOWTORELEASE.rst @@ -1,5 +1,9 @@ -How to release pytest --------------------------------------------- +Release Procedure +----------------- + +Our current policy for releasing is to aim for a bugfix every few weeks and a minor release every 2-3 months. The idea +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. .. important:: @@ -21,7 +25,7 @@ How to release pytest #. Generate docs, changelog, announcements and upload a package to your ``devpi`` staging server:: - invoke generate.pre_release --password + invoke generate.pre-release --password If ``--password`` is not given, it is assumed the user is already logged in ``devpi``. If you don't have an account, please ask for one. @@ -49,7 +53,7 @@ How to release pytest #. Publish to PyPI:: - invoke generate.publish_release + invoke generate.publish-release where PYPI_NAME is the name of pypi.python.org as configured in your ``~/.pypirc`` file `for devpi `_. diff --git a/_pytest/_argcomplete.py b/_pytest/_argcomplete.py index 1ba1ecc1e..965ec7951 100644 --- a/_pytest/_argcomplete.py +++ b/_pytest/_argcomplete.py @@ -78,7 +78,8 @@ class FastFilesCompleter: completion = [] globbed = [] if '*' not in prefix and '?' not in prefix: - if prefix[-1] == os.path.sep: # we are on unix, otherwise no bash + # we are on unix, otherwise no bash + if not prefix or prefix[-1] == os.path.sep: globbed.extend(glob(prefix + '.*')) prefix += '*' globbed.extend(glob(prefix)) @@ -98,7 +99,7 @@ if os.environ.get('_ARGCOMPLETE'): filescompleter = FastFilesCompleter() def try_argcomplete(parser): - argcomplete.autocomplete(parser) + argcomplete.autocomplete(parser, always_complete_options=False) else: def try_argcomplete(parser): pass diff --git a/_pytest/mark.py b/_pytest/mark.py index f76d7da3b..31e0151b6 100644 --- a/_pytest/mark.py +++ b/_pytest/mark.py @@ -326,7 +326,7 @@ class MarkDecorator: return self.name # for backward-compat (2.4.1 had this attr) def __eq__(self, other): - return self.mark == other.mark + return self.mark == other.mark if isinstance(other, MarkDecorator) else False def __repr__(self): return "" % (self.mark,) diff --git a/appveyor.yml b/appveyor.yml index ec2611daf..01a723d5f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -22,11 +22,11 @@ environment: - TOXENV: "py27-trial" - TOXENV: "py27-numpy" - TOXENV: "py27-pluggymaster" - - TOXENV: "py35-pexpect" - - TOXENV: "py35-xdist" - - TOXENV: "py35-trial" - - TOXENV: "py35-numpy" - - TOXENV: "py35-pluggymaster" + - TOXENV: "py36-pexpect" + - TOXENV: "py36-xdist" + - TOXENV: "py36-trial" + - TOXENV: "py36-numpy" + - TOXENV: "py36-pluggymaster" - TOXENV: "py27-nobyte" - TOXENV: "doctesting" - TOXENV: "py35-freeze" diff --git a/changelog/1548.doc b/changelog/1548.doc new file mode 100644 index 000000000..84ad8f10c --- /dev/null +++ b/changelog/1548.doc @@ -0,0 +1 @@ +Add note in ``parametrize.rst`` about calling ``metafunc.parametrize`` multiple times. \ No newline at end of file diff --git a/changelog/2722.trivial b/changelog/2722.trivial new file mode 100644 index 000000000..2c0ccd7b1 --- /dev/null +++ b/changelog/2722.trivial @@ -0,0 +1 @@ +Set ``xfail_strict=True`` in pytest's own test suite to catch expected failures as soon as they start to pass. diff --git a/changelog/2748.bugfix b/changelog/2748.bugfix new file mode 100644 index 000000000..b5b6f5839 --- /dev/null +++ b/changelog/2748.bugfix @@ -0,0 +1 @@ +Fix crash in tab completion when no prefix is given. diff --git a/changelog/2758.bugfix b/changelog/2758.bugfix new file mode 100644 index 000000000..12e4046b8 --- /dev/null +++ b/changelog/2758.bugfix @@ -0,0 +1 @@ +The equality checking function (``__eq__``) of ``MarkDecorator`` returns ``False`` if one object is not an instance of ``MarkDecorator``. \ No newline at end of file diff --git a/changelog/2765.trivial b/changelog/2765.trivial new file mode 100644 index 000000000..01110b852 --- /dev/null +++ b/changelog/2765.trivial @@ -0,0 +1 @@ +Fix typo in example of passing a callable to markers (in example/markers.rst) \ No newline at end of file diff --git a/doc/en/contents.rst b/doc/en/contents.rst index 028414eb6..6b9eed010 100644 --- a/doc/en/contents.rst +++ b/doc/en/contents.rst @@ -41,6 +41,7 @@ Full pytest documentation historical-notes license contributing + development_guide talks projects faq diff --git a/doc/en/development_guide.rst b/doc/en/development_guide.rst new file mode 100644 index 000000000..465e97de0 --- /dev/null +++ b/doc/en/development_guide.rst @@ -0,0 +1,108 @@ +================= +Development Guide +================= + +Some general guidelines regarding development in pytest for core maintainers and general contributors. Nothing here +is set in stone and can't be changed, feel free to suggest improvements or changes in the workflow. + + +Code Style +---------- + +* `PEP-8 `_ +* `flake8 `_ for quality checks +* `invoke `_ to automate development tasks + + +Branches +-------- + +We have two long term branches: + +* ``master``: contains the code for the next bugfix release. +* ``features``: contains the code with new features for the next minor release. + +The official repository usually does not contain topic branches, developers and contributors should create topic +branches in their own forks. + +Exceptions can be made for cases where more than one contributor is working on the same +topic or where it makes sense to use some automatic capability of the main repository, such as automatic docs from +`readthedocs `_ for a branch dealing with documentation refactoring. + +Issues +------ + +Any question, feature, bug or proposal is welcome as an issue. Users are encouraged to use them whenever they need. + +GitHub issues should use labels to categorize them. Labels should be created sporadically, to fill a niche; we should +avoid creating labels just for the sake of creating them. + +Here is a list of labels and a brief description mentioning their intent. + + +**Type** + +* ``type: backward compatibility``: issue that will cause problems with old pytest versions. +* ``type: bug``: problem that needs to be addressed. +* ``type: deprecation``: feature that will be deprecated in the future. +* ``type: docs``: documentation missing or needing clarification. +* ``type: enhancement``: new feature or API change, should be merged into ``features``. +* ``type: feature-branch``: new feature or API change, should be merged into ``features``. +* ``type: infrastructure``: improvement to development/releases/CI structure. +* ``type: performance``: performance or memory problem/improvement. +* ``type: proposal``: proposal for a new feature, often to gather opinions or design the API around the new feature. +* ``type: question``: question regarding usage, installation, internals or how to test something. +* ``type: refactoring``: internal improvements to the code. +* ``type: regression``: indicates a problem that was introduced in a release which was working previously. + +**Status** + +* ``status: critical``: grave problem or usability issue that affects lots of users. +* ``status: easy``: easy issue that is friendly to new contributors. +* ``status: help wanted``: core developers need help from experts on this topic. +* ``status: needs information``: reporter needs to provide more information; can be closed after 2 or more weeks of inactivity. + +**Topic** + +* ``topic: collection`` +* ``topic: fixtures`` +* ``topic: parametrize`` +* ``topic: reporting`` +* ``topic: selection`` +* ``topic: tracebacks`` + +**Plugin (internal or external)** + +* ``plugin: cache`` +* ``plugin: capture`` +* ``plugin: doctests`` +* ``plugin: junitxml`` +* ``plugin: monkeypatch`` +* ``plugin: nose`` +* ``plugin: pastebin`` +* ``plugin: pytester`` +* ``plugin: tmpdir`` +* ``plugin: unittest`` +* ``plugin: warnings`` +* ``plugin: xdist`` + + +**OS** + +Issues specific to a single operating system. Do not use as a means to indicate where an issue originated from, only +for problems that happen **only** in that system. + +* ``os: linux`` +* ``os: mac`` +* ``os: windows`` + +**Temporary** + +Used to classify issues for limited time, to help find issues related in events for example. +They should be removed after they are no longer relevant. + +* ``temporary: EP2017 sprint``: +* ``temporary: sprint-candidate``: + + +.. include:: ../../HOWTORELEASE.rst diff --git a/doc/en/example/markers.rst b/doc/en/example/markers.rst index 0dbc67310..e3082f279 100644 --- a/doc/en/example/markers.rst +++ b/doc/en/example/markers.rst @@ -435,7 +435,7 @@ The output is as follows:: . 1 passed in 0.12 seconds -We can see that the custom marker has its argument set extended with the function ``hello_world``. This is the key different between creating a custom marker as a callable, which invokes ``__call__`` behind the scenes, and using ``with_args``. +We can see that the custom marker has its argument set extended with the function ``hello_world``. This is the key difference between creating a custom marker as a callable, which invokes ``__call__`` behind the scenes, and using ``with_args``. Reading markers which were set from multiple places diff --git a/doc/en/parametrize.rst b/doc/en/parametrize.rst index d1d47c229..6215cf133 100644 --- a/doc/en/parametrize.rst +++ b/doc/en/parametrize.rst @@ -198,6 +198,10 @@ list:: SKIP [1] test_strings.py:2: got empty parameter set ['stringinput'], function test_valid_string at $REGENDOC_TMPDIR/test_strings.py:1 1 skipped in 0.12 seconds + +Note that when calling ``metafunc.parametrize`` multiple times with different parameter sets, all parameter names across +those sets cannot be duplicated, otherwise an error will be raised. + For further examples, you might want to look at :ref:`more parametrization examples `. diff --git a/doc/en/skipping.rst b/doc/en/skipping.rst index 8690035a3..630f73422 100644 --- a/doc/en/skipping.rst +++ b/doc/en/skipping.rst @@ -54,7 +54,7 @@ by calling the ``pytest.skip(reason)`` function: if not valid_config(): pytest.skip("unsupported configuration") -The imperative method is useful when it is not possible to evaluate the skip condition +The imperative method is useful when it is not possible to evaluate the skip condition during import time. ``skipif`` @@ -73,7 +73,7 @@ when run on a Python3.3 interpreter:: ... If the condition evaluates to ``True`` during collection, the test function will be skipped, -with the specified reason appearing in the summary when using ``-rs``. +with the specified reason appearing in the summary when using ``-rs``. You can share ``skipif`` markers between modules. Consider this test module:: @@ -118,6 +118,12 @@ You can use the ``skipif`` marker (as any other marker) on classes:: If the condition is ``True``, this marker will produce a skip result for each of the test methods of that class. +.. warning:: + + The use of ``skipif`` on classes that use inheritance is strongly + discouraged. `A Known bug `_ + in pytest's markers may cause unexpected behavior in super classes. + If you want to skip all test functions of a module, you may use the ``pytestmark`` name on the global level: @@ -305,12 +311,12 @@ Running it with the report-on-xfail option gives this output:: platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR/example, inifile: collected 7 items - + xfail_demo.py xxxxxxx ======= short test summary info ======== XFAIL xfail_demo.py::test_hello XFAIL xfail_demo.py::test_hello2 - reason: [NOTRUN] + reason: [NOTRUN] XFAIL xfail_demo.py::test_hello3 condition: hasattr(os, 'sep') XFAIL xfail_demo.py::test_hello4 @@ -320,7 +326,7 @@ Running it with the report-on-xfail option gives this output:: XFAIL xfail_demo.py::test_hello6 reason: reason XFAIL xfail_demo.py::test_hello7 - + ======= 7 xfailed in 0.12 seconds ======== .. _`skip/xfail with parametrize`: @@ -346,5 +352,3 @@ test instances when using parametrize: ]) def test_increment(n, expected): assert n + 1 == expected - - diff --git a/testing/code/test_source.py b/testing/code/test_source.py index aaa2b8c5f..1d315aa9b 100644 --- a/testing/code/test_source.py +++ b/testing/code/test_source.py @@ -391,7 +391,6 @@ def test_deindent(): assert lines == ['', 'def f():', ' def g():', ' pass', ' '] -@pytest.mark.xfail("sys.version_info[:3] < (2,7,0)") def test_source_of_class_at_eof_without_newline(tmpdir): # this test fails because the implicit inspect.getsource(A) below # does not return the "x = 1" last line. diff --git a/testing/python/collect.py b/testing/python/collect.py index 6b9c6db4e..ccd5c11e7 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -841,7 +841,7 @@ class TestConftestCustomization(object): def pytest_pycollect_makeitem(): outcome = yield if outcome.excinfo is None: - result = outcome.result + result = outcome.get_result() if result: for func in result: func._some123 = "world" diff --git a/testing/test_argcomplete.py b/testing/test_argcomplete.py index b07f6e83b..c92612577 100644 --- a/testing/test_argcomplete.py +++ b/testing/test_argcomplete.py @@ -82,7 +82,7 @@ class TestArgComplete(object): from _pytest._argcomplete import FastFilesCompleter ffc = FastFilesCompleter() fc = FilesCompleter() - for x in '/ /d /data qqq'.split(): + for x in ['/', '/d', '/data', 'qqq', '']: assert equal_with_bash(x, ffc, fc, out=py.std.sys.stdout) @pytest.mark.skipif("sys.platform in ('win32', 'darwin')") diff --git a/testing/test_mark.py b/testing/test_mark.py index 4c495fde0..ae070f3a0 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -812,3 +812,15 @@ def test_legacy_transfer(): assert fake_method.fun # pristine marks dont transfer assert fake_method.pytestmark == [pytest.mark.fun.mark] + + +class TestMarkDecorator(object): + + @pytest.mark.parametrize('lhs, rhs, expected', [ + (pytest.mark.foo(), pytest.mark.foo(), True), + (pytest.mark.foo(), pytest.mark.bar(), False), + (pytest.mark.foo(), 'bar', False), + ('foo', pytest.mark.bar(), False) + ]) + def test__eq__(self, lhs, rhs, expected): + assert (lhs == rhs) == expected diff --git a/tox.ini b/tox.ini index 6df6c53a2..496005a04 100644 --- a/tox.ini +++ b/tox.ini @@ -12,7 +12,7 @@ envlist = py36 py37 pypy - {py27,py35}-{pexpect,xdist,trial,numpy,pluggymaster} + {py27,py36}-{pexpect,xdist,trial,numpy,pluggymaster} py27-nobyte doctesting py35-freeze @@ -37,7 +37,6 @@ deps = [testenv:py27-subprocess] changedir = . -basepython = python2.7 deps = pytest-xdist>=1.13 mock @@ -68,7 +67,7 @@ deps = commands = pytest -n1 -ra {posargs:testing} -[testenv:py35-xdist] +[testenv:py36-xdist] deps = {[testenv:py27-xdist]deps} commands = pytest -n3 -ra {posargs:testing} @@ -80,7 +79,7 @@ deps = pexpect commands = pytest -ra test_pdb.py test_terminal.py test_unittest.py -[testenv:py35-pexpect] +[testenv:py36-pexpect] changedir = testing platform = linux|darwin deps = {[testenv:py27-pexpect]deps} @@ -102,7 +101,7 @@ deps = twisted commands = pytest -ra {posargs:testing/test_unittest.py} -[testenv:py35-trial] +[testenv:py36-trial] deps = {[testenv:py27-trial]deps} commands = pytest -ra {posargs:testing/test_unittest.py} @@ -112,7 +111,7 @@ deps=numpy commands= pytest -ra {posargs:testing/python/approx.py} -[testenv:py35-numpy] +[testenv:py36-numpy] deps=numpy commands= pytest -ra {posargs:testing/python/approx.py} @@ -198,7 +197,6 @@ commands = [testenv:coveralls] passenv = TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH COVERALLS_REPO_TOKEN usedevelop = True -basepython = python3.5 changedir = . deps = {[testenv]deps} @@ -218,6 +216,7 @@ python_files = test_*.py *_test.py testing/*/*.py python_classes = Test Acceptance python_functions = test norecursedirs = .tox ja .hg cx_freeze_source +xfail_strict=true filterwarnings = error # produced by path.local