Skip to content

Commit d01f08e

Browse files
authored
Merge branch 'features' into pluggy-master
2 parents 7d59b2e + e1f2254 commit d01f08e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+415
-148
lines changed

.travis.yml

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
sudo: false
22
language: python
33
python:
4-
- '3.5'
4+
- '3.6'
55
# command to install dependencies
6-
install: "pip install -U tox"
6+
install:
7+
- pip install --upgrade --pre tox
78
# # command to run tests
89
env:
910
matrix:
@@ -13,7 +14,7 @@ env:
1314
- TOXENV=linting
1415
- TOXENV=py27
1516
- TOXENV=py34
16-
- TOXENV=py35
17+
- TOXENV=py36
1718
- TOXENV=py27-pexpect
1819
- TOXENV=py27-xdist
1920
- TOXENV=py27-trial
@@ -26,7 +27,6 @@ env:
2627
- TOXENV=py35-pluggymaster
2728
- TOXENV=py27-nobyte
2829
- TOXENV=doctesting
29-
- TOXENV=freeze
3030
- TOXENV=docs
3131

3232
matrix:
@@ -37,8 +37,10 @@ matrix:
3737
python: '3.3'
3838
- env: TOXENV=pypy
3939
python: 'pypy-5.4'
40-
- env: TOXENV=py36
41-
python: '3.6'
40+
- env: TOXENV=py35
41+
python: '3.5'
42+
- env: TOXENV=py35-freeze
43+
python: '3.5'
4244
- env: TOXENV=py37
4345
python: 'nightly'
4446
allow_failures:

AUTHORS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,11 +156,13 @@ Samuele Pedroni
156156
Segev Finer
157157
Simon Gomizelj
158158
Skylar Downes
159+
Srinivas Reddy Thatiparthy
159160
Stefan Farmbauer
160161
Stefan Zimmermann
161162
Stefano Taschini
162163
Steffen Allner
163164
Stephan Obermann
165+
Tarcisio Fischer
164166
Tareq Alayan
165167
Ted Xiao
166168
Thomas Grainger

CHANGELOG.rst

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,36 @@
88
99
.. towncrier release notes start
1010
11+
Pytest 3.2.1 (2017-08-08)
12+
=========================
13+
14+
Bug Fixes
15+
---------
16+
17+
- Fixed small terminal glitch when collecting a single test item. (`#2579
18+
<https://github.com/pytest-dev/pytest/issues/2579>`_)
19+
20+
- Correctly consider ``/`` as the file separator to automatically mark plugin
21+
files for rewrite on Windows. (`#2591 <https://github.com/pytest-
22+
dev/pytest/issues/2591>`_)
23+
24+
- Properly escape test names when setting ``PYTEST_CURRENT_TEST`` environment
25+
variable. (`#2644 <https://github.com/pytest-dev/pytest/issues/2644>`_)
26+
27+
- Fix error on Windows and Python 3.6+ when ``sys.stdout`` has been replaced
28+
with a stream-like object which does not implement the full ``io`` module
29+
buffer protocol. In particular this affects ``pytest-xdist`` users on the
30+
aforementioned platform. (`#2666 <https://github.com/pytest-
31+
dev/pytest/issues/2666>`_)
32+
33+
34+
Improved Documentation
35+
----------------------
36+
37+
- Explicitly document which pytest features work with ``unittest``. (`#2626
38+
<https://github.com/pytest-dev/pytest/issues/2626>`_)
39+
40+
1141
Pytest 3.2.0 (2017-07-30)
1242
=========================
1343

@@ -113,7 +143,7 @@ Bug Fixes
113143
- capture: ensure that EncodedFile.name is a string. (`#2555
114144
<https://github.com/pytest-dev/pytest/issues/2555>`_)
115145

116-
- The options ```--fixtures`` and ```--fixtures-per-test`` will now keep
146+
- The options ``--fixtures`` and ``--fixtures-per-test`` will now keep
117147
indentation within docstrings. (`#2574 <https://github.com/pytest-
118148
dev/pytest/issues/2574>`_)
119149

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ Features
7878

7979
- Python2.6+, Python3.3+, PyPy-2.3, Jython-2.5 (untested);
8080

81-
- Rich plugin architecture, with over 150+ `external plugins <http://docs.pytest.org/en/latest/plugins.html#installing-external-plugins-searching>`_ and thriving community;
81+
- Rich plugin architecture, with over 315+ `external plugins <http://plugincompat.herokuapp.com>`_ and thriving community;
8282

8383

8484
Documentation

_pytest/_code/code.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -861,7 +861,7 @@ def toterminal(self, tw):
861861
if self.args:
862862
linesofar = ""
863863
for name, value in self.args:
864-
ns = "%s = %s" % (name, value)
864+
ns = "%s = %s" % (safe_str(name), safe_str(value))
865865
if len(ns) + len(linesofar) + 2 > tw.fullwidth:
866866
if linesofar:
867867
tw.line(linesofar)

_pytest/capture.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def pytest_addoption(parser):
3535
def pytest_load_initial_conftests(early_config, parser, args):
3636
ns = early_config.known_args_namespace
3737
if ns.capture == "fd":
38-
_py36_windowsconsoleio_workaround()
38+
_py36_windowsconsoleio_workaround(sys.stdout)
3939
_colorama_workaround()
4040
_readline_workaround()
4141
pluginmanager = early_config.pluginmanager
@@ -523,7 +523,7 @@ def _readline_workaround():
523523
pass
524524

525525

526-
def _py36_windowsconsoleio_workaround():
526+
def _py36_windowsconsoleio_workaround(stream):
527527
"""
528528
Python 3.6 implemented unicode console handling for Windows. This works
529529
by reading/writing to the raw console handle using
@@ -540,13 +540,20 @@ def _py36_windowsconsoleio_workaround():
540540
also means a different handle by replicating the logic in
541541
"Py_lifecycle.c:initstdio/create_stdio".
542542
543+
:param stream: in practice ``sys.stdout`` or ``sys.stderr``, but given
544+
here as parameter for unittesting purposes.
545+
543546
See https://github.com/pytest-dev/py/issues/103
544547
"""
545548
if not sys.platform.startswith('win32') or sys.version_info[:2] < (3, 6):
546549
return
547550

548-
buffered = hasattr(sys.stdout.buffer, 'raw')
549-
raw_stdout = sys.stdout.buffer.raw if buffered else sys.stdout.buffer
551+
# bail out if ``stream`` doesn't seem like a proper ``io`` stream (#2666)
552+
if not hasattr(stream, 'buffer'):
553+
return
554+
555+
buffered = hasattr(stream.buffer, 'raw')
556+
raw_stdout = stream.buffer.raw if buffered else stream.buffer
550557

551558
if not isinstance(raw_stdout, io._WindowsConsoleIO):
552559
return

_pytest/compat.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
import types
88
import re
99
import functools
10+
import codecs
1011

1112
import py
1213

1314
import _pytest
15+
from _pytest.outcomes import TEST_OUTCOME
1416

1517

1618
try:
@@ -121,12 +123,24 @@ def isclass(object):
121123

122124

123125
if _PY3:
124-
import codecs
125126
imap = map
126127
izip = zip
127128
STRING_TYPES = bytes, str
128129
UNICODE_TYPES = str,
129130

131+
if PY35:
132+
def _bytes_to_ascii(val):
133+
return val.decode('ascii', 'backslashreplace')
134+
else:
135+
def _bytes_to_ascii(val):
136+
if val:
137+
# source: http://goo.gl/bGsnwC
138+
encoded_bytes, _ = codecs.escape_encode(val)
139+
return encoded_bytes.decode('ascii')
140+
else:
141+
# empty bytes crashes codecs.escape_encode (#1087)
142+
return ''
143+
130144
def _ascii_escaped(val):
131145
"""If val is pure ascii, returns it as a str(). Otherwise, escapes
132146
bytes objects into a sequence of escaped bytes:
@@ -146,13 +160,7 @@ def _ascii_escaped(val):
146160
147161
"""
148162
if isinstance(val, bytes):
149-
if val:
150-
# source: http://goo.gl/bGsnwC
151-
encoded_bytes, _ = codecs.escape_encode(val)
152-
return encoded_bytes.decode('ascii')
153-
else:
154-
# empty bytes crashes codecs.escape_encode (#1087)
155-
return ''
163+
return _bytes_to_ascii(val)
156164
else:
157165
return val.encode('unicode_escape').decode('ascii')
158166
else:
@@ -221,14 +229,16 @@ def getimfunc(func):
221229

222230

223231
def safe_getattr(object, name, default):
224-
""" Like getattr but return default upon any Exception.
232+
""" Like getattr but return default upon any Exception or any OutcomeException.
225233
226234
Attribute access can potentially fail for 'evil' Python objects.
227235
See issue #214.
236+
It catches OutcomeException because of #2490 (issue #580), new outcomes are derived from BaseException
237+
instead of Exception (for more details check #2707)
228238
"""
229239
try:
230240
return getattr(object, name, default)
231-
except Exception:
241+
except TEST_OUTCOME:
232242
return default
233243

234244

_pytest/config.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -862,6 +862,18 @@ def __repr__(self):
862862
FILE_OR_DIR = 'file_or_dir'
863863

864864

865+
def _iter_rewritable_modules(package_files):
866+
for fn in package_files:
867+
is_simple_module = '/' not in fn and fn.endswith('.py')
868+
is_package = fn.count('/') == 1 and fn.endswith('__init__.py')
869+
if is_simple_module:
870+
module_name, _ = os.path.splitext(fn)
871+
yield module_name
872+
elif is_package:
873+
package_name = os.path.dirname(fn)
874+
yield package_name
875+
876+
865877
class Config(object):
866878
""" access to configuration values, pluginmanager and plugin hooks. """
867879

@@ -1022,15 +1034,8 @@ def _mark_plugins_for_rewrite(self, hook):
10221034
for entry in entrypoint.dist._get_metadata(metadata)
10231035
)
10241036

1025-
for fn in package_files:
1026-
is_simple_module = os.sep not in fn and fn.endswith('.py')
1027-
is_package = fn.count(os.sep) == 1 and fn.endswith('__init__.py')
1028-
if is_simple_module:
1029-
module_name, ext = os.path.splitext(fn)
1030-
hook.mark_rewrite(module_name)
1031-
elif is_package:
1032-
package_name = os.path.dirname(fn)
1033-
hook.mark_rewrite(package_name)
1037+
for name in _iter_rewritable_modules(package_files):
1038+
hook.mark_rewrite(name)
10341039

10351040
def _warn_about_missing_assertion(self, mode):
10361041
try:
@@ -1332,7 +1337,7 @@ def determine_setup(inifile, args, warnfunc=None):
13321337
rootdir, inifile, inicfg = getcfg(dirs, warnfunc=warnfunc)
13331338
if rootdir is None:
13341339
rootdir = get_common_ancestor([py.path.local(), ancestor])
1335-
is_fs_root = os.path.splitdrive(str(rootdir))[1] == os.sep
1340+
is_fs_root = os.path.splitdrive(str(rootdir))[1] == '/'
13361341
if is_fs_root:
13371342
rootdir = ancestor
13381343
return rootdir, inifile, inicfg or {}

_pytest/deprecated.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ class RemovedInPytest4Warning(DeprecationWarning):
2626

2727
GETFUNCARGVALUE = "use of getfuncargvalue is deprecated, use getfixturevalue"
2828

29-
RESULT_LOG = '--result-log is deprecated and scheduled for removal in pytest 4.0'
29+
RESULT_LOG = (
30+
'--result-log is deprecated and scheduled for removal in pytest 4.0.\n'
31+
'See https://docs.pytest.org/en/latest/usage.html#creating-resultlog-format-files for more information.'
32+
)
3033

3134
MARK_INFO_ATTRIBUTE = RemovedInPytest4Warning(
3235
"MarkInfo objects are deprecated as they contain the merged marks"

_pytest/fixtures.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,8 @@ def getfuncargvalue(self, argname):
432432
from _pytest import deprecated
433433
warnings.warn(
434434
deprecated.GETFUNCARGVALUE,
435-
DeprecationWarning)
435+
DeprecationWarning,
436+
stacklevel=2)
436437
return self.getfixturevalue(argname)
437438

438439
def _get_active_fixturedef(self, argname):

_pytest/pytester.py

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -379,13 +379,17 @@ def parseoutcomes(self):
379379
return d
380380
raise ValueError("Pytest terminal report not found")
381381

382-
def assert_outcomes(self, passed=0, skipped=0, failed=0):
382+
def assert_outcomes(self, passed=0, skipped=0, failed=0, error=0):
383383
""" assert that the specified outcomes appear with the respective
384384
numbers (0 means it didn't occur) in the text output from a test run."""
385385
d = self.parseoutcomes()
386-
assert passed == d.get("passed", 0)
387-
assert skipped == d.get("skipped", 0)
388-
assert failed == d.get("failed", 0)
386+
obtained = {
387+
'passed': d.get('passed', 0),
388+
'skipped': d.get('skipped', 0),
389+
'failed': d.get('failed', 0),
390+
'error': d.get('error', 0),
391+
}
392+
assert obtained == dict(passed=passed, skipped=skipped, failed=failed, error=error)
389393

390394

391395
class Testdir:
@@ -412,16 +416,8 @@ class Testdir:
412416
def __init__(self, request, tmpdir_factory):
413417
self.request = request
414418
self._mod_collections = WeakKeyDictionary()
415-
# XXX remove duplication with tmpdir plugin
416-
basetmp = tmpdir_factory.ensuretemp("testdir")
417419
name = request.function.__name__
418-
for i in range(100):
419-
try:
420-
tmpdir = basetmp.mkdir(name + str(i))
421-
except py.error.EEXIST:
422-
continue
423-
break
424-
self.tmpdir = tmpdir
420+
self.tmpdir = tmpdir_factory.mktemp(name, numbered=True)
425421
self.plugins = []
426422
self._savesyspath = (list(sys.path), list(sys.meta_path))
427423
self._savemodulekeys = set(sys.modules)

_pytest/python_api.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,8 @@ def raises(expected_exception, *args, **kwargs):
493493
...
494494
>>> assert exc_info.type == ValueError
495495
496-
Or you can use the keyword argument ``match`` to assert that the
496+
497+
Since version ``3.1`` you can use the keyword argument ``match`` to assert that the
497498
exception matches a text or regex::
498499
499500
>>> with raises(ValueError, match='must be 0 or None'):
@@ -502,7 +503,12 @@ def raises(expected_exception, *args, **kwargs):
502503
>>> with raises(ValueError, match=r'must be \d+$'):
503504
... raise ValueError("value must be 42")
504505
505-
Or you can specify a callable by passing a to-be-called lambda::
506+
**Legacy forms**
507+
508+
The forms below are fully supported but are discouraged for new code because the
509+
context manager form is regarded as more readable and less error-prone.
510+
511+
It is possible to specify a callable by passing a to-be-called lambda::
506512
507513
>>> raises(ZeroDivisionError, lambda: 1/0)
508514
<ExceptionInfo ...>
@@ -516,11 +522,14 @@ def raises(expected_exception, *args, **kwargs):
516522
>>> raises(ZeroDivisionError, f, x=0)
517523
<ExceptionInfo ...>
518524
519-
A third possibility is to use a string to be executed::
525+
It is also possible to pass a string to be evaluated at runtime::
520526
521527
>>> raises(ZeroDivisionError, "f(0)")
522528
<ExceptionInfo ...>
523529
530+
The string will be evaluated using the same ``locals()`` and ``globals()``
531+
at the moment of the ``raises`` call.
532+
524533
.. autoclass:: _pytest._code.ExceptionInfo
525534
:members:
526535

0 commit comments

Comments
 (0)