Merge branch 'master' into merge-master-into-features
This commit is contained in:
commit
b30a6d22c5
4
AUTHORS
4
AUTHORS
|
@ -44,6 +44,7 @@ David Mohr
|
|||
David Vierra
|
||||
Diego Russo
|
||||
Dmitry Dygalo
|
||||
Duncan Betts
|
||||
Edison Gustavo Muenz
|
||||
Edoardo Batini
|
||||
Eduardo Schettino
|
||||
|
@ -82,6 +83,7 @@ Kevin Cox
|
|||
Lee Kamentsky
|
||||
Lev Maximov
|
||||
Lukas Bednar
|
||||
Luke Murphy
|
||||
Maciek Fijalkowski
|
||||
Maho
|
||||
Marc Schlaich
|
||||
|
@ -102,6 +104,8 @@ Michael Birtwell
|
|||
Michael Droettboom
|
||||
Michael Seifert
|
||||
Mike Lundy
|
||||
Ned Batchelder
|
||||
Neven Mundar
|
||||
Nicolas Delaby
|
||||
Oleg Pidsadnyi
|
||||
Oliver Bestwalter
|
||||
|
|
|
@ -45,16 +45,44 @@ Changes
|
|||
==========
|
||||
|
||||
|
||||
* Add hint to error message hinting possible missing ``__init__.py`` (`#478`_). Thanks `@DuncanBetts`_.
|
||||
|
||||
|
||||
* Provide ``:ref:`` targets for ``recwarn.rst`` so we can use intersphinx referencing.
|
||||
Thanks to `@dupuy`_ for the report and `@lwm`_ for the PR.
|
||||
|
||||
* Using ``item.Function``, ``item.Module``, etc., is now issuing deprecation warnings, prefer
|
||||
``pytest.Function``, ``pytest.Module``, etc., instead (`#2034`_).
|
||||
Thanks `@nmundar`_ for the PR.
|
||||
|
||||
* An error message is now displayed if ``--confcutdir`` is not a valid directory, avoiding
|
||||
subtle bugs (`#2078`_).
|
||||
Thanks `@nicoddemus`_ for the PR.
|
||||
|
||||
* Fix error message using ``approx`` with complex numbers (`#2082`_).
|
||||
Thanks `@adler-j`_ for the report and `@nicoddemus`_ for the PR.
|
||||
|
||||
*
|
||||
|
||||
* Cope gracefully with a .pyc file with no matching .py file (`#2038`_). Thanks
|
||||
`@nedbat`_.
|
||||
|
||||
*
|
||||
|
||||
*
|
||||
|
||||
*
|
||||
.. _@dupuy: https://bitbucket.org/dupuy/
|
||||
.. _@lwm: https://github.com/lwm
|
||||
.. _@adler-j: https://github.com/adler-j
|
||||
.. _@DuncanBetts: https://github.com/DuncanBetts
|
||||
.. _@nedbat: https://github.com/nedbat
|
||||
.. _@nmundar: https://github.com/nmundar
|
||||
|
||||
*
|
||||
|
||||
*
|
||||
.. _#478: https://github.com/pytest-dev/pytest/issues/478
|
||||
.. _#2034: https://github.com/pytest-dev/pytest/issues/2034
|
||||
.. _#2038: https://github.com/pytest-dev/pytest/issues/2038
|
||||
.. _#2078: https://github.com/pytest-dev/pytest/issues/2078
|
||||
.. _#2082: https://github.com/pytest-dev/pytest/issues/2082
|
||||
|
||||
|
||||
3.0.4
|
||||
|
|
|
@ -79,6 +79,16 @@ Pytest could always use more documentation. What exactly is needed?
|
|||
You can also edit documentation files directly in the GitHub web interface,
|
||||
without using a local copy. This can be convenient for small fixes.
|
||||
|
||||
.. note::
|
||||
Build the documentation locally with the following command:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
$ tox -e docs
|
||||
|
||||
The built documentation should be available in the ``doc/en/_build/``.
|
||||
|
||||
Where 'en' refers to the documentation language.
|
||||
|
||||
.. _submitplugin:
|
||||
|
||||
|
|
16
README.rst
16
README.rst
|
@ -24,31 +24,31 @@ An example of a simple test:
|
|||
.. code-block:: python
|
||||
|
||||
# content of test_sample.py
|
||||
def func(x):
|
||||
def inc(x):
|
||||
return x + 1
|
||||
|
||||
def test_answer():
|
||||
assert func(3) == 5
|
||||
assert inc(3) == 5
|
||||
|
||||
|
||||
To execute it::
|
||||
|
||||
$ pytest
|
||||
======= test session starts ========
|
||||
============================= test session starts =============================
|
||||
collected 1 items
|
||||
|
||||
test_sample.py F
|
||||
|
||||
======= FAILURES ========
|
||||
_______ test_answer ________
|
||||
================================== FAILURES ===================================
|
||||
_________________________________ test_answer _________________________________
|
||||
|
||||
def test_answer():
|
||||
> assert func(3) == 5
|
||||
> assert inc(3) == 5
|
||||
E assert 4 == 5
|
||||
E + where 4 = func(3)
|
||||
E + where 4 = inc(3)
|
||||
|
||||
test_sample.py:5: AssertionError
|
||||
======= 1 failed in 0.12 seconds ========
|
||||
========================== 1 failed in 0.04 seconds ===========================
|
||||
|
||||
|
||||
Due to ``pytest``'s detailed assertion introspection, only plain ``assert`` statements are used. See `getting-started <http://docs.pytest.org/en/latest/getting-started.html#our-first-test-run>`_ for more examples.
|
||||
|
|
|
@ -80,7 +80,12 @@ class AssertionRewritingHook(object):
|
|||
tp = desc[2]
|
||||
if tp == imp.PY_COMPILED:
|
||||
if hasattr(imp, "source_from_cache"):
|
||||
fn = imp.source_from_cache(fn)
|
||||
try:
|
||||
fn = imp.source_from_cache(fn)
|
||||
except ValueError:
|
||||
# Python 3 doesn't like orphaned but still-importable
|
||||
# .pyc files.
|
||||
fn = fn[:-1]
|
||||
else:
|
||||
fn = fn[:-1]
|
||||
elif tp != imp.PY_SOURCE:
|
||||
|
|
|
@ -996,6 +996,7 @@ class Config(object):
|
|||
"(are you using python -O?)\n")
|
||||
|
||||
def _preparse(self, args, addopts=True):
|
||||
import pytest
|
||||
self._initini(args)
|
||||
if addopts:
|
||||
args[:] = shlex.split(os.environ.get('PYTEST_ADDOPTS', '')) + args
|
||||
|
@ -1007,7 +1008,10 @@ class Config(object):
|
|||
self.pluginmanager.load_setuptools_entrypoints(entrypoint_name)
|
||||
self.pluginmanager.consider_env()
|
||||
self.known_args_namespace = ns = self._parser.parse_known_args(args, namespace=self.option.copy())
|
||||
if self.known_args_namespace.confcutdir is None and self.inifile:
|
||||
confcutdir = self.known_args_namespace.confcutdir
|
||||
if confcutdir and not os.path.isdir(confcutdir):
|
||||
raise pytest.UsageError('--confcutdir must be a directory, given: {0}'.format(confcutdir))
|
||||
if confcutdir is None and self.inifile:
|
||||
confcutdir = py.path.local(self.inifile).dirname
|
||||
self.known_args_namespace.confcutdir = confcutdir
|
||||
try:
|
||||
|
|
|
@ -190,7 +190,9 @@ class FSHookProxy:
|
|||
|
||||
def compatproperty(name):
|
||||
def fget(self):
|
||||
# deprecated - use pytest.name
|
||||
import warnings
|
||||
warnings.warn("This usage is deprecated, please use pytest.{0} instead".format(name),
|
||||
PendingDeprecationWarning, stacklevel=2)
|
||||
return getattr(pytest, name)
|
||||
|
||||
return property(fget)
|
||||
|
@ -700,10 +702,9 @@ class Session(FSCollector):
|
|||
path = self.config.invocation_dir.join(relpath, abs=True)
|
||||
if not path.check():
|
||||
if self.config.option.pyargs:
|
||||
msg = "file or package not found: "
|
||||
raise pytest.UsageError("file or package not found: " + arg + " (missing __init__.py?)")
|
||||
else:
|
||||
msg = "file not found: "
|
||||
raise pytest.UsageError(msg + arg)
|
||||
raise pytest.UsageError("file not found: " + arg)
|
||||
parts[0] = path
|
||||
return parts
|
||||
|
||||
|
|
|
@ -1414,6 +1414,9 @@ class ApproxNonIterable(object):
|
|||
self.rel = rel
|
||||
|
||||
def __repr__(self):
|
||||
if isinstance(self.expected, complex):
|
||||
return str(self.expected)
|
||||
|
||||
# Infinities aren't compared using tolerances, so don't show a
|
||||
# tolerance.
|
||||
if math.isinf(self.expected):
|
||||
|
|
|
@ -6,6 +6,8 @@ environment:
|
|||
# https://www.appveyor.com/docs/build-configuration#secure-variables
|
||||
|
||||
matrix:
|
||||
# coveralls is not in the default env list
|
||||
- TOXENV: "coveralls"
|
||||
# note: please use "tox --listenvs" to populate the build matrix below
|
||||
- TOXENV: "linting"
|
||||
- TOXENV: "py26"
|
||||
|
@ -29,14 +31,11 @@ install:
|
|||
- echo Installed Pythons
|
||||
- dir c:\Python*
|
||||
|
||||
- if "%TOXENV%" == "pypy" scripts\install-pypy.bat
|
||||
- if "%TOXENV%" == "pypy" call scripts\install-pypy.bat
|
||||
|
||||
- C:\Python35\python -m pip install tox
|
||||
|
||||
build: false # Not a C# project, build stuff at the test step instead.
|
||||
|
||||
test_script:
|
||||
- C:\Python35\python -m tox
|
||||
# coveralls is not in tox's envlist, plus for PRs the secure variable
|
||||
# is not defined so we have to check for it
|
||||
- if defined COVERALLS_REPO_TOKEN C:\Python35\python -m tox -e coveralls
|
||||
- call scripts\call-tox.bat
|
||||
|
|
|
@ -16,10 +16,12 @@ Conventions for Python test discovery
|
|||
* If no arguments are specified then collection starts from :confval:`testpaths`
|
||||
(if configured) or the current directory. Alternatively, command line arguments
|
||||
can be used in any combination of directories, file names or node ids.
|
||||
* recurse into directories, unless they match :confval:`norecursedirs`
|
||||
* ``test_*.py`` or ``*_test.py`` files, imported by their `test package name`_.
|
||||
* ``Test`` prefixed test classes (without an ``__init__`` method)
|
||||
* ``test_`` prefixed test functions or methods are test items
|
||||
* Recurse into directories, unless they match :confval:`norecursedirs`.
|
||||
* In those directories, search for ``test_*.py`` or ``*_test.py`` files, imported by their `test package name`_.
|
||||
* From those files, collect test items:
|
||||
|
||||
* ``test_`` prefixed test functions or methods outside of class
|
||||
* ``test_`` prefixed test functions or methods inside ``Test`` prefixed test classes (without an ``__init__`` method)
|
||||
|
||||
For examples of how to customize your test discovery :doc:`example/pythoncollection`.
|
||||
|
||||
|
|
|
@ -14,33 +14,31 @@ An example of a simple test:
|
|||
.. code-block:: python
|
||||
|
||||
# content of test_sample.py
|
||||
def func(x):
|
||||
def inc(x):
|
||||
return x + 1
|
||||
|
||||
def test_answer():
|
||||
assert func(3) == 5
|
||||
assert inc(3) == 5
|
||||
|
||||
|
||||
To execute it::
|
||||
|
||||
$ pytest
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
============================= test session starts =============================
|
||||
collected 1 items
|
||||
|
||||
test_sample.py F
|
||||
|
||||
======= FAILURES ========
|
||||
_______ test_answer ________
|
||||
================================== FAILURES ===================================
|
||||
_________________________________ test_answer _________________________________
|
||||
|
||||
def test_answer():
|
||||
> assert func(3) == 5
|
||||
> assert inc(3) == 5
|
||||
E assert 4 == 5
|
||||
E + where 4 = func(3)
|
||||
E + where 4 = inc(3)
|
||||
|
||||
test_sample.py:5: AssertionError
|
||||
======= 1 failed in 0.12 seconds ========
|
||||
========================== 1 failed in 0.04 seconds ===========================
|
||||
|
||||
Due to ``pytest``'s detailed assertion introspection, only plain ``assert`` statements are used.
|
||||
See :ref:`Getting Started <getstarted>` for more examples.
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
.. _`asserting warnings`:
|
||||
|
||||
.. _assertwarnings:
|
||||
|
||||
Asserting Warnings
|
||||
=====================================================
|
||||
|
||||
.. _`asserting warnings with the warns function`:
|
||||
|
||||
.. _warns:
|
||||
|
||||
Asserting warnings with the warns function
|
||||
|
@ -46,6 +50,8 @@ Alternatively, you can examine raised warnings in detail using the
|
|||
``DeprecationWarning`` and ``PendingDeprecationWarning`` are treated
|
||||
differently; see :ref:`ensuring_function_triggers`.
|
||||
|
||||
.. _`recording warnings`:
|
||||
|
||||
.. _recwarn:
|
||||
|
||||
Recording warnings
|
||||
|
@ -99,6 +105,8 @@ class of the warning. The ``message`` is the warning itself; calling
|
|||
``DeprecationWarning`` and ``PendingDeprecationWarning`` are treated
|
||||
differently; see :ref:`ensuring_function_triggers`.
|
||||
|
||||
.. _`ensuring a function triggers a deprecation warning`:
|
||||
|
||||
.. _ensuring_function_triggers:
|
||||
|
||||
Ensuring a function triggers a deprecation warning
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
REM skip "coveralls" run in PRs or forks
|
||||
if "%TOXENV%" == "coveralls" (
|
||||
if not defined COVERALLS_REPO_TOKEN (
|
||||
echo skipping coveralls run because COVERALLS_REPO_TOKEN is not defined
|
||||
exit /b 0
|
||||
)
|
||||
)
|
||||
C:\Python35\python -m tox
|
|
@ -28,6 +28,7 @@ class TestApprox:
|
|||
print(approx(inf))
|
||||
print(approx(1.0, rel=nan))
|
||||
print(approx(1.0, rel=inf))
|
||||
print(approx(1.0j, rel=inf))
|
||||
|
||||
def test_operator_overloading(self):
|
||||
assert 1 == approx(1, rel=1e-6, abs=1e-12)
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import glob
|
||||
import os
|
||||
import py_compile
|
||||
import stat
|
||||
import sys
|
||||
import zipfile
|
||||
|
||||
import py
|
||||
import pytest
|
||||
|
||||
|
@ -573,6 +576,31 @@ def test_rewritten():
|
|||
monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", "1")
|
||||
assert testdir.runpytest_subprocess().ret == 0
|
||||
|
||||
def test_orphaned_pyc_file(self, testdir):
|
||||
if sys.version_info < (3, 0) and hasattr(sys, 'pypy_version_info'):
|
||||
pytest.skip("pypy2 doesn't run orphaned pyc files")
|
||||
|
||||
testdir.makepyfile("""
|
||||
import orphan
|
||||
def test_it():
|
||||
assert orphan.value == 17
|
||||
""")
|
||||
testdir.makepyfile(orphan="""
|
||||
value = 17
|
||||
""")
|
||||
py_compile.compile("orphan.py")
|
||||
os.remove("orphan.py")
|
||||
|
||||
# Python 3 puts the .pyc files in a __pycache__ directory, and will
|
||||
# not import from there without source. It will import a .pyc from
|
||||
# the source location though.
|
||||
if not os.path.exists("orphan.pyc"):
|
||||
pycs = glob.glob("__pycache__/orphan.*.pyc")
|
||||
assert len(pycs) == 1
|
||||
os.rename(pycs[0], "orphan.pyc")
|
||||
|
||||
assert testdir.runpytest().ret == 0
|
||||
|
||||
@pytest.mark.skipif('"__pypy__" in sys.modules')
|
||||
def test_pyc_vs_pyo(self, testdir, monkeypatch):
|
||||
testdir.makepyfile("""
|
||||
|
|
|
@ -294,6 +294,15 @@ class TestConfigAPI:
|
|||
assert len(l) == 2
|
||||
assert l == ["456", "123"]
|
||||
|
||||
def test_confcutdir_check_isdir(self, testdir):
|
||||
"""Give an error if --confcutdir is not a valid directory (#2078)"""
|
||||
with pytest.raises(pytest.UsageError):
|
||||
testdir.parseconfig('--confcutdir', testdir.tmpdir.join('file').ensure(file=1))
|
||||
with pytest.raises(pytest.UsageError):
|
||||
testdir.parseconfig('--confcutdir', testdir.tmpdir.join('inexistant'))
|
||||
config = testdir.parseconfig('--confcutdir', testdir.tmpdir.join('dir').ensure(dir=1))
|
||||
assert config.getoption('confcutdir') == str(testdir.tmpdir.join('dir'))
|
||||
|
||||
|
||||
class TestConfigFromdictargs:
|
||||
def test_basic_behavior(self):
|
||||
|
|
|
@ -249,6 +249,18 @@ class TestPython:
|
|||
snode = tnode.find_first_by_tag("skipped")
|
||||
snode.assert_attr(type="pytest.skip", message="hello25", )
|
||||
|
||||
def test_mark_skip_doesnt_capture_output(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
import pytest
|
||||
@pytest.mark.skip(reason="foo")
|
||||
def test_skip():
|
||||
print("bar!")
|
||||
""")
|
||||
result, dom = runandparse(testdir)
|
||||
assert result.ret == 0
|
||||
node_xml = dom.find_first_by_tag("testsuite").toxml()
|
||||
assert "bar!" not in node_xml
|
||||
|
||||
def test_classname_instance(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
class TestClass:
|
||||
|
|
Loading…
Reference in New Issue