Skip to content

Commit 36eb5b3

Browse files
Merge pull request #2096 from nicoddemus/merge-master-into-features
Merge master into features
2 parents 5ce551e + b30a6d2 commit 36eb5b3

18 files changed

+161
-40
lines changed

AUTHORS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ David Mohr
4444
David Vierra
4545
Diego Russo
4646
Dmitry Dygalo
47+
Duncan Betts
4748
Edison Gustavo Muenz
4849
Edoardo Batini
4950
Eduardo Schettino
@@ -82,6 +83,7 @@ Kevin Cox
8283
Lee Kamentsky
8384
Lev Maximov
8485
Lukas Bednar
86+
Luke Murphy
8587
Maciek Fijalkowski
8688
Maho
8789
Marc Schlaich
@@ -102,6 +104,8 @@ Michael Birtwell
102104
Michael Droettboom
103105
Michael Seifert
104106
Mike Lundy
107+
Ned Batchelder
108+
Neven Mundar
105109
Nicolas Delaby
106110
Oleg Pidsadnyi
107111
Oliver Bestwalter

CHANGELOG.rst

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,45 @@ Changes
4545
==========
4646

4747

48+
* Add hint to error message hinting possible missing ``__init__.py`` (`#478`_). Thanks `@DuncanBetts`_.
4849

49-
*
5050

51-
*
51+
* Provide ``:ref:`` targets for ``recwarn.rst`` so we can use intersphinx referencing.
52+
Thanks to `@dupuy`_ for the report and `@lwm`_ for the PR.
53+
54+
* Using ``item.Function``, ``item.Module``, etc., is now issuing deprecation warnings, prefer
55+
``pytest.Function``, ``pytest.Module``, etc., instead (`#2034`_).
56+
Thanks `@nmundar`_ for the PR.
57+
58+
* An error message is now displayed if ``--confcutdir`` is not a valid directory, avoiding
59+
subtle bugs (`#2078`_).
60+
Thanks `@nicoddemus`_ for the PR.
61+
62+
* Fix error message using ``approx`` with complex numbers (`#2082`_).
63+
Thanks `@adler-j`_ for the report and `@nicoddemus`_ for the PR.
5264

5365
*
5466

67+
* Cope gracefully with a .pyc file with no matching .py file (`#2038`_). Thanks
68+
`@nedbat`_.
69+
5570
*
5671

5772
*
5873

74+
.. _@dupuy: https://bitbucket.org/dupuy/
75+
.. _@lwm: https://github.com/lwm
76+
.. _@adler-j: https://github.com/adler-j
77+
.. _@DuncanBetts: https://github.com/DuncanBetts
78+
.. _@nedbat: https://github.com/nedbat
79+
.. _@nmundar: https://github.com/nmundar
80+
81+
.. _#478: https://github.com/pytest-dev/pytest/issues/478
82+
.. _#2034: https://github.com/pytest-dev/pytest/issues/2034
83+
.. _#2038: https://github.com/pytest-dev/pytest/issues/2038
84+
.. _#2078: https://github.com/pytest-dev/pytest/issues/2078
85+
.. _#2082: https://github.com/pytest-dev/pytest/issues/2082
86+
5987

6088
3.0.4
6189
=====

CONTRIBUTING.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,16 @@ Pytest could always use more documentation. What exactly is needed?
7979
You can also edit documentation files directly in the GitHub web interface,
8080
without using a local copy. This can be convenient for small fixes.
8181

82+
.. note::
83+
Build the documentation locally with the following command:
84+
85+
.. code:: bash
86+
87+
$ tox -e docs
88+
89+
The built documentation should be available in the ``doc/en/_build/``.
90+
91+
Where 'en' refers to the documentation language.
8292

8393
.. _submitplugin:
8494

README.rst

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,31 +24,31 @@ An example of a simple test:
2424
.. code-block:: python
2525
2626
# content of test_sample.py
27-
def func(x):
27+
def inc(x):
2828
return x + 1
2929
3030
def test_answer():
31-
assert func(3) == 5
31+
assert inc(3) == 5
3232
3333
3434
To execute it::
3535

3636
$ pytest
37-
======= test session starts ========
37+
============================= test session starts =============================
3838
collected 1 items
3939

4040
test_sample.py F
4141

42-
======= FAILURES ========
43-
_______ test_answer ________
42+
================================== FAILURES ===================================
43+
_________________________________ test_answer _________________________________
4444

4545
def test_answer():
46-
> assert func(3) == 5
46+
> assert inc(3) == 5
4747
E assert 4 == 5
48-
E + where 4 = func(3)
48+
E + where 4 = inc(3)
4949

5050
test_sample.py:5: AssertionError
51-
======= 1 failed in 0.12 seconds ========
51+
========================== 1 failed in 0.04 seconds ===========================
5252

5353

5454
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.

_pytest/assertion/rewrite.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,12 @@ def find_module(self, name, path=None):
8080
tp = desc[2]
8181
if tp == imp.PY_COMPILED:
8282
if hasattr(imp, "source_from_cache"):
83-
fn = imp.source_from_cache(fn)
83+
try:
84+
fn = imp.source_from_cache(fn)
85+
except ValueError:
86+
# Python 3 doesn't like orphaned but still-importable
87+
# .pyc files.
88+
fn = fn[:-1]
8489
else:
8590
fn = fn[:-1]
8691
elif tp != imp.PY_SOURCE:

_pytest/config.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -996,6 +996,7 @@ def _warn_about_missing_assertion(self, mode):
996996
"(are you using python -O?)\n")
997997

998998
def _preparse(self, args, addopts=True):
999+
import pytest
9991000
self._initini(args)
10001001
if addopts:
10011002
args[:] = shlex.split(os.environ.get('PYTEST_ADDOPTS', '')) + args
@@ -1007,7 +1008,10 @@ def _preparse(self, args, addopts=True):
10071008
self.pluginmanager.load_setuptools_entrypoints(entrypoint_name)
10081009
self.pluginmanager.consider_env()
10091010
self.known_args_namespace = ns = self._parser.parse_known_args(args, namespace=self.option.copy())
1010-
if self.known_args_namespace.confcutdir is None and self.inifile:
1011+
confcutdir = self.known_args_namespace.confcutdir
1012+
if confcutdir and not os.path.isdir(confcutdir):
1013+
raise pytest.UsageError('--confcutdir must be a directory, given: {0}'.format(confcutdir))
1014+
if confcutdir is None and self.inifile:
10111015
confcutdir = py.path.local(self.inifile).dirname
10121016
self.known_args_namespace.confcutdir = confcutdir
10131017
try:

_pytest/main.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,9 @@ def __getattr__(self, name):
190190

191191
def compatproperty(name):
192192
def fget(self):
193-
# deprecated - use pytest.name
193+
import warnings
194+
warnings.warn("This usage is deprecated, please use pytest.{0} instead".format(name),
195+
PendingDeprecationWarning, stacklevel=2)
194196
return getattr(pytest, name)
195197

196198
return property(fget)
@@ -700,10 +702,9 @@ def _parsearg(self, arg):
700702
path = self.config.invocation_dir.join(relpath, abs=True)
701703
if not path.check():
702704
if self.config.option.pyargs:
703-
msg = "file or package not found: "
705+
raise pytest.UsageError("file or package not found: " + arg + " (missing __init__.py?)")
704706
else:
705-
msg = "file not found: "
706-
raise pytest.UsageError(msg + arg)
707+
raise pytest.UsageError("file not found: " + arg)
707708
parts[0] = path
708709
return parts
709710

_pytest/python.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1414,6 +1414,9 @@ def __init__(self, expected, rel=None, abs=None):
14141414
self.rel = rel
14151415

14161416
def __repr__(self):
1417+
if isinstance(self.expected, complex):
1418+
return str(self.expected)
1419+
14171420
# Infinities aren't compared using tolerances, so don't show a
14181421
# tolerance.
14191422
if math.isinf(self.expected):

appveyor.yml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ environment:
66
# https://www.appveyor.com/docs/build-configuration#secure-variables
77

88
matrix:
9+
# coveralls is not in the default env list
10+
- TOXENV: "coveralls"
911
# note: please use "tox --listenvs" to populate the build matrix below
1012
- TOXENV: "linting"
1113
- TOXENV: "py26"
@@ -29,14 +31,11 @@ install:
2931
- echo Installed Pythons
3032
- dir c:\Python*
3133

32-
- if "%TOXENV%" == "pypy" scripts\install-pypy.bat
34+
- if "%TOXENV%" == "pypy" call scripts\install-pypy.bat
3335

3436
- C:\Python35\python -m pip install tox
3537

3638
build: false # Not a C# project, build stuff at the test step instead.
3739

3840
test_script:
39-
- C:\Python35\python -m tox
40-
# coveralls is not in tox's envlist, plus for PRs the secure variable
41-
# is not defined so we have to check for it
42-
- if defined COVERALLS_REPO_TOKEN C:\Python35\python -m tox -e coveralls
41+
- call scripts\call-tox.bat

doc/en/goodpractices.rst

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@ Conventions for Python test discovery
1616
* If no arguments are specified then collection starts from :confval:`testpaths`
1717
(if configured) or the current directory. Alternatively, command line arguments
1818
can be used in any combination of directories, file names or node ids.
19-
* recurse into directories, unless they match :confval:`norecursedirs`
20-
* ``test_*.py`` or ``*_test.py`` files, imported by their `test package name`_.
21-
* ``Test`` prefixed test classes (without an ``__init__`` method)
22-
* ``test_`` prefixed test functions or methods are test items
19+
* Recurse into directories, unless they match :confval:`norecursedirs`.
20+
* In those directories, search for ``test_*.py`` or ``*_test.py`` files, imported by their `test package name`_.
21+
* From those files, collect test items:
22+
23+
* ``test_`` prefixed test functions or methods outside of class
24+
* ``test_`` prefixed test functions or methods inside ``Test`` prefixed test classes (without an ``__init__`` method)
2325

2426
For examples of how to customize your test discovery :doc:`example/pythoncollection`.
2527

doc/en/index.rst

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,33 +14,31 @@ An example of a simple test:
1414
.. code-block:: python
1515
1616
# content of test_sample.py
17-
def func(x):
17+
def inc(x):
1818
return x + 1
1919
2020
def test_answer():
21-
assert func(3) == 5
21+
assert inc(3) == 5
2222
2323
2424
To execute it::
2525

2626
$ pytest
27-
======= test session starts ========
28-
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0
29-
rootdir: $REGENDOC_TMPDIR, inifile:
27+
============================= test session starts =============================
3028
collected 1 items
31-
29+
3230
test_sample.py F
33-
34-
======= FAILURES ========
35-
_______ test_answer ________
36-
31+
32+
================================== FAILURES ===================================
33+
_________________________________ test_answer _________________________________
34+
3735
def test_answer():
38-
> assert func(3) == 5
36+
> assert inc(3) == 5
3937
E assert 4 == 5
40-
E + where 4 = func(3)
41-
38+
E + where 4 = inc(3)
39+
4240
test_sample.py:5: AssertionError
43-
======= 1 failed in 0.12 seconds ========
41+
========================== 1 failed in 0.04 seconds ===========================
4442

4543
Due to ``pytest``'s detailed assertion introspection, only plain ``assert`` statements are used.
4644
See :ref:`Getting Started <getstarted>` for more examples.

doc/en/recwarn.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
.. _`asserting warnings`:
2+
13
.. _assertwarnings:
24

35
Asserting Warnings
46
=====================================================
57

8+
.. _`asserting warnings with the warns function`:
9+
610
.. _warns:
711

812
Asserting warnings with the warns function
@@ -46,6 +50,8 @@ Alternatively, you can examine raised warnings in detail using the
4650
``DeprecationWarning`` and ``PendingDeprecationWarning`` are treated
4751
differently; see :ref:`ensuring_function_triggers`.
4852

53+
.. _`recording warnings`:
54+
4955
.. _recwarn:
5056

5157
Recording warnings
@@ -99,6 +105,8 @@ class of the warning. The ``message`` is the warning itself; calling
99105
``DeprecationWarning`` and ``PendingDeprecationWarning`` are treated
100106
differently; see :ref:`ensuring_function_triggers`.
101107

108+
.. _`ensuring a function triggers a deprecation warning`:
109+
102110
.. _ensuring_function_triggers:
103111

104112
Ensuring a function triggers a deprecation warning

scripts/call-tox.bat

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
REM skip "coveralls" run in PRs or forks
2+
if "%TOXENV%" == "coveralls" (
3+
if not defined COVERALLS_REPO_TOKEN (
4+
echo skipping coveralls run because COVERALLS_REPO_TOKEN is not defined
5+
exit /b 0
6+
)
7+
)
8+
C:\Python35\python -m tox

testing/python/approx.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ def test_repr_string(self):
2828
print(approx(inf))
2929
print(approx(1.0, rel=nan))
3030
print(approx(1.0, rel=inf))
31+
print(approx(1.0j, rel=inf))
3132

3233
def test_operator_overloading(self):
3334
assert 1 == approx(1, rel=1e-6, abs=1e-12)

testing/test_assertrewrite.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
import glob
12
import os
3+
import py_compile
24
import stat
35
import sys
46
import zipfile
7+
58
import py
69
import pytest
710

@@ -573,6 +576,31 @@ def test_no_bytecode():
573576
monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", "1")
574577
assert testdir.runpytest_subprocess().ret == 0
575578

579+
def test_orphaned_pyc_file(self, testdir):
580+
if sys.version_info < (3, 0) and hasattr(sys, 'pypy_version_info'):
581+
pytest.skip("pypy2 doesn't run orphaned pyc files")
582+
583+
testdir.makepyfile("""
584+
import orphan
585+
def test_it():
586+
assert orphan.value == 17
587+
""")
588+
testdir.makepyfile(orphan="""
589+
value = 17
590+
""")
591+
py_compile.compile("orphan.py")
592+
os.remove("orphan.py")
593+
594+
# Python 3 puts the .pyc files in a __pycache__ directory, and will
595+
# not import from there without source. It will import a .pyc from
596+
# the source location though.
597+
if not os.path.exists("orphan.pyc"):
598+
pycs = glob.glob("__pycache__/orphan.*.pyc")
599+
assert len(pycs) == 1
600+
os.rename(pycs[0], "orphan.pyc")
601+
602+
assert testdir.runpytest().ret == 0
603+
576604
@pytest.mark.skipif('"__pypy__" in sys.modules')
577605
def test_pyc_vs_pyo(self, testdir, monkeypatch):
578606
testdir.makepyfile("""

testing/test_config.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,15 @@ def pytest_addoption(parser):
294294
assert len(l) == 2
295295
assert l == ["456", "123"]
296296

297+
def test_confcutdir_check_isdir(self, testdir):
298+
"""Give an error if --confcutdir is not a valid directory (#2078)"""
299+
with pytest.raises(pytest.UsageError):
300+
testdir.parseconfig('--confcutdir', testdir.tmpdir.join('file').ensure(file=1))
301+
with pytest.raises(pytest.UsageError):
302+
testdir.parseconfig('--confcutdir', testdir.tmpdir.join('inexistant'))
303+
config = testdir.parseconfig('--confcutdir', testdir.tmpdir.join('dir').ensure(dir=1))
304+
assert config.getoption('confcutdir') == str(testdir.tmpdir.join('dir'))
305+
297306

298307
class TestConfigFromdictargs:
299308
def test_basic_behavior(self):

0 commit comments

Comments
 (0)