Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f24cc2feb | ||
|
|
10fcac7f90 | ||
|
|
8942a05cfe | ||
|
|
66c1a120ba | ||
|
|
8cdf9d43a9 | ||
|
|
2ddc330b62 | ||
|
|
1c0ab3c2a3 | ||
|
|
7ff91d8127 | ||
|
|
64c71daa21 | ||
|
|
c7f9fda42d | ||
|
|
42fb1f7ede | ||
|
|
c00a43a17d | ||
|
|
1dc612f7d4 | ||
|
|
e5bd7fb053 | ||
|
|
256a9e0027 | ||
|
|
d2d7b97a70 | ||
|
|
23f6adc760 | ||
|
|
172b82875a | ||
|
|
277857b026 | ||
|
|
d3ab56f531 | ||
|
|
209d99102d | ||
|
|
6d31684da5 | ||
|
|
4ee280ae1f | ||
|
|
8b8cba369c | ||
|
|
83182b82ea | ||
|
|
31d5cedc6d | ||
|
|
91b3ff1bb7 | ||
|
|
b66dc80008 | ||
|
|
69ad2026f6 | ||
|
|
2d24c062b6 | ||
|
|
d940d0b657 | ||
|
|
f9f092a5e6 | ||
|
|
faea273c93 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -25,6 +25,7 @@ src/_pytest/_version.py
|
||||
|
||||
doc/*/_build
|
||||
doc/*/.doctrees
|
||||
doc/*/_changelog_towncrier_draft.rst
|
||||
build/
|
||||
dist/
|
||||
*.egg-info
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
exclude: doc/en/example/py2py3/test_py2.py
|
||||
repos:
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 19.10b0
|
||||
@@ -48,7 +47,7 @@ repos:
|
||||
- id: rst
|
||||
name: rst
|
||||
entry: rst-lint --encoding utf-8
|
||||
files: ^(CHANGELOG.rst|HOWTORELEASE.rst|README.rst|TIDELIFT.rst|changelog/.*)$
|
||||
files: ^(HOWTORELEASE.rst|README.rst|TIDELIFT.rst)$
|
||||
language: python
|
||||
additional_dependencies: [pygments, restructuredtext_lint]
|
||||
- id: changelogs-rst
|
||||
|
||||
1
AUTHORS
1
AUTHORS
@@ -61,6 +61,7 @@ Christian Theunert
|
||||
Christian Tismer
|
||||
Christopher Gilling
|
||||
Christopher Dignam
|
||||
Claudio Madotto
|
||||
CrazyMerlyn
|
||||
Cyrus Maden
|
||||
Damian Skrzypczak
|
||||
|
||||
7553
CHANGELOG.rst
7553
CHANGELOG.rst
File diff suppressed because it is too large
Load Diff
@@ -137,7 +137,7 @@ Save time, reduce risk, and improve code health, while paying the maintainers of
|
||||
Security
|
||||
^^^^^^^^
|
||||
|
||||
pytest has never been associated with a security vunerability, but in any case, to report a
|
||||
pytest has never been associated with a security vulnerability, but in any case, to report a
|
||||
security vulnerability please use the `Tidelift security contact <https://tidelift.com/security>`_.
|
||||
Tidelift will coordinate the fix and disclosure.
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ changelog using that instead.
|
||||
If you are not sure what issue type to use, don't hesitate to ask in your PR.
|
||||
|
||||
``towncrier`` preserves multiple paragraphs and formatting (code blocks, lists, and so on), but for entries
|
||||
other than ``features`` it is usually better to stick to a single paragraph to keep it concise. You can install
|
||||
``towncrier`` and then run ``towncrier --draft``
|
||||
if you want to get a preview of how your change will look in the final release notes.
|
||||
other than ``features`` it is usually better to stick to a single paragraph to keep it concise.
|
||||
|
||||
You can also run ``tox -e docs`` to build the documentation
|
||||
with the draft changelog (``doc/en/_build/changelog.html``) if you want to get a preview of how your change will look in the final release notes.
|
||||
|
||||
@@ -6,6 +6,7 @@ Release announcements
|
||||
:maxdepth: 2
|
||||
|
||||
|
||||
release-5.3.2
|
||||
release-5.3.1
|
||||
release-5.3.0
|
||||
release-5.2.4
|
||||
|
||||
26
doc/en/announce/release-5.3.2.rst
Normal file
26
doc/en/announce/release-5.3.2.rst
Normal file
@@ -0,0 +1,26 @@
|
||||
pytest-5.3.2
|
||||
=======================================
|
||||
|
||||
pytest 5.3.2 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Claudio Madotto
|
||||
* Daniel Hahler
|
||||
* Jared Vasquez
|
||||
* Michael Rose
|
||||
* Ran Benita
|
||||
* Ronny Pfannschmidt
|
||||
* Zac Hatfield-Dodds
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
7598
doc/en/changelog.rst
7598
doc/en/changelog.rst
File diff suppressed because it is too large
Load Diff
@@ -20,6 +20,10 @@ import sys
|
||||
|
||||
from _pytest import __version__ as version
|
||||
|
||||
if False: # TYPE_CHECKING
|
||||
import sphinx.application
|
||||
|
||||
|
||||
release = ".".join(version.split(".")[:2])
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
@@ -342,7 +346,30 @@ texinfo_documents = [
|
||||
intersphinx_mapping = {"python": ("https://docs.python.org/3", None)}
|
||||
|
||||
|
||||
def setup(app):
|
||||
def configure_logging(app: "sphinx.application.Sphinx") -> None:
|
||||
"""Configure Sphinx's WarningHandler to handle (expected) missing include."""
|
||||
import sphinx.util.logging
|
||||
import logging
|
||||
|
||||
class WarnLogFilter(logging.Filter):
|
||||
def filter(self, record: logging.LogRecord) -> bool:
|
||||
"""Ignore warnings about missing include with "only" directive.
|
||||
|
||||
Ref: https://github.com/sphinx-doc/sphinx/issues/2150."""
|
||||
if (
|
||||
record.msg.startswith('Problems with "include" directive path:')
|
||||
and "_changelog_towncrier_draft.rst" in record.msg
|
||||
):
|
||||
return False
|
||||
return True
|
||||
|
||||
logger = logging.getLogger(sphinx.util.logging.NAMESPACE)
|
||||
warn_handler = [x for x in logger.handlers if x.level == logging.WARNING]
|
||||
assert len(warn_handler) == 1, warn_handler
|
||||
warn_handler[0].filters.insert(0, WarnLogFilter())
|
||||
|
||||
|
||||
def setup(app: "sphinx.application.Sphinx") -> None:
|
||||
# from sphinx.ext.autodoc import cut_lines
|
||||
# app.connect('autodoc-process-docstring', cut_lines(4, what=['module']))
|
||||
app.add_object_type(
|
||||
@@ -351,3 +378,4 @@ def setup(app):
|
||||
objname="configuration value",
|
||||
indextemplate="pair: %s; configuration value",
|
||||
)
|
||||
configure_logging(app)
|
||||
|
||||
@@ -475,10 +475,10 @@ Running it results in some skips if we don't have all the python interpreters in
|
||||
.. code-block:: pytest
|
||||
|
||||
. $ pytest -rs -q multipython.py
|
||||
ssssssssssssssssssssssss... [100%]
|
||||
ssssssssssss...ssssssssssss [100%]
|
||||
========================= short test summary info ==========================
|
||||
SKIPPED [12] $REGENDOC_TMPDIR/CWD/multipython.py:29: 'python3.5' not found
|
||||
SKIPPED [12] $REGENDOC_TMPDIR/CWD/multipython.py:29: 'python3.6' not found
|
||||
SKIPPED [12] $REGENDOC_TMPDIR/CWD/multipython.py:29: 'python3.7' not found
|
||||
3 passed, 24 skipped in 0.12s
|
||||
|
||||
Indirect parametrization of optional implementations/imports
|
||||
@@ -604,13 +604,13 @@ Then run ``pytest`` with verbose mode and with only the ``basic`` marker:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collecting ... collected 18 items / 15 deselected / 3 selected
|
||||
collecting ... collected 17 items / 14 deselected / 3 selected
|
||||
|
||||
test_pytest_param_example.py::test_eval[1+7-8] PASSED [ 33%]
|
||||
test_pytest_param_example.py::test_eval[basic_2+4] PASSED [ 66%]
|
||||
test_pytest_param_example.py::test_eval[basic_6*9] XFAIL [100%]
|
||||
|
||||
=============== 2 passed, 15 deselected, 1 xfailed in 0.12s ================
|
||||
=============== 2 passed, 14 deselected, 1 xfailed in 0.12s ================
|
||||
|
||||
As the result:
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
py3 = sys.version_info[0] >= 3
|
||||
|
||||
|
||||
class DummyCollector(pytest.collect.File):
|
||||
def collect(self):
|
||||
return []
|
||||
|
||||
|
||||
def pytest_pycollect_makemodule(path, parent):
|
||||
bn = path.basename
|
||||
if "py3" in bn and not py3 or ("py2" in bn and py3):
|
||||
return DummyCollector(path, parent=parent)
|
||||
@@ -1,5 +0,0 @@
|
||||
def test_exception_syntax():
|
||||
try:
|
||||
0 / 0
|
||||
except ZeroDivisionError, e:
|
||||
assert e
|
||||
@@ -1,5 +0,0 @@
|
||||
def test_exception_syntax():
|
||||
try:
|
||||
0 / 0
|
||||
except ZeroDivisionError as e:
|
||||
assert e
|
||||
@@ -436,7 +436,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
|
||||
items = [1, 2, 3]
|
||||
print("items is {!r}".format(items))
|
||||
> a, b = items.pop()
|
||||
E TypeError: cannot unpack non-iterable int object
|
||||
E TypeError: 'int' object is not iterable
|
||||
|
||||
failure_demo.py:181: TypeError
|
||||
--------------------------- Captured stdout call ---------------------------
|
||||
@@ -516,7 +516,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
|
||||
def test_z2_type_error(self):
|
||||
items = 3
|
||||
> a, b = items
|
||||
E TypeError: cannot unpack non-iterable int object
|
||||
E TypeError: 'int' object is not iterable
|
||||
|
||||
failure_demo.py:222: TypeError
|
||||
______________________ TestMoreErrors.test_startswith ______________________
|
||||
|
||||
@@ -442,8 +442,8 @@ Now we can profile which test functions execute the slowest:
|
||||
|
||||
========================= slowest 3 test durations =========================
|
||||
0.30s call test_some_are_slow.py::test_funcslow2
|
||||
0.21s call test_some_are_slow.py::test_funcslow1
|
||||
0.11s call test_some_are_slow.py::test_funcfast
|
||||
0.20s call test_some_are_slow.py::test_funcslow1
|
||||
0.10s call test_some_are_slow.py::test_funcfast
|
||||
============================ 3 passed in 0.12s =============================
|
||||
|
||||
incremental testing - test steps
|
||||
|
||||
@@ -28,7 +28,7 @@ Install ``pytest``
|
||||
.. code-block:: bash
|
||||
|
||||
$ pytest --version
|
||||
This is pytest version 5.x.y, imported from $PYTHON_PREFIX/lib/python3.7/site-packages/pytest.py
|
||||
This is pytest version 5.x.y, imported from $PYTHON_PREFIX/lib/python3.6/site-packages/pytest/__init__.py
|
||||
|
||||
.. _`simpletest`:
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ Save time, reduce risk, and improve code health, while paying the maintainers of
|
||||
Security
|
||||
^^^^^^^^
|
||||
|
||||
pytest has never been associated with a security vunerability, but in any case, to report a
|
||||
pytest has never been associated with a security vulnerability, but in any case, to report a
|
||||
security vulnerability please use the `Tidelift security contact <https://tidelift.com/security>`_.
|
||||
Tidelift will coordinate the fix and disclosure.
|
||||
|
||||
|
||||
@@ -360,7 +360,7 @@ capfd
|
||||
|
||||
def test_system_echo(capfd):
|
||||
os.system('echo "hello"')
|
||||
captured = capsys.readouterr()
|
||||
captured = capfd.readouterr()
|
||||
assert captured.out == "hello\n"
|
||||
|
||||
|
||||
@@ -463,6 +463,8 @@ monkeypatch
|
||||
.. autoclass:: _pytest.monkeypatch.MonkeyPatch
|
||||
:members:
|
||||
|
||||
.. _testdir:
|
||||
|
||||
testdir
|
||||
~~~~~~~
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ build-backend = "setuptools.build_meta"
|
||||
[tool.towncrier]
|
||||
package = "pytest"
|
||||
package_dir = "src"
|
||||
filename = "CHANGELOG.rst"
|
||||
filename = "doc/en/changelog.rst"
|
||||
directory = "changelog/"
|
||||
title_format = "pytest {version} ({project_date})"
|
||||
template = "changelog/_template.rst"
|
||||
|
||||
@@ -85,13 +85,14 @@ def check_links():
|
||||
check_call(["tox", "-e", "docs-checklinks"])
|
||||
|
||||
|
||||
def pre_release(version):
|
||||
def pre_release(version, *, skip_check_links):
|
||||
"""Generates new docs, release announcements and creates a local tag."""
|
||||
announce(version)
|
||||
regen()
|
||||
changelog(version, write_out=True)
|
||||
fix_formatting()
|
||||
check_links()
|
||||
if not skip_check_links:
|
||||
check_links()
|
||||
|
||||
msg = "Preparing release version {}".format(version)
|
||||
check_call(["git", "commit", "-a", "-m", msg])
|
||||
@@ -114,8 +115,9 @@ def main():
|
||||
init(autoreset=True)
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("version", help="Release version")
|
||||
parser.add_argument("--skip-check-links", action="store_true", default=False)
|
||||
options = parser.parse_args()
|
||||
pre_release(options.version)
|
||||
pre_release(options.version, skip_check_links=options.skip_check_links)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -38,8 +38,8 @@ packages =
|
||||
_pytest.assertion
|
||||
_pytest.config
|
||||
_pytest.mark
|
||||
pytest
|
||||
|
||||
py_modules = pytest
|
||||
python_requires = >=3.5
|
||||
|
||||
[options.entry_points]
|
||||
|
||||
@@ -799,13 +799,6 @@ class AssertionRewriter(ast.NodeVisitor):
|
||||
self.push_format_context()
|
||||
# Rewrite assert into a bunch of statements.
|
||||
top_condition, explanation = self.visit(assert_.test)
|
||||
# If in a test module, check if directly asserting None, in order to warn [Issue #3191]
|
||||
if self.module_path is not None:
|
||||
self.statements.append(
|
||||
self.warn_about_none_ast(
|
||||
top_condition, module_path=self.module_path, lineno=assert_.lineno
|
||||
)
|
||||
)
|
||||
|
||||
negation = ast.UnaryOp(ast.Not(), top_condition)
|
||||
|
||||
@@ -887,30 +880,6 @@ class AssertionRewriter(ast.NodeVisitor):
|
||||
set_location(stmt, assert_.lineno, assert_.col_offset)
|
||||
return self.statements
|
||||
|
||||
def warn_about_none_ast(self, node, module_path, lineno):
|
||||
"""
|
||||
Returns an AST issuing a warning if the value of node is `None`.
|
||||
This is used to warn the user when asserting a function that asserts
|
||||
internally already.
|
||||
See issue #3191 for more details.
|
||||
"""
|
||||
val_is_none = ast.Compare(node, [ast.Is()], [ast.NameConstant(None)])
|
||||
send_warning = ast.parse(
|
||||
"""\
|
||||
from _pytest.warning_types import PytestAssertRewriteWarning
|
||||
from warnings import warn_explicit
|
||||
warn_explicit(
|
||||
PytestAssertRewriteWarning('asserting the value None, please use "assert is None"'),
|
||||
category=None,
|
||||
filename={filename!r},
|
||||
lineno={lineno},
|
||||
)
|
||||
""".format(
|
||||
filename=fspath(module_path), lineno=lineno
|
||||
)
|
||||
).body
|
||||
return ast.If(val_is_none, send_warning, [])
|
||||
|
||||
def visit_Name(self, name):
|
||||
# Display the repr of the name if it's a local variable or
|
||||
# _should_repr_global_name() thinks it's acceptable.
|
||||
|
||||
@@ -44,14 +44,27 @@ class Cache:
|
||||
_cachedir = attr.ib(repr=False)
|
||||
_config = attr.ib(repr=False)
|
||||
|
||||
# sub-directory under cache-dir for directories created by "makedir"
|
||||
_CACHE_PREFIX_DIRS = "d"
|
||||
|
||||
# sub-directory under cache-dir for values created by "set"
|
||||
_CACHE_PREFIX_VALUES = "v"
|
||||
|
||||
@classmethod
|
||||
def for_config(cls, config):
|
||||
cachedir = cls.cache_dir_from_config(config)
|
||||
if config.getoption("cacheclear") and cachedir.exists():
|
||||
rm_rf(cachedir)
|
||||
cachedir.mkdir()
|
||||
if config.getoption("cacheclear") and cachedir.is_dir():
|
||||
cls.clear_cache(cachedir)
|
||||
return cls(cachedir, config)
|
||||
|
||||
@classmethod
|
||||
def clear_cache(cls, cachedir: Path):
|
||||
"""Clears the sub-directories used to hold cached directories and values."""
|
||||
for prefix in (cls._CACHE_PREFIX_DIRS, cls._CACHE_PREFIX_VALUES):
|
||||
d = cachedir / prefix
|
||||
if d.is_dir():
|
||||
rm_rf(d)
|
||||
|
||||
@staticmethod
|
||||
def cache_dir_from_config(config):
|
||||
return resolve_from_str(config.getini("cache_dir"), config.rootdir)
|
||||
@@ -79,12 +92,12 @@ class Cache:
|
||||
name = Path(name)
|
||||
if len(name.parts) > 1:
|
||||
raise ValueError("name is not allowed to contain path separators")
|
||||
res = self._cachedir.joinpath("d", name)
|
||||
res = self._cachedir.joinpath(self._CACHE_PREFIX_DIRS, name)
|
||||
res.mkdir(exist_ok=True, parents=True)
|
||||
return py.path.local(res)
|
||||
|
||||
def _getvaluepath(self, key):
|
||||
return self._cachedir.joinpath("v", Path(key))
|
||||
return self._cachedir.joinpath(self._CACHE_PREFIX_VALUES, Path(key))
|
||||
|
||||
def get(self, key, default):
|
||||
""" return cached value for the given key. If no value
|
||||
@@ -417,7 +430,7 @@ def cacheshow(config, session):
|
||||
|
||||
dummy = object()
|
||||
basedir = config.cache._cachedir
|
||||
vdir = basedir / "v"
|
||||
vdir = basedir / Cache._CACHE_PREFIX_VALUES
|
||||
tw.sep("-", "cache values for %r" % glob)
|
||||
for valpath in sorted(x for x in vdir.rglob(glob) if x.is_file()):
|
||||
key = valpath.relative_to(vdir)
|
||||
@@ -429,7 +442,7 @@ def cacheshow(config, session):
|
||||
for line in pformat(val).splitlines():
|
||||
tw.line(" " + line)
|
||||
|
||||
ddir = basedir / "d"
|
||||
ddir = basedir / Cache._CACHE_PREFIX_DIRS
|
||||
if ddir.is_dir():
|
||||
contents = sorted(ddir.rglob(glob))
|
||||
tw.sep("-", "cache directories for %r" % glob)
|
||||
|
||||
@@ -630,16 +630,67 @@ notset = Notset()
|
||||
|
||||
|
||||
def _iter_rewritable_modules(package_files):
|
||||
"""
|
||||
Given an iterable of file names in a source distribution, return the "names" that should
|
||||
be marked for assertion rewrite (for example the package "pytest_mock/__init__.py" should
|
||||
be added as "pytest_mock" in the assertion rewrite mechanism.
|
||||
|
||||
This function has to deal with dist-info based distributions and egg based distributions
|
||||
(which are still very much in use for "editable" installs).
|
||||
|
||||
Here are the file names as seen in a dist-info based distribution:
|
||||
|
||||
pytest_mock/__init__.py
|
||||
pytest_mock/_version.py
|
||||
pytest_mock/plugin.py
|
||||
pytest_mock.egg-info/PKG-INFO
|
||||
|
||||
Here are the file names as seen in an egg based distribution:
|
||||
|
||||
src/pytest_mock/__init__.py
|
||||
src/pytest_mock/_version.py
|
||||
src/pytest_mock/plugin.py
|
||||
src/pytest_mock.egg-info/PKG-INFO
|
||||
LICENSE
|
||||
setup.py
|
||||
|
||||
We have to take in account those two distribution flavors in order to determine which
|
||||
names should be considered for assertion rewriting.
|
||||
|
||||
More information:
|
||||
https://github.com/pytest-dev/pytest-mock/issues/167
|
||||
"""
|
||||
package_files = list(package_files)
|
||||
seen_some = False
|
||||
for fn in package_files:
|
||||
is_simple_module = "/" not in fn and fn.endswith(".py")
|
||||
is_package = fn.count("/") == 1 and fn.endswith("__init__.py")
|
||||
if is_simple_module:
|
||||
module_name, _ = os.path.splitext(fn)
|
||||
yield module_name
|
||||
# we ignore "setup.py" at the root of the distribution
|
||||
if module_name != "setup":
|
||||
seen_some = True
|
||||
yield module_name
|
||||
elif is_package:
|
||||
package_name = os.path.dirname(fn)
|
||||
seen_some = True
|
||||
yield package_name
|
||||
|
||||
if not seen_some:
|
||||
# at this point we did not find any packages or modules suitable for assertion
|
||||
# rewriting, so we try again by stripping the first path component (to account for
|
||||
# "src" based source trees for example)
|
||||
# this approach lets us have the common case continue to be fast, as egg-distributions
|
||||
# are rarer
|
||||
new_package_files = []
|
||||
for fn in package_files:
|
||||
parts = fn.split("/")
|
||||
new_fn = "/".join(parts[1:])
|
||||
if new_fn:
|
||||
new_package_files.append(new_fn)
|
||||
if new_package_files:
|
||||
yield from _iter_rewritable_modules(new_package_files)
|
||||
|
||||
|
||||
class Config:
|
||||
"""
|
||||
|
||||
@@ -591,6 +591,8 @@ class LogXML:
|
||||
if report.when == "call":
|
||||
reporter.append_failure(report)
|
||||
self.open_reports.append(report)
|
||||
if not self.log_passing_tests:
|
||||
reporter.write_captured_output(report)
|
||||
else:
|
||||
reporter.append_error(report)
|
||||
elif report.skipped:
|
||||
|
||||
@@ -4,6 +4,7 @@ pytest: unit and functional testing with Python.
|
||||
"""
|
||||
from _pytest import __version__
|
||||
from _pytest.assertion import register_assert_rewrite
|
||||
from _pytest.compat import _setup_collect_fakemodule
|
||||
from _pytest.config import cmdline
|
||||
from _pytest.config import hookimpl
|
||||
from _pytest.config import hookspec
|
||||
@@ -93,14 +94,6 @@ __all__ = [
|
||||
"yield_fixture",
|
||||
]
|
||||
|
||||
if __name__ == "__main__":
|
||||
# if run as a script or by 'python -m pytest'
|
||||
# we trigger the below "else" condition by the following import
|
||||
import pytest
|
||||
|
||||
raise SystemExit(pytest.main())
|
||||
else:
|
||||
|
||||
from _pytest.compat import _setup_collect_fakemodule
|
||||
|
||||
_setup_collect_fakemodule()
|
||||
_setup_collect_fakemodule()
|
||||
del _setup_collect_fakemodule
|
||||
7
src/pytest/__main__.py
Normal file
7
src/pytest/__main__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
"""
|
||||
pytest entry point
|
||||
"""
|
||||
import pytest
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(pytest.main())
|
||||
@@ -270,6 +270,7 @@ class TestLastFailed:
|
||||
)
|
||||
result = testdir.runpytest(str(p), "--lf", "--cache-clear")
|
||||
result.stdout.fnmatch_lines(["*1 failed*2 passed*"])
|
||||
assert testdir.tmpdir.join(".pytest_cache", "README.md").isfile()
|
||||
|
||||
# Run this again to make sure clear-cache is robust
|
||||
if os.path.isdir(".pytest_cache"):
|
||||
|
||||
@@ -422,15 +422,21 @@ class TestConfigAPI:
|
||||
@pytest.mark.parametrize(
|
||||
"names, expected",
|
||||
[
|
||||
# dist-info based distributions root are files as will be put in PYTHONPATH
|
||||
(["bar.py"], ["bar"]),
|
||||
(["foo", "bar.py"], []),
|
||||
(["foo", "bar.pyc"], []),
|
||||
(["foo", "__init__.py"], ["foo"]),
|
||||
(["foo", "bar", "__init__.py"], []),
|
||||
(["foo/bar.py"], ["bar"]),
|
||||
(["foo/bar.pyc"], []),
|
||||
(["foo/__init__.py"], ["foo"]),
|
||||
(["bar/__init__.py", "xz.py"], ["bar", "xz"]),
|
||||
(["setup.py"], []),
|
||||
# egg based distributions root contain the files from the dist root
|
||||
(["src/bar/__init__.py"], ["bar"]),
|
||||
(["src/bar/__init__.py", "setup.py"], ["bar"]),
|
||||
(["source/python/bar/__init__.py", "setup.py"], ["bar"]),
|
||||
],
|
||||
)
|
||||
def test_iter_rewritable_modules(self, names, expected):
|
||||
assert list(_iter_rewritable_modules(["/".join(names)])) == expected
|
||||
assert list(_iter_rewritable_modules(names)) == expected
|
||||
|
||||
|
||||
class TestConfigFromdictargs:
|
||||
|
||||
@@ -1477,3 +1477,45 @@ def test_logging_passing_tests_disabled_does_not_log_test_output(
|
||||
node = dom.find_first_by_tag("testcase")
|
||||
assert len(node.find_by_tag("system-err")) == 0
|
||||
assert len(node.find_by_tag("system-out")) == 0
|
||||
|
||||
|
||||
@parametrize_families
|
||||
@pytest.mark.parametrize("junit_logging", ["no", "system-out", "system-err"])
|
||||
def test_logging_passing_tests_disabled_logs_output_for_failing_test_issue5430(
|
||||
testdir, junit_logging, run_and_parse, xunit_family
|
||||
):
|
||||
testdir.makeini(
|
||||
"""
|
||||
[pytest]
|
||||
junit_log_passing_tests=False
|
||||
junit_family={family}
|
||||
""".format(
|
||||
family=xunit_family
|
||||
)
|
||||
)
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
import logging
|
||||
import sys
|
||||
|
||||
def test_func():
|
||||
logging.warning('hello')
|
||||
assert 0
|
||||
"""
|
||||
)
|
||||
result, dom = run_and_parse(
|
||||
"-o", "junit_logging=%s" % junit_logging, family=xunit_family
|
||||
)
|
||||
assert result.ret == 1
|
||||
node = dom.find_first_by_tag("testcase")
|
||||
if junit_logging == "system-out":
|
||||
assert len(node.find_by_tag("system-err")) == 0
|
||||
assert len(node.find_by_tag("system-out")) == 1
|
||||
elif junit_logging == "system-err":
|
||||
assert len(node.find_by_tag("system-err")) == 1
|
||||
assert len(node.find_by_tag("system-out")) == 0
|
||||
else:
|
||||
assert junit_logging == "no"
|
||||
assert len(node.find_by_tag("system-err")) == 0
|
||||
assert len(node.find_by_tag("system-out")) == 0
|
||||
|
||||
@@ -543,7 +543,7 @@ class TestAssertionWarnings:
|
||||
|
||||
def test_tuple_warning(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
"""\
|
||||
def test_foo():
|
||||
assert (1,2)
|
||||
"""
|
||||
@@ -553,48 +553,6 @@ class TestAssertionWarnings:
|
||||
result, "assertion is always true, perhaps remove parentheses?"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def create_file(testdir, return_none):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
def foo(return_none):
|
||||
if return_none:
|
||||
return None
|
||||
else:
|
||||
return False
|
||||
|
||||
def test_foo():
|
||||
assert foo({return_none})
|
||||
""".format(
|
||||
return_none=return_none
|
||||
)
|
||||
)
|
||||
|
||||
def test_none_function_warns(self, testdir):
|
||||
self.create_file(testdir, True)
|
||||
result = testdir.runpytest()
|
||||
self.assert_result_warns(
|
||||
result, 'asserting the value None, please use "assert is None"'
|
||||
)
|
||||
|
||||
def test_assert_is_none_no_warn(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
def foo():
|
||||
return None
|
||||
|
||||
def test_foo():
|
||||
assert foo() is None
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(["*1 passed in*"])
|
||||
|
||||
def test_false_function_no_warn(self, testdir):
|
||||
self.create_file(testdir, False)
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(["*1 failed in*"])
|
||||
|
||||
|
||||
def test_warnings_checker_twice():
|
||||
"""Issue #4617"""
|
||||
|
||||
18
tox.ini
18
tox.ini
@@ -58,10 +58,16 @@ commands = pre-commit run --all-files --show-diff-on-failure
|
||||
[testenv:docs]
|
||||
basepython = python3
|
||||
usedevelop = True
|
||||
changedir = doc/en
|
||||
deps = -r{toxinidir}/doc/en/requirements.txt
|
||||
deps =
|
||||
-r{toxinidir}/doc/en/requirements.txt
|
||||
towncrier
|
||||
whitelist_externals = sh
|
||||
commands =
|
||||
sphinx-build -W --keep-going -b html . _build {posargs:}
|
||||
sh -c 'towncrier --draft > doc/en/_changelog_towncrier_draft.rst'
|
||||
# the '-t changelog_towncrier_draft' tags makes sphinx include the draft
|
||||
# changelog in the docs; this does not happen on ReadTheDocs because it uses
|
||||
# the standard sphinx command so the 'changelog_towncrier_draft' is never set there
|
||||
sphinx-build -W --keep-going -b html doc/en doc/en/_build -t changelog_towncrier_draft {posargs:}
|
||||
|
||||
[testenv:docs-checklinks]
|
||||
basepython = python3
|
||||
@@ -86,10 +92,10 @@ changedir = doc/en
|
||||
skipsdist = True
|
||||
basepython = python3
|
||||
deps =
|
||||
sphinx
|
||||
dataclasses
|
||||
PyYAML
|
||||
regendoc>=0.6.1
|
||||
dataclasses
|
||||
sphinx
|
||||
whitelist_externals =
|
||||
rm
|
||||
make
|
||||
@@ -117,8 +123,8 @@ deps =
|
||||
colorama
|
||||
gitpython
|
||||
pre-commit>=1.11.0
|
||||
towncrier
|
||||
wheel
|
||||
towncrier
|
||||
commands = python scripts/release.py {posargs}
|
||||
|
||||
[testenv:publish_gh_release_notes]
|
||||
|
||||
Reference in New Issue
Block a user