Skip to content

Commit 0f11a7a

Browse files
authored
Merge master into features (#5744)
Merge master into features
2 parents 0822a1e + 1049a38 commit 0f11a7a

30 files changed

+218
-107
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
exclude: doc/en/example/py2py3/test_py2.py
22
repos:
3-
- repo: https://github.com/python/black
3+
- repo: https://github.com/psf/black
44
rev: 19.3b0
55
hooks:
66
- id: black

CONTRIBUTING.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ Short version
167167
#. Enable and install `pre-commit <https://pre-commit.com>`_ to ensure style-guides and code checks are followed.
168168
#. Target ``master`` for bugfixes and doc changes.
169169
#. Target ``features`` for new features or functionality changes.
170-
#. Follow **PEP-8** for naming and `black <https://github.com/python/black>`_ for formatting.
170+
#. Follow **PEP-8** for naming and `black <https://github.com/psf/black>`_ for formatting.
171171
#. Tests are run using ``tox``::
172172

173173
tox -e linting,py37

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
:target: https://dev.azure.com/pytest-dev/pytest
2727

2828
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
29-
:target: https://github.com/python/black
29+
:target: https://github.com/psf/black
3030

3131
.. image:: https://www.codetriage.com/pytest-dev/pytest/badges/users.svg
3232
:target: https://www.codetriage.com/pytest-dev/pytest

changelog/5115.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Warnings issued during ``pytest_configure`` are explicitly not treated as errors, even if configured as such, because it otherwise completely breaks pytest.

changelog/5669.doc.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add docstring for ``Testdir.copy_example``.

changelog/5701.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix collection of ``staticmethod`` objects defined with ``functools.partial``.

changelog/5734.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Skip async generator test functions, and update the warning message to refer to ``async def`` functions.

doc/en/capture.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ is that you can use print statements for debugging:
5757
5858
5959
def setup_function(function):
60-
print("setting up %s" % function)
60+
print("setting up", function)
6161
6262
6363
def test_func1():

doc/en/example/assertion/failure_demo.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ def test_tupleerror(self):
177177

178178
def test_reinterpret_fails_with_print_for_the_fun_of_it(self):
179179
items = [1, 2, 3]
180-
print("items is %r" % items)
180+
print("items is {!r}".format(items))
181181
a, b = items.pop()
182182

183183
def test_some_error(self):

doc/en/example/markers.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ specifies via named environments:
384384
envnames = [mark.args[0] for mark in item.iter_markers(name="env")]
385385
if envnames:
386386
if item.config.getoption("-E") not in envnames:
387-
pytest.skip("test requires env in %r" % envnames)
387+
pytest.skip("test requires env in {!r}".format(envnames))
388388
389389
A test file using this local plugin:
390390

@@ -578,7 +578,7 @@ for your particular platform, you could use the following plugin:
578578
supported_platforms = ALL.intersection(mark.name for mark in item.iter_markers())
579579
plat = sys.platform
580580
if supported_platforms and plat not in supported_platforms:
581-
pytest.skip("cannot run on platform %s" % (plat))
581+
pytest.skip("cannot run on platform {}".format(plat))
582582
583583
then tests will be skipped if they were specified for a different platform.
584584
Let's do a little test file to show how this looks like:

doc/en/example/multipython.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,4 @@ def load_and_is_true(self, expression):
6969
@pytest.mark.parametrize("obj", [42, {}, {1: 3}])
7070
def test_basic_objects(python1, python2, obj):
7171
python1.dumps(obj)
72-
python2.load_and_is_true("obj == %s" % obj)
72+
python2.load_and_is_true("obj == {}".format(obj))

doc/en/example/nonpython/conftest.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@ def repr_failure(self, excinfo):
3333
return "\n".join(
3434
[
3535
"usecase execution failed",
36-
" spec failed: %r: %r" % excinfo.value.args[1:3],
36+
" spec failed: {1!r}: {2!r}".format(*excinfo.value.args),
3737
" no further details known at this point.",
3838
]
3939
)
4040

4141
def reportinfo(self):
42-
return self.fspath, 0, "usecase: %s" % self.name
42+
return self.fspath, 0, "usecase: {}".format(self.name)
4343

4444

4545
class YamlException(Exception):

doc/en/example/reportingdemo.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
434434
435435
def test_reinterpret_fails_with_print_for_the_fun_of_it(self):
436436
items = [1, 2, 3]
437-
print("items is %r" % items)
437+
print("items is {!r}".format(items))
438438
> a, b = items.pop()
439439
E TypeError: 'int' object is not iterable
440440

doc/en/example/simple.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ an ``incremental`` marker which is to be used on classes:
478478
if "incremental" in item.keywords:
479479
previousfailed = getattr(item.parent, "_previousfailed", None)
480480
if previousfailed is not None:
481-
pytest.xfail("previous test failed (%s)" % previousfailed.name)
481+
pytest.xfail("previous test failed ({})".format(previousfailed.name))
482482
483483
These two hook implementations work together to abort incremental-marked
484484
tests in a class. Here is a test module example:
@@ -684,7 +684,7 @@ case we just write some information out to a ``failures`` file:
684684
with open("failures", mode) as f:
685685
# let's also access a fixture for the fun of it
686686
if "tmpdir" in item.fixturenames:
687-
extra = " (%s)" % item.funcargs["tmpdir"]
687+
extra = " ({})".format(item.funcargs["tmpdir"])
688688
else:
689689
extra = ""
690690

doc/en/fixture.rst

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -629,7 +629,7 @@ through the special :py:class:`request <FixtureRequest>` object:
629629
def smtp_connection(request):
630630
smtp_connection = smtplib.SMTP(request.param, 587, timeout=5)
631631
yield smtp_connection
632-
print("finalizing %s" % smtp_connection)
632+
print("finalizing {}".format(smtp_connection))
633633
smtp_connection.close()
634634
635635
The main change is the declaration of ``params`` with
@@ -902,25 +902,25 @@ to show the setup/teardown flow:
902902
@pytest.fixture(scope="module", params=["mod1", "mod2"])
903903
def modarg(request):
904904
param = request.param
905-
print(" SETUP modarg %s" % param)
905+
print(" SETUP modarg", param)
906906
yield param
907-
print(" TEARDOWN modarg %s" % param)
907+
print(" TEARDOWN modarg", param)
908908
909909
910910
@pytest.fixture(scope="function", params=[1, 2])
911911
def otherarg(request):
912912
param = request.param
913-
print(" SETUP otherarg %s" % param)
913+
print(" SETUP otherarg", param)
914914
yield param
915-
print(" TEARDOWN otherarg %s" % param)
915+
print(" TEARDOWN otherarg", param)
916916
917917
918918
def test_0(otherarg):
919-
print(" RUN test0 with otherarg %s" % otherarg)
919+
print(" RUN test0 with otherarg", otherarg)
920920
921921
922922
def test_1(modarg):
923-
print(" RUN test1 with modarg %s" % modarg)
923+
print(" RUN test1 with modarg", modarg)
924924
925925
926926
def test_2(otherarg, modarg):

doc/en/getting-started.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ Install ``pytest``
2828
.. code-block:: bash
2929
3030
$ pytest --version
31-
This is pytest version 5.x.y, imported from $PYTHON_PREFIX/lib/python3.6/site-packages/pytest.py
31+
This is pytest version 5.x.y, imported from $PYTHON_PREFIX/lib/python3.x/site-packages/pytest.py
3232
3333
.. _`simpletest`:
3434

doc/en/reference.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ pytest.skip
2727

2828
.. autofunction:: _pytest.outcomes.skip(msg, [allow_module_level=False])
2929

30+
.. _`pytest.importorskip ref`:
31+
3032
pytest.importorskip
3133
~~~~~~~~~~~~~~~~~~~
3234

doc/en/skipping.rst

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -179,16 +179,15 @@ information.
179179
Skipping on a missing import dependency
180180
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
181181

182-
You can use the following helper at module level
183-
or within a test or test setup function:
182+
You can skip tests on a missing import by using :ref:`pytest.importorskip ref`
183+
at module level, within a test, or test setup function.
184184

185185
.. code-block:: python
186186
187187
docutils = pytest.importorskip("docutils")
188188
189-
If ``docutils`` cannot be imported here, this will lead to a
190-
skip outcome of the test. You can also skip based on the
191-
version number of a library:
189+
If ``docutils`` cannot be imported here, this will lead to a skip outcome of
190+
the test. You can also skip based on the version number of a library:
192191

193192
.. code-block:: python
194193

src/_pytest/compat.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,16 @@ def is_generator(func):
4646

4747

4848
def iscoroutinefunction(func):
49-
"""Return True if func is a decorated coroutine function.
49+
"""
50+
Return True if func is a coroutine function (a function defined with async
51+
def syntax, and doesn't contain yield), or a function decorated with
52+
@asyncio.coroutine.
5053
51-
Note: copied and modified from Python 3.5's builtin couroutines.py to avoid import asyncio directly,
52-
which in turns also initializes the "logging" module as side-effect (see issue #8).
54+
Note: copied and modified from Python 3.5's builtin couroutines.py to avoid
55+
importing asyncio directly, which in turns also initializes the "logging"
56+
module as a side-effect (see issue #8).
5357
"""
54-
return getattr(func, "_is_coroutine", False) or (
55-
hasattr(inspect, "iscoroutinefunction") and inspect.iscoroutinefunction(func)
56-
)
58+
return inspect.iscoroutinefunction(func) or getattr(func, "_is_coroutine", False)
5759

5860

5961
def getlocation(function, curdir=None):
@@ -84,7 +86,7 @@ def num_mock_patch_args(function):
8486
)
8587

8688

87-
def getfuncargnames(function, is_method=False, cls=None):
89+
def getfuncargnames(function, *, name: str = "", is_method=False, cls=None):
8890
"""Returns the names of a function's mandatory arguments.
8991
9092
This should return the names of all function arguments that:
@@ -97,11 +99,12 @@ def getfuncargnames(function, is_method=False, cls=None):
9799
be treated as a bound method even though it's not unless, only in
98100
the case of cls, the function is a static method.
99101
102+
The name parameter should be the original name in which the function was collected.
103+
100104
@RonnyPfannschmidt: This function should be refactored when we
101105
revisit fixtures. The fixture mechanism should ask the node for
102106
the fixture names, and not try to obtain directly from the
103107
function object well after collection has occurred.
104-
105108
"""
106109
# The parameters attribute of a Signature object contains an
107110
# ordered mapping of parameter names to Parameter instances. This
@@ -124,11 +127,14 @@ def getfuncargnames(function, is_method=False, cls=None):
124127
)
125128
and p.default is Parameter.empty
126129
)
130+
if not name:
131+
name = function.__name__
132+
127133
# If this function should be treated as a bound method even though
128134
# it's passed as an unbound method or function, remove the first
129135
# parameter name.
130136
if is_method or (
131-
cls and not isinstance(cls.__dict__.get(function.__name__, None), staticmethod)
137+
cls and not isinstance(cls.__dict__.get(name, None), staticmethod)
132138
):
133139
arg_names = arg_names[1:]
134140
# Remove any names that will be replaced with mocks.
@@ -251,7 +257,7 @@ def get_real_method(obj, holder):
251257
try:
252258
is_method = hasattr(obj, "__func__")
253259
obj = get_real_func(obj)
254-
except Exception:
260+
except Exception: # pragma: no cover
255261
return obj
256262
if is_method and hasattr(obj, "__get__") and callable(obj.__get__):
257263
obj = obj.__get__(holder)

src/_pytest/config/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -695,7 +695,9 @@ def add_cleanup(self, func):
695695
def _do_configure(self):
696696
assert not self._configured
697697
self._configured = True
698-
self.hook.pytest_configure.call_historic(kwargs=dict(config=self))
698+
with warnings.catch_warnings():
699+
warnings.simplefilter("default")
700+
self.hook.pytest_configure.call_historic(kwargs=dict(config=self))
699701

700702
def _ensure_unconfigure(self):
701703
if self._configured:

src/_pytest/fixtures.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -818,7 +818,7 @@ def __init__(
818818
where=baseid,
819819
)
820820
self.params = params
821-
self.argnames = getfuncargnames(func, is_method=unittest)
821+
self.argnames = getfuncargnames(func, name=argname, is_method=unittest)
822822
self.unittest = unittest
823823
self.ids = ids
824824
self._finalizers = []
@@ -1142,7 +1142,7 @@ def _get_direct_parametrize_args(self, node):
11421142

11431143
def getfixtureinfo(self, node, func, cls, funcargs=True):
11441144
if funcargs and not getattr(node, "nofuncargs", False):
1145-
argnames = getfuncargnames(func, cls=cls)
1145+
argnames = getfuncargnames(func, name=node.name, cls=cls)
11461146
else:
11471147
argnames = ()
11481148

src/_pytest/outcomes.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -157,14 +157,21 @@ def xfail(reason: str = "") -> "NoReturn":
157157
def importorskip(
158158
modname: str, minversion: Optional[str] = None, reason: Optional[str] = None
159159
) -> Any:
160-
"""Imports and returns the requested module ``modname``, or skip the current test
161-
if the module cannot be imported.
160+
"""Imports and returns the requested module ``modname``, or skip the
161+
current test if the module cannot be imported.
162162
163163
:param str modname: the name of the module to import
164-
:param str minversion: if given, the imported module ``__version__`` attribute must be
165-
at least this minimal version, otherwise the test is still skipped.
166-
:param str reason: if given, this reason is shown as the message when the module
167-
cannot be imported.
164+
:param str minversion: if given, the imported module's ``__version__``
165+
attribute must be at least this minimal version, otherwise the test is
166+
still skipped.
167+
:param str reason: if given, this reason is shown as the message when the
168+
module cannot be imported.
169+
:returns: The imported module. This should be assigned to its canonical
170+
name.
171+
172+
Example::
173+
174+
docutils = pytest.importorskip("docutils")
168175
"""
169176
import warnings
170177

src/_pytest/pytester.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,12 @@ def mkpydir(self, name):
630630
return p
631631

632632
def copy_example(self, name=None):
633+
"""Copy file from project's directory into the testdir.
634+
635+
:param str name: The name of the file to copy.
636+
:return: path to the copied directory (inside ``self.tmpdir``).
637+
638+
"""
633639
import warnings
634640
from _pytest.warning_types import PYTESTER_COPY_EXAMPLE
635641

src/_pytest/python.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from _pytest.compat import getimfunc
2424
from _pytest.compat import getlocation
2525
from _pytest.compat import is_generator
26+
from _pytest.compat import iscoroutinefunction
2627
from _pytest.compat import NOTSET
2728
from _pytest.compat import REGEX_TYPE
2829
from _pytest.compat import safe_getattr
@@ -150,19 +151,25 @@ def pytest_configure(config):
150151

151152
@hookimpl(trylast=True)
152153
def pytest_pyfunc_call(pyfuncitem):
153-
testfunction = pyfuncitem.obj
154-
iscoroutinefunction = getattr(inspect, "iscoroutinefunction", None)
155-
if iscoroutinefunction is not None and iscoroutinefunction(testfunction):
156-
msg = "Coroutine functions are not natively supported and have been skipped.\n"
154+
def async_warn():
155+
msg = "async def functions are not natively supported and have been skipped.\n"
157156
msg += "You need to install a suitable plugin for your async framework, for example:\n"
158157
msg += " - pytest-asyncio\n"
159158
msg += " - pytest-trio\n"
160159
msg += " - pytest-tornasync"
161160
warnings.warn(PytestUnhandledCoroutineWarning(msg.format(pyfuncitem.nodeid)))
162-
skip(msg="coroutine function and no async plugin installed (see warnings)")
161+
skip(msg="async def function and no async plugin installed (see warnings)")
162+
163+
testfunction = pyfuncitem.obj
164+
if iscoroutinefunction(testfunction) or (
165+
sys.version_info >= (3, 6) and inspect.isasyncgenfunction(testfunction)
166+
):
167+
async_warn()
163168
funcargs = pyfuncitem.funcargs
164169
testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames}
165-
testfunction(**testargs)
170+
result = testfunction(**testargs)
171+
if hasattr(result, "__await__") or hasattr(result, "__aiter__"):
172+
async_warn()
166173
return True
167174

168175

0 commit comments

Comments
 (0)