Drop Python 2.7 and 3.4 support

* Update setup.py requires and classifiers
* Drop Python 2.7 and 3.4 from CI
* Update docs dropping 2.7 and 3.4 support
* Fix mock imports and remove tests related to pypi's mock module
* Add py27 and 34 support docs to the sidebar
* Remove usage of six from tmpdir
* Remove six.PY* code blocks
* Remove sys.version_info related code
* Cleanup compat
* Remove obsolete safe_str
* Remove obsolete __unicode__ methods
* Remove compat.PY35 and compat.PY36: not really needed anymore
* Remove unused UNICODE_TYPES
* Remove Jython specific code
* Remove some Python 2 references from docs

Related to #5275
This commit is contained in:
Bruno Oliveira 2019-05-27 20:31:52 -03:00
parent 733f43b02e
commit 4d49ba6529
64 changed files with 226 additions and 1331 deletions

View File

@ -19,18 +19,13 @@ install:
jobs: jobs:
include: include:
# OSX tests - first (in test stage), since they are the slower ones. # OSX tests - first (in test stage), since they are the slower ones.
- &test-macos - os: osx
os: osx # NOTE: (tests with) pexpect appear to be buggy on Travis,
# at least with coverage.
# Log: https://travis-ci.org/pytest-dev/pytest/jobs/500358864
osx_image: xcode10.1 osx_image: xcode10.1
language: generic language: generic
# Coverage for: env: TOXENV=py37-xdist PYTEST_COVERAGE=1
# - py2 with symlink in test_cmdline_python_package_symlink.
env: TOXENV=py27-xdist PYTEST_COVERAGE=1
before_install:
- python -V
- test $(python -c 'import sys; print("%d%d" % sys.version_info[0:2])') = 27
- <<: *test-macos
env: TOXENV=py37-pexpect,py37-xdist PYTEST_COVERAGE=1
before_install: before_install:
- which python3 - which python3
- python3 -V - python3 -V
@ -38,20 +33,14 @@ jobs:
- python -V - python -V
- test $(python -c 'import sys; print("%d%d" % sys.version_info[0:2])') = 37 - test $(python -c 'import sys; print("%d%d" % sys.version_info[0:2])') = 37
# Full run of latest (major) supported versions, without xdist. # Full run of latest supported version, without xdist.
- env: TOXENV=py27
python: '2.7'
- env: TOXENV=py37 - env: TOXENV=py37
python: '3.7' python: '3.7'
# Coverage tracking is slow with pypy, skip it. # Coverage tracking is slow with pypy, skip it.
- env: TOXENV=pypy-xdist
python: 'pypy'
- env: TOXENV=pypy3-xdist - env: TOXENV=pypy3-xdist
python: 'pypy3' python: 'pypy3'
- env: TOXENV=py34-xdist
python: '3.4'
- env: TOXENV=py35-xdist - env: TOXENV=py35-xdist
python: '3.5' python: '3.5'
@ -62,12 +51,6 @@ jobs:
# Empty PYTEST_ADDOPTS to run this non-verbose. # Empty PYTEST_ADDOPTS to run this non-verbose.
- env: TOXENV=py37-lsof-numpy-xdist PYTEST_COVERAGE=1 PYTEST_ADDOPTS= - env: TOXENV=py37-lsof-numpy-xdist PYTEST_COVERAGE=1 PYTEST_ADDOPTS=
# Specialized factors for py27.
- env: TOXENV=py27-nobyte-numpy-xdist
python: '2.7'
- env: TOXENV=py27-pluggymaster-xdist
python: '2.7'
# Specialized factors for py37. # Specialized factors for py37.
# Coverage for: # Coverage for:
# - test_sys_breakpoint_interception (via pexpect). # - test_sys_breakpoint_interception (via pexpect).
@ -81,12 +64,7 @@ jobs:
if: type = cron if: type = cron
- stage: baseline - stage: baseline
# Coverage for: env: TOXENV=py36-xdist
# - _pytest.unittest._handle_skip (via pexpect).
env: TOXENV=py27-pexpect,py27-twisted PYTEST_COVERAGE=1
python: '2.7'
# Use py36 here for faster baseline.
- env: TOXENV=py36-xdist
python: '3.6' python: '3.6'
- env: TOXENV=linting,docs,doctesting PYTEST_COVERAGE=1 - env: TOXENV=linting,docs,doctesting PYTEST_COVERAGE=1
cache: cache:

View File

@ -85,7 +85,7 @@ Features
- Can run `unittest <https://docs.pytest.org/en/latest/unittest.html>`_ (or trial), - Can run `unittest <https://docs.pytest.org/en/latest/unittest.html>`_ (or trial),
`nose <https://docs.pytest.org/en/latest/nose.html>`_ test suites out of the box; `nose <https://docs.pytest.org/en/latest/nose.html>`_ test suites out of the box;
- Python 2.7, Python 3.4+, PyPy 2.3, Jython 2.5 (untested); - Python 3.5+ and PyPy3;
- Rich plugin architecture, with over 315+ `external plugins <http://plugincompat.herokuapp.com>`_ and thriving community; - Rich plugin architecture, with over 315+ `external plugins <http://plugincompat.herokuapp.com>`_ and thriving community;

View File

@ -4,7 +4,6 @@ trigger:
variables: variables:
PYTEST_ADDOPTS: "--junitxml=build/test-results/$(tox.env).xml -vv" PYTEST_ADDOPTS: "--junitxml=build/test-results/$(tox.env).xml -vv"
python.needs_vc: False
COVERAGE_FILE: "$(Build.Repository.LocalPath)/.coverage" COVERAGE_FILE: "$(Build.Repository.LocalPath)/.coverage"
COVERAGE_PROCESS_START: "$(Build.Repository.LocalPath)/.coveragerc" COVERAGE_PROCESS_START: "$(Build.Repository.LocalPath)/.coveragerc"
PYTEST_COVERAGE: '0' PYTEST_COVERAGE: '0'
@ -16,44 +15,10 @@ jobs:
vmImage: "vs2017-win2016" vmImage: "vs2017-win2016"
strategy: strategy:
matrix: matrix:
py27: # -- pypy3 disabled for now: #5279 --
python.version: '2.7'
tox.env: 'py27'
py27-nobyte-lsof-numpy:
python.version: '2.7'
tox.env: 'py27-lsof-nobyte-numpy'
# Coverage for:
# - test_supports_breakpoint_module_global
# - test_terminal_reporter_writer_attr (without xdist)
# - "if write" branch in _pytest.assertion.rewrite
# - numpy
# - pytester's LsofFdLeakChecker (being skipped)
PYTEST_COVERAGE: '1'
py27-twisted:
python.version: '2.7'
tox.env: 'py27-twisted'
python.needs_vc: True
py27-pluggymaster-xdist:
python.version: '2.7'
tox.env: 'py27-pluggymaster-xdist'
# Coverage for:
# - except-IOError in _attempt_to_close_capture_file for py2.
# Also seen with py27-nobyte (using xdist), and py27-xdist.
# But no exception with py27-pexpect,py27-twisted,py27-numpy.
PYTEST_COVERAGE: '1'
# -- pypy2 and pypy3 are disabled for now: #5279 --
# pypy:
# python.version: 'pypy2'
# tox.env: 'pypy'
# pypy3: # pypy3:
# python.version: 'pypy3' # python.version: 'pypy3'
# tox.env: 'pypy3' # tox.env: 'pypy3'
py34-xdist:
python.version: '3.4'
tox.env: 'py34-xdist'
# Coverage for:
# - _pytest.compat._bytes_to_ascii
PYTEST_COVERAGE: '1'
py35-xdist: py35-xdist:
python.version: '3.5' python.version: '3.5'
tox.env: 'py35-xdist' tox.env: 'py35-xdist'
@ -87,10 +52,6 @@ jobs:
versionSpec: '$(python.version)' versionSpec: '$(python.version)'
architecture: 'x64' architecture: 'x64'
- script: choco install vcpython27
condition: eq(variables['python.needs_vc'], True)
displayName: 'Install VC for py27'
- script: python -m pip install --upgrade pip && python -m pip install tox - script: python -m pip install --upgrade pip && python -m pip install tox
displayName: 'Install tox' displayName: 'Install tox'

View File

@ -10,6 +10,7 @@
<li><a href="{{ pathto('changelog') }}">Changelog</a></li> <li><a href="{{ pathto('changelog') }}">Changelog</a></li>
<li><a href="{{ pathto('contributing') }}">Contributing</a></li> <li><a href="{{ pathto('contributing') }}">Contributing</a></li>
<li><a href="{{ pathto('backwards-compatibility') }}">Backwards Compatibility</a></li> <li><a href="{{ pathto('backwards-compatibility') }}">Backwards Compatibility</a></li>
<li><a href="{{ pathto('py27-py34-deprecation') }}">Python 2.7 and 3.4 Support</a></li>
<li><a href="{{ pathto('license') }}">License</a></li> <li><a href="{{ pathto('license') }}">License</a></li>
<li><a href="{{ pathto('contact') }}">Contact Channels</a></li> <li><a href="{{ pathto('contact') }}">Contact Channels</a></li>
</ul> </ul>

View File

@ -288,8 +288,7 @@ its test methods:
This is equivalent to directly applying the decorator to the This is equivalent to directly applying the decorator to the
two test functions. two test functions.
To remain backward-compatible with Python 2.4 you can also set a Due to legacy reasons, it is possible to set the ``pytestmark`` attribute on a TestClass like this:
``pytestmark`` attribute on a TestClass like this:
.. code-block:: python .. code-block:: python

View File

@ -1,9 +1,9 @@
Installation and Getting Started Installation and Getting Started
=================================== ===================================
**Pythons**: Python 2.7, 3.4, 3.5, 3.6, 3.7, Jython, PyPy-2.3 **Pythons**: Python 3.5, 3.6, 3.7, PyPy3
**Platforms**: Unix/Posix and Windows **Platforms**: Linux and Windows
**PyPI package name**: `pytest <https://pypi.org/project/pytest/>`_ **PyPI package name**: `pytest <https://pypi.org/project/pytest/>`_

View File

@ -7,8 +7,7 @@ Good Integration Practices
Install package with pip Install package with pip
------------------------------------------------- -------------------------------------------------
For development, we recommend you use venv_ for virtual environments For development, we recommend you use venv_ for virtual environments and
(or virtualenv_ for Python 2.7) and
pip_ for installing your application and any dependencies, pip_ for installing your application and any dependencies,
as well as the ``pytest`` package itself. as well as the ``pytest`` package itself.
This ensures your code and dependencies are isolated from your system Python installation. This ensures your code and dependencies are isolated from your system Python installation.

View File

@ -61,7 +61,7 @@ Features
- Can run :ref:`unittest <unittest>` (including trial) and :ref:`nose <noseintegration>` test suites out of the box; - Can run :ref:`unittest <unittest>` (including trial) and :ref:`nose <noseintegration>` test suites out of the box;
- Python 2.7, Python 3.4+, PyPy 2.3, Jython 2.5 (untested); - Python Python 3.5+ and PyPy 3;
- Rich plugin architecture, with over 315+ `external plugins <http://plugincompat.herokuapp.com>`_ and thriving community; - Rich plugin architecture, with over 315+ `external plugins <http://plugincompat.herokuapp.com>`_ and thriving community;

View File

@ -1,5 +1,4 @@
[metadata] [metadata]
name = pytest name = pytest
description = pytest: simple powerful testing with Python description = pytest: simple powerful testing with Python
long_description = file: README.rst long_description = file: README.rst
@ -23,13 +22,11 @@ classifiers =
Topic :: Software Development :: Testing Topic :: Software Development :: Testing
Topic :: Software Development :: Libraries Topic :: Software Development :: Libraries
Topic :: Utilities Topic :: Utilities
Programming Language :: Python :: 2 Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.4
Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
platforms = unix, linux, osx, cygwin, win32 platforms = unix, linux, osx, cygwin, win32
[options] [options]
@ -43,8 +40,7 @@ packages =
_pytest.mark _pytest.mark
py_modules = pytest py_modules = pytest
python_requires = >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* python_requires = >=3.5
[options.entry_points] [options.entry_points]
console_scripts = console_scripts =
@ -59,13 +55,9 @@ all_files = 1
[upload_sphinx] [upload_sphinx]
upload-dir = doc/en/build/html upload-dir = doc/en/build/html
[bdist_wheel]
universal = 1
[check-manifest] [check-manifest]
ignore = ignore =
_pytest/_version.py _pytest/_version.py
[devpi:upload] [devpi:upload]
formats = sdist.tgz,bdist_wheel formats = sdist.tgz,bdist_wheel

View File

@ -8,10 +8,8 @@ INSTALL_REQUIRES = [
"six>=1.10.0", "six>=1.10.0",
"packaging", "packaging",
"attrs>=17.4.0", "attrs>=17.4.0",
'more-itertools>=4.0.0,<6.0.0;python_version<="2.7"', "more-itertools>=4.0.0",
'more-itertools>=4.0.0;python_version>"2.7"',
"atomicwrites>=1.0", "atomicwrites>=1.0",
'funcsigs>=1.0;python_version<"3.0"',
'pathlib2>=2.2.0;python_version<"3.6"', 'pathlib2>=2.2.0;python_version<"3.6"',
'colorama;sys_platform=="win32"', 'colorama;sys_platform=="win32"',
"pluggy>=0.12,<1.0", "pluggy>=0.12,<1.0",
@ -30,9 +28,9 @@ def main():
"testing": [ "testing": [
"argcomplete", "argcomplete",
"hypothesis>=3.56", "hypothesis>=3.56",
"mock",
"nose", "nose",
"requests", "requests",
"mock;python_version=='2.7'",
], ],
}, },
# fmt: on # fmt: on

View File

@ -9,25 +9,16 @@ import sys
import traceback import traceback
from inspect import CO_VARARGS from inspect import CO_VARARGS
from inspect import CO_VARKEYWORDS from inspect import CO_VARKEYWORDS
from traceback import format_exception_only
from weakref import ref from weakref import ref
import attr import attr
import pluggy import pluggy
import py import py
from six import text_type
import _pytest import _pytest
from _pytest._io.saferepr import safeformat from _pytest._io.saferepr import safeformat
from _pytest._io.saferepr import saferepr from _pytest._io.saferepr import saferepr
from _pytest.compat import _PY2
from _pytest.compat import _PY3
from _pytest.compat import PY35
from _pytest.compat import safe_str
if _PY3:
from traceback import format_exception_only
else:
from ._py2traceback import format_exception_only
class Code(object): class Code(object):
@ -208,8 +199,7 @@ class TracebackEntry(object):
locals = property(getlocals, None, None, "locals of underlaying frame") locals = property(getlocals, None, None, "locals of underlaying frame")
def getfirstlinesource(self): def getfirstlinesource(self):
# on Jython this firstlineno can be -1 apparently return self.frame.code.firstlineno
return max(self.frame.code.firstlineno, 0)
def getsource(self, astcache=None): def getsource(self, astcache=None):
""" return failing source code. """ """ return failing source code. """
@ -391,9 +381,7 @@ class ExceptionInfo(object):
help for navigating the traceback. help for navigating the traceback.
""" """
_assert_start_repr = ( _assert_start_repr = "AssertionError('assert "
"AssertionError(u'assert " if _PY2 else "AssertionError('assert "
)
_excinfo = attr.ib() _excinfo = attr.ib()
_striptext = attr.ib(default="") _striptext = attr.ib(default="")
@ -558,11 +546,6 @@ class ExceptionInfo(object):
loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly()) loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly())
return str(loc) return str(loc)
def __unicode__(self):
entry = self.traceback[-1]
loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly())
return text_type(loc)
def match(self, regexp): def match(self, regexp):
""" """
Check whether the regular expression 'regexp' is found in the string Check whether the regular expression 'regexp' is found in the string
@ -692,8 +675,7 @@ class FormattedExcinfo(object):
source = _pytest._code.Source("???") source = _pytest._code.Source("???")
line_index = 0 line_index = 0
else: else:
# entry.getfirstlinesource() can be -1, should be 0 on jython line_index = entry.lineno - entry.getfirstlinesource()
line_index = entry.lineno - max(entry.getfirstlinesource(), 0)
lines = [] lines = []
style = entry._repr_style style = entry._repr_style
@ -733,7 +715,7 @@ class FormattedExcinfo(object):
if self.tbfilter: if self.tbfilter:
traceback = traceback.filter() traceback = traceback.filter()
if is_recursion_error(excinfo): if excinfo.errisinstance(RecursionError):
traceback, extraline = self._truncate_recursive_traceback(traceback) traceback, extraline = self._truncate_recursive_traceback(traceback)
else: else:
extraline = None extraline = None
@ -769,7 +751,7 @@ class FormattedExcinfo(object):
" Displaying first and last {max_frames} stack frames out of {total}." " Displaying first and last {max_frames} stack frames out of {total}."
).format( ).format(
exc_type=type(e).__name__, exc_type=type(e).__name__,
exc_msg=safe_str(e), exc_msg=str(e),
max_frames=max_frames, max_frames=max_frames,
total=len(traceback), total=len(traceback),
) )
@ -784,64 +766,51 @@ class FormattedExcinfo(object):
return traceback, extraline return traceback, extraline
def repr_excinfo(self, excinfo): def repr_excinfo(self, excinfo):
if _PY2:
reprtraceback = self.repr_traceback(excinfo)
reprcrash = excinfo._getreprcrash()
return ReprExceptionInfo(reprtraceback, reprcrash) repr_chain = []
else: e = excinfo.value
repr_chain = [] descr = None
e = excinfo.value seen = set()
descr = None while e is not None and id(e) not in seen:
seen = set() seen.add(id(e))
while e is not None and id(e) not in seen: if excinfo:
seen.add(id(e)) reprtraceback = self.repr_traceback(excinfo)
if excinfo: reprcrash = excinfo._getreprcrash()
reprtraceback = self.repr_traceback(excinfo) else:
reprcrash = excinfo._getreprcrash() # fallback to native repr if the exception doesn't have a traceback:
else: # ExceptionInfo objects require a full traceback to work
# fallback to native repr if the exception doesn't have a traceback: reprtraceback = ReprTracebackNative(
# ExceptionInfo objects require a full traceback to work traceback.format_exception(type(e), e, None)
reprtraceback = ReprTracebackNative( )
traceback.format_exception(type(e), e, None) reprcrash = None
)
reprcrash = None
repr_chain += [(reprtraceback, reprcrash, descr)] repr_chain += [(reprtraceback, reprcrash, descr)]
if e.__cause__ is not None and self.chain: if e.__cause__ is not None and self.chain:
e = e.__cause__ e = e.__cause__
excinfo = ( excinfo = (
ExceptionInfo((type(e), e, e.__traceback__)) ExceptionInfo((type(e), e, e.__traceback__))
if e.__traceback__ if e.__traceback__
else None else None
) )
descr = "The above exception was the direct cause of the following exception:" descr = "The above exception was the direct cause of the following exception:"
elif ( elif (
e.__context__ is not None e.__context__ is not None and not e.__suppress_context__ and self.chain
and not e.__suppress_context__ ):
and self.chain e = e.__context__
): excinfo = (
e = e.__context__ ExceptionInfo((type(e), e, e.__traceback__))
excinfo = ( if e.__traceback__
ExceptionInfo((type(e), e, e.__traceback__)) else None
if e.__traceback__ )
else None descr = "During handling of the above exception, another exception occurred:"
) else:
descr = "During handling of the above exception, another exception occurred:" e = None
else: repr_chain.reverse()
e = None return ExceptionChainRepr(repr_chain)
repr_chain.reverse()
return ExceptionChainRepr(repr_chain)
class TerminalRepr(object): class TerminalRepr(object):
def __str__(self): def __str__(self):
s = self.__unicode__()
if _PY2:
s = s.encode("utf-8")
return s
def __unicode__(self):
# FYI this is called from pytest-xdist's serialization of exception # FYI this is called from pytest-xdist's serialization of exception
# information. # information.
io = py.io.TextIO() io = py.io.TextIO()
@ -1006,7 +975,7 @@ class ReprFuncArgs(TerminalRepr):
if self.args: if self.args:
linesofar = "" linesofar = ""
for name, value in self.args: for name, value in self.args:
ns = "%s = %s" % (safe_str(name), safe_str(value)) ns = "%s = %s" % (name, value)
if len(ns) + len(linesofar) + 2 > tw.fullwidth: if len(ns) + len(linesofar) + 2 > tw.fullwidth:
if linesofar: if linesofar:
tw.line(linesofar) tw.line(linesofar)
@ -1038,23 +1007,6 @@ def getrawcode(obj, trycall=True):
return obj return obj
if PY35: # RecursionError introduced in 3.5
def is_recursion_error(excinfo):
return excinfo.errisinstance(RecursionError) # noqa
else:
def is_recursion_error(excinfo):
if not excinfo.errisinstance(RuntimeError):
return False
try:
return "maximum recursion depth exceeded" in str(excinfo.value)
except UnicodeError:
return False
# relative paths that we use to filter traceback entries from appearing to the user; # relative paths that we use to filter traceback entries from appearing to the user;
# see filter_traceback # see filter_traceback
# note: if we need to add more paths than what we have now we should probably use a list # note: if we need to add more paths than what we have now we should probably use a list

View File

@ -74,10 +74,6 @@ class AssertionState(object):
def install_importhook(config): def install_importhook(config):
"""Try to install the rewrite hook, raise SystemError if it fails.""" """Try to install the rewrite hook, raise SystemError if it fails."""
# Jython has an AST bug that make the assertion rewriting hook malfunction.
if sys.platform.startswith("java"):
raise SystemError("rewrite not supported")
config._assertstate = AssertionState(config, "rewrite") config._assertstate = AssertionState(config, "rewrite")
config._assertstate.hook = hook = rewrite.AssertionRewritingHook(config) config._assertstate.hook = hook = rewrite.AssertionRewritingHook(config)
sys.meta_path.insert(0, hook) sys.meta_path.insert(0, hook)

View File

@ -15,6 +15,7 @@ import string
import struct import struct
import sys import sys
import types import types
from importlib.util import spec_from_file_location
import atomicwrites import atomicwrites
import py import py
@ -25,7 +26,6 @@ from _pytest.assertion import util
from _pytest.assertion.util import ( # noqa: F401 from _pytest.assertion.util import ( # noqa: F401
format_explanation as _format_explanation, format_explanation as _format_explanation,
) )
from _pytest.compat import spec_from_file_location
from _pytest.pathlib import fnmatch_ex from _pytest.pathlib import fnmatch_ex
from _pytest.pathlib import PurePath from _pytest.pathlib import PurePath
@ -35,8 +35,6 @@ if hasattr(imp, "get_tag"):
else: else:
if hasattr(sys, "pypy_version_info"): if hasattr(sys, "pypy_version_info"):
impl = "pypy" impl = "pypy"
elif sys.platform == "java":
impl = "jython"
else: else:
impl = "cpython" impl = "cpython"
ver = sys.version_info ver = sys.version_info
@ -46,15 +44,6 @@ else:
PYC_EXT = ".py" + (__debug__ and "c" or "o") PYC_EXT = ".py" + (__debug__ and "c" or "o")
PYC_TAIL = "." + PYTEST_TAG + PYC_EXT PYC_TAIL = "." + PYTEST_TAG + PYC_EXT
ASCII_IS_DEFAULT_ENCODING = sys.version_info[0] < 3
if sys.version_info >= (3, 5):
ast_Call = ast.Call
else:
def ast_Call(a, b, c):
return ast.Call(a, b, c, None, None)
class AssertionRewritingHook(object): class AssertionRewritingHook(object):
"""PEP302 Import hook which rewrites asserts.""" """PEP302 Import hook which rewrites asserts."""
@ -364,37 +353,6 @@ def _rewrite_test(config, fn):
source = fn.read("rb") source = fn.read("rb")
except EnvironmentError: except EnvironmentError:
return None, None return None, None
if ASCII_IS_DEFAULT_ENCODING:
# ASCII is the default encoding in Python 2. Without a coding
# declaration, Python 2 will complain about any bytes in the file
# outside the ASCII range. Sadly, this behavior does not extend to
# compile() or ast.parse(), which prefer to interpret the bytes as
# latin-1. (At least they properly handle explicit coding cookies.) To
# preserve this error behavior, we could force ast.parse() to use ASCII
# as the encoding by inserting a coding cookie. Unfortunately, that
# messes up line numbers. Thus, we have to check ourselves if anything
# is outside the ASCII range in the case no encoding is explicitly
# declared. For more context, see issue #269. Yay for Python 3 which
# gets this right.
end1 = source.find("\n")
end2 = source.find("\n", end1 + 1)
if (
not source.startswith(BOM_UTF8)
and cookie_re.match(source[0:end1]) is None
and cookie_re.match(source[end1 + 1 : end2]) is None
):
if hasattr(state, "_indecode"):
# encodings imported us again, so don't rewrite.
return None, None
state._indecode = True
try:
try:
source.decode("ascii")
except UnicodeDecodeError:
# Let it fail in real import.
return None, None
finally:
del state._indecode
try: try:
tree = ast.parse(source, filename=fn.strpath) tree = ast.parse(source, filename=fn.strpath)
except SyntaxError: except SyntaxError:
@ -737,7 +695,7 @@ class AssertionRewriter(ast.NodeVisitor):
"""Call a helper in this module.""" """Call a helper in this module."""
py_name = ast.Name("@pytest_ar", ast.Load()) py_name = ast.Name("@pytest_ar", ast.Load())
attr = ast.Attribute(py_name, name, ast.Load()) attr = ast.Attribute(py_name, name, ast.Load())
return ast_Call(attr, list(args), []) return ast.Call(attr, list(args), [])
def builtin(self, name): def builtin(self, name):
"""Return the builtin called *name*.""" """Return the builtin called *name*."""
@ -847,11 +805,9 @@ class AssertionRewriter(ast.NodeVisitor):
msg = self.pop_format_context(template) msg = self.pop_format_context(template)
fmt = self.helper("_format_explanation", msg) fmt = self.helper("_format_explanation", msg)
err_name = ast.Name("AssertionError", ast.Load()) err_name = ast.Name("AssertionError", ast.Load())
exc = ast_Call(err_name, [fmt], []) exc = ast.Call(err_name, [fmt], [])
if sys.version_info[0] >= 3: raise_ = ast.Raise(exc, None)
raise_ = ast.Raise(exc, None)
else:
raise_ = ast.Raise(exc, None, None)
body.append(raise_) body.append(raise_)
# Clear temporary variables by setting them to None. # Clear temporary variables by setting them to None.
if self.variables: if self.variables:
@ -893,7 +849,7 @@ warn_explicit(
def visit_Name(self, name): def visit_Name(self, name):
# Display the repr of the name if it's a local variable or # Display the repr of the name if it's a local variable or
# _should_repr_global_name() thinks it's acceptable. # _should_repr_global_name() thinks it's acceptable.
locs = ast_Call(self.builtin("locals"), [], []) locs = ast.Call(self.builtin("locals"), [], [])
inlocs = ast.Compare(ast.Str(name.id), [ast.In()], [locs]) inlocs = ast.Compare(ast.Str(name.id), [ast.In()], [locs])
dorepr = self.helper("_should_repr_global_name", name) dorepr = self.helper("_should_repr_global_name", name)
test = ast.BoolOp(ast.Or(), [inlocs, dorepr]) test = ast.BoolOp(ast.Or(), [inlocs, dorepr])
@ -920,7 +876,7 @@ warn_explicit(
res, expl = self.visit(v) res, expl = self.visit(v)
body.append(ast.Assign([ast.Name(res_var, ast.Store())], res)) body.append(ast.Assign([ast.Name(res_var, ast.Store())], res))
expl_format = self.pop_format_context(ast.Str(expl)) expl_format = self.pop_format_context(ast.Str(expl))
call = ast_Call(app, [expl_format], []) call = ast.Call(app, [expl_format], [])
self.on_failure.append(ast.Expr(call)) self.on_failure.append(ast.Expr(call))
if i < levels: if i < levels:
cond = res cond = res
@ -959,9 +915,9 @@ warn_explicit(
and isinstance(call.args[0], (ast.GeneratorExp, ast.ListComp)) and isinstance(call.args[0], (ast.GeneratorExp, ast.ListComp))
) )
def visit_Call_35(self, call): def visit_Call(self, call):
""" """
visit `ast.Call` nodes on Python3.5 and after visit `ast.Call` nodes
""" """
if self._is_any_call_with_generator_or_list_comprehension(call): if self._is_any_call_with_generator_or_list_comprehension(call):
return self._visit_all(call) return self._visit_all(call)
@ -1013,46 +969,6 @@ warn_explicit(
new_starred = ast.Starred(res, starred.ctx) new_starred = ast.Starred(res, starred.ctx)
return new_starred, "*" + expl return new_starred, "*" + expl
def visit_Call_legacy(self, call):
"""
visit `ast.Call nodes on 3.4 and below`
"""
if self._is_any_call_with_generator_or_list_comprehension(call):
return self._visit_all(call)
new_func, func_expl = self.visit(call.func)
arg_expls = []
new_args = []
new_kwargs = []
new_star = new_kwarg = None
for arg in call.args:
res, expl = self.visit(arg)
new_args.append(res)
arg_expls.append(expl)
for keyword in call.keywords:
res, expl = self.visit(keyword.value)
new_kwargs.append(ast.keyword(keyword.arg, res))
arg_expls.append(keyword.arg + "=" + expl)
if call.starargs:
new_star, expl = self.visit(call.starargs)
arg_expls.append("*" + expl)
if call.kwargs:
new_kwarg, expl = self.visit(call.kwargs)
arg_expls.append("**" + expl)
expl = "%s(%s)" % (func_expl, ", ".join(arg_expls))
new_call = ast.Call(new_func, new_args, new_kwargs, new_star, new_kwarg)
res = self.assign(new_call)
res_expl = self.explanation_param(self.display(res))
outer_expl = "%s\n{%s = %s\n}" % (res_expl, res_expl, expl)
return res, outer_expl
# ast.Call signature changed on 3.5,
# conditionally change which methods is named
# visit_Call depending on Python version
if sys.version_info >= (3, 5):
visit_Call = visit_Call_35
else:
visit_Call = visit_Call_legacy
def visit_Attribute(self, attr): def visit_Attribute(self, attr):
if not isinstance(attr.ctx, ast.Load): if not isinstance(attr.ctx, ast.Load):
return self.generic_visit(attr) return self.generic_visit(attr)

View File

@ -5,11 +5,11 @@ from __future__ import division
from __future__ import print_function from __future__ import print_function
import pprint import pprint
from collections.abc import Sequence
import six import six
import _pytest._code import _pytest._code
from ..compat import Sequence
from _pytest import outcomes from _pytest import outcomes
from _pytest._io.saferepr import saferepr from _pytest._io.saferepr import saferepr

View File

@ -18,7 +18,6 @@ import py
import six import six
import pytest import pytest
from .compat import _PY2 as PY2
from .pathlib import Path from .pathlib import Path
from .pathlib import resolve_from_str from .pathlib import resolve_from_str
from .pathlib import rmtree from .pathlib import rmtree
@ -129,7 +128,7 @@ class Cache(object):
if not cache_dir_exists_already: if not cache_dir_exists_already:
self._ensure_supporting_files() self._ensure_supporting_files()
try: try:
f = path.open("wb" if PY2 else "w") f = path.open("w")
except (IOError, OSError): except (IOError, OSError):
self.warn("cache could not write path {path}", path=path) self.warn("cache could not write path {path}", path=path)
else: else:

View File

@ -18,7 +18,6 @@ from tempfile import TemporaryFile
import six import six
import pytest import pytest
from _pytest.compat import _PY3
from _pytest.compat import CaptureIO from _pytest.compat import CaptureIO
patchsysdict = {0: "stdin", 1: "stdout", 2: "stderr"} patchsysdict = {0: "stdin", 1: "stdout", 2: "stderr"}
@ -283,10 +282,6 @@ def capsysbinary(request):
``out`` and ``err`` will be ``bytes`` objects. ``out`` and ``err`` will be ``bytes`` objects.
""" """
_ensure_only_one_capture_fixture(request, "capsysbinary") _ensure_only_one_capture_fixture(request, "capsysbinary")
# Currently, the implementation uses the python3 specific `.buffer`
# property of CaptureIO.
if sys.version_info < (3,):
raise request.raiseerror("capsysbinary is only supported on Python 3")
with _install_capture_fixture_on_item(request, SysCaptureBinary) as fixture: with _install_capture_fixture_on_item(request, SysCaptureBinary) as fixture:
yield fixture yield fixture
@ -434,7 +429,7 @@ class EncodedFile(object):
def write(self, obj): def write(self, obj):
if isinstance(obj, six.text_type): if isinstance(obj, six.text_type):
obj = obj.encode(self.encoding, "replace") obj = obj.encode(self.encoding, "replace")
elif _PY3: else:
raise TypeError( raise TypeError(
"write() argument must be str, not {}".format(type(obj).__name__) "write() argument must be str, not {}".format(type(obj).__name__)
) )
@ -608,7 +603,7 @@ class FDCaptureBinary(object):
os.dup2(targetfd_save, self.targetfd) os.dup2(targetfd_save, self.targetfd)
os.close(targetfd_save) os.close(targetfd_save)
self.syscapture.done() self.syscapture.done()
_attempt_to_close_capture_file(self.tmpfile) self.tmpfile.close()
self._state = "done" self._state = "done"
def suspend(self): def suspend(self):
@ -681,7 +676,7 @@ class SysCapture(object):
def done(self): def done(self):
setattr(sys, self.name, self._old) setattr(sys, self.name, self._old)
del self._old del self._old
_attempt_to_close_capture_file(self.tmpfile) self.tmpfile.close()
self._state = "done" self._state = "done"
def suspend(self): def suspend(self):
@ -738,10 +733,7 @@ class DontReadFromInput(six.Iterator):
@property @property
def buffer(self): def buffer(self):
if sys.version_info >= (3, 0): return self
return self
else:
raise AttributeError("redirected stdin has no attribute buffer")
def _colorama_workaround(): def _colorama_workaround():
@ -837,14 +829,3 @@ def _py36_windowsconsoleio_workaround(stream):
sys.stdin = _reopen_stdio(sys.stdin, "rb") sys.stdin = _reopen_stdio(sys.stdin, "rb")
sys.stdout = _reopen_stdio(sys.stdout, "wb") sys.stdout = _reopen_stdio(sys.stdout, "wb")
sys.stderr = _reopen_stdio(sys.stderr, "wb") sys.stderr = _reopen_stdio(sys.stderr, "wb")
def _attempt_to_close_capture_file(f):
"""Suppress IOError when closing the temporary file used for capturing streams in py27 (#2370)"""
if six.PY2:
try:
f.close()
except IOError:
pass
else:
f.close()

View File

@ -6,59 +6,28 @@ from __future__ import absolute_import
from __future__ import division from __future__ import division
from __future__ import print_function from __future__ import print_function
import codecs
import functools import functools
import inspect import inspect
import io
import re import re
import sys import sys
from contextlib import contextmanager from contextlib import contextmanager
from inspect import Parameter
from inspect import signature
import py import py
import six
from six import text_type
import _pytest import _pytest
from _pytest._io.saferepr import saferepr from _pytest._io.saferepr import saferepr
from _pytest.outcomes import fail from _pytest.outcomes import fail
from _pytest.outcomes import TEST_OUTCOME from _pytest.outcomes import TEST_OUTCOME
try:
import enum
except ImportError: # pragma: no cover
# Only available in Python 3.4+ or as a backport
enum = None
_PY3 = sys.version_info > (3, 0)
_PY2 = not _PY3
if _PY3:
from inspect import signature, Parameter as Parameter
else:
from funcsigs import signature, Parameter as Parameter
NOTSET = object() NOTSET = object()
PY35 = sys.version_info[:2] >= (3, 5) MODULE_NOT_FOUND_ERROR = (
PY36 = sys.version_info[:2] >= (3, 6) "ModuleNotFoundError" if sys.version_info[:2] >= (3, 6) else "ImportError"
MODULE_NOT_FOUND_ERROR = "ModuleNotFoundError" if PY36 else "ImportError" )
if _PY3:
from collections.abc import MutableMapping as MappingMixin
from collections.abc import Iterable, Mapping, Sequence, Sized
else:
# those raise DeprecationWarnings in Python >=3.7
from collections import MutableMapping as MappingMixin # noqa
from collections import Iterable, Mapping, Sequence, Sized # noqa
if sys.version_info >= (3, 4):
from importlib.util import spec_from_file_location
else:
def spec_from_file_location(*_, **__):
return None
def _format_args(func): def _format_args(func):
@ -195,72 +164,36 @@ def _translate_non_printable(s):
return s.translate(_non_printable_ascii_translate_table) return s.translate(_non_printable_ascii_translate_table)
if _PY3: STRING_TYPES = bytes, str
STRING_TYPES = bytes, str
UNICODE_TYPES = six.text_type
if PY35:
def _bytes_to_ascii(val): def _bytes_to_ascii(val):
return val.decode("ascii", "backslashreplace") return val.decode("ascii", "backslashreplace")
def ascii_escaped(val):
"""If val is pure ascii, returns it as a str(). Otherwise, escapes
bytes objects into a sequence of escaped bytes:
b'\xc3\xb4\xc5\xd6' -> u'\\xc3\\xb4\\xc5\\xd6'
and escapes unicode objects into a sequence of escaped unicode
ids, e.g.:
'4\\nV\\U00043efa\\x0eMXWB\\x1e\\u3028\\u15fd\\xcd\\U0007d944'
note:
the obvious "v.decode('unicode-escape')" will return
valid utf-8 unicode if it finds them in bytes, but we
want to return escaped bytes for any byte, even if they match
a utf-8 string.
"""
if isinstance(val, bytes):
ret = _bytes_to_ascii(val)
else: else:
ret = val.encode("unicode_escape").decode("ascii")
def _bytes_to_ascii(val): return _translate_non_printable(ret)
if val:
# source: http://goo.gl/bGsnwC
encoded_bytes, _ = codecs.escape_encode(val)
return encoded_bytes.decode("ascii")
else:
# empty bytes crashes codecs.escape_encode (#1087)
return ""
def ascii_escaped(val):
"""If val is pure ascii, returns it as a str(). Otherwise, escapes
bytes objects into a sequence of escaped bytes:
b'\xc3\xb4\xc5\xd6' -> u'\\xc3\\xb4\\xc5\\xd6'
and escapes unicode objects into a sequence of escaped unicode
ids, e.g.:
'4\\nV\\U00043efa\\x0eMXWB\\x1e\\u3028\\u15fd\\xcd\\U0007d944'
note:
the obvious "v.decode('unicode-escape')" will return
valid utf-8 unicode if it finds them in bytes, but we
want to return escaped bytes for any byte, even if they match
a utf-8 string.
"""
if isinstance(val, bytes):
ret = _bytes_to_ascii(val)
else:
ret = val.encode("unicode_escape").decode("ascii")
return _translate_non_printable(ret)
else:
STRING_TYPES = six.string_types
UNICODE_TYPES = six.text_type
def ascii_escaped(val):
"""In py2 bytes and str are the same type, so return if it's a bytes
object, return it unchanged if it is a full ascii string,
otherwise escape it into its binary form.
If it's a unicode string, change the unicode characters into
unicode escapes.
"""
if isinstance(val, bytes):
try:
ret = val.decode("ascii")
except UnicodeDecodeError:
ret = val.encode("string-escape").decode("ascii")
else:
ret = val.encode("unicode-escape").decode("ascii")
return _translate_non_printable(ret)
class _PytestWrapper(object): class _PytestWrapper(object):
@ -357,36 +290,6 @@ def safe_isclass(obj):
return False return False
def _is_unittest_unexpected_success_a_failure():
"""Return if the test suite should fail if an @expectedFailure unittest test PASSES.
From https://docs.python.org/3/library/unittest.html?highlight=unittest#unittest.TestResult.wasSuccessful:
Changed in version 3.4: Returns False if there were any
unexpectedSuccesses from tests marked with the expectedFailure() decorator.
"""
return sys.version_info >= (3, 4)
if _PY3:
def safe_str(v):
"""returns v as string"""
return str(v)
else:
def safe_str(v):
"""returns v as string, converting to ascii if necessary"""
try:
return str(v)
except UnicodeError:
if not isinstance(v, text_type):
v = text_type(v)
errors = "replace"
return v.encode("utf-8", errors)
COLLECT_FAKEMODULE_ATTRIBUTES = ( COLLECT_FAKEMODULE_ATTRIBUTES = (
"Collector", "Collector",
"Module", "Module",
@ -410,27 +313,14 @@ def _setup_collect_fakemodule():
setattr(pytest.collect, attr, getattr(pytest, attr)) setattr(pytest.collect, attr, getattr(pytest, attr))
if _PY2: class CaptureIO(io.TextIOWrapper):
# Without this the test_dupfile_on_textio will fail, otherwise CaptureIO could directly inherit from StringIO. def __init__(self):
from py.io import TextIO super(CaptureIO, self).__init__(
io.BytesIO(), encoding="UTF-8", newline="", write_through=True
)
class CaptureIO(TextIO): def getvalue(self):
@property return self.buffer.getvalue().decode("UTF-8")
def encoding(self):
return getattr(self, "_encoding", "UTF-8")
else:
import io
class CaptureIO(io.TextIOWrapper):
def __init__(self):
super(CaptureIO, self).__init__(
io.BytesIO(), encoding="UTF-8", newline="", write_through=True
)
def getvalue(self):
return self.buffer.getvalue().decode("UTF-8")
class FuncargnamesCompatAttr(object): class FuncargnamesCompatAttr(object):
@ -442,16 +332,3 @@ class FuncargnamesCompatAttr(object):
def funcargnames(self): def funcargnames(self):
""" alias attribute for ``fixturenames`` for pre-2.3 compatibility""" """ alias attribute for ``fixturenames`` for pre-2.3 compatibility"""
return self.fixturenames return self.fixturenames
if six.PY2:
def lru_cache(*_, **__):
def dec(fn):
return fn
return dec
else:
from functools import lru_cache # noqa: F401

View File

@ -12,6 +12,7 @@ import shlex
import sys import sys
import types import types
import warnings import warnings
from functools import lru_cache
import importlib_metadata import importlib_metadata
import py import py
@ -31,8 +32,6 @@ from .findpaths import exists
from _pytest import deprecated from _pytest import deprecated
from _pytest._code import ExceptionInfo from _pytest._code import ExceptionInfo
from _pytest._code import filter_traceback from _pytest._code import filter_traceback
from _pytest.compat import lru_cache
from _pytest.compat import safe_str
from _pytest.outcomes import fail from _pytest.outcomes import fail
from _pytest.outcomes import Skipped from _pytest.outcomes import Skipped
from _pytest.warning_types import PytestConfigWarning from _pytest.warning_types import PytestConfigWarning
@ -73,7 +72,7 @@ def main(args=None, plugins=None):
if exc_info.traceback if exc_info.traceback
else exc_info.exconly() else exc_info.exconly()
) )
formatted_tb = safe_str(exc_repr) formatted_tb = str(exc_repr)
for line in formatted_tb.splitlines(): for line in formatted_tb.splitlines():
tw.line(line.rstrip(), red=True) tw.line(line.rstrip(), red=True)
return 4 return 4
@ -403,12 +402,6 @@ class PytestPluginManager(PluginManager):
else: else:
directory = path directory = path
if six.PY2: # py2 is not using lru_cache.
try:
return self._dirpath2confmods[directory]
except KeyError:
pass
# XXX these days we may rather want to use config.rootdir # XXX these days we may rather want to use config.rootdir
# and allow users to opt into looking into the rootdir parent # and allow users to opt into looking into the rootdir parent
# directories instead of requiring to specify confcutdir # directories instead of requiring to specify confcutdir
@ -566,7 +559,7 @@ class PytestPluginManager(PluginManager):
except ImportError as e: except ImportError as e:
new_exc_message = 'Error importing plugin "%s": %s' % ( new_exc_message = 'Error importing plugin "%s": %s' % (
modname, modname,
safe_str(e.args[0]), str(e.args[0]),
) )
new_exc = ImportError(new_exc_message) new_exc = ImportError(new_exc_message)
tb = sys.exc_info()[2] tb = sys.exc_info()[2]

View File

@ -333,7 +333,6 @@ class DoctestTextfile(pytest.Module):
checker=_get_checker(), checker=_get_checker(),
continue_on_failure=_get_continue_on_failure(self.config), continue_on_failure=_get_continue_on_failure(self.config),
) )
_fix_spoof_python2(runner, encoding)
parser = doctest.DocTestParser() parser = doctest.DocTestParser()
test = parser.get_doctest(text, globs, name, filename, 0) test = parser.get_doctest(text, globs, name, filename, 0)
@ -539,32 +538,6 @@ def _get_report_choice(key):
}[key] }[key]
def _fix_spoof_python2(runner, encoding):
"""
Installs a "SpoofOut" into the given DebugRunner so it properly deals with unicode output. This
should patch only doctests for text files because they don't have a way to declare their
encoding. Doctests in docstrings from Python modules don't have the same problem given that
Python already decoded the strings.
This fixes the problem related in issue #2434.
"""
from _pytest.compat import _PY2
if not _PY2:
return
from doctest import _SpoofOut
class UnicodeSpoof(_SpoofOut):
def getvalue(self):
result = _SpoofOut.getvalue(self)
if encoding and isinstance(result, bytes):
result = result.decode(encoding)
return result
runner._fakeout = UnicodeSpoof()
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
def doctest_namespace(): def doctest_namespace():
""" """

View File

@ -1303,11 +1303,7 @@ class FixtureManager(object):
# during fixture definition we wrap the original fixture function # during fixture definition we wrap the original fixture function
# to issue a warning if called directly, so here we unwrap it in order to not emit the warning # to issue a warning if called directly, so here we unwrap it in order to not emit the warning
# when pytest itself calls the fixture function # when pytest itself calls the fixture function
if six.PY2 and unittest: obj = get_real_method(obj, holderobj)
# hack on Python 2 because of the unbound methods
obj = get_real_func(obj)
else:
obj = get_real_method(obj, holderobj)
fixture_def = FixtureDef( fixture_def = FixtureDef(
self, self,

View File

@ -26,10 +26,6 @@ import pytest
from _pytest import nodes from _pytest import nodes
from _pytest.config import filename_arg from _pytest.config import filename_arg
# Python 2.X and 3.X compatibility
if sys.version_info[0] < 3:
from codecs import open
class Junit(py.xml.Namespace): class Junit(py.xml.Namespace):
pass pass

View File

@ -43,10 +43,7 @@ class ColoredLevelFormatter(logging.Formatter):
def __init__(self, terminalwriter, *args, **kwargs): def __init__(self, terminalwriter, *args, **kwargs):
super(ColoredLevelFormatter, self).__init__(*args, **kwargs) super(ColoredLevelFormatter, self).__init__(*args, **kwargs)
if six.PY2: self._original_fmt = self._style._fmt
self._original_fmt = self._fmt
else:
self._original_fmt = self._style._fmt
self._level_to_fmt_mapping = {} self._level_to_fmt_mapping = {}
levelname_fmt_match = self.LEVELNAME_FMT_REGEX.search(self._fmt) levelname_fmt_match = self.LEVELNAME_FMT_REGEX.search(self._fmt)
@ -70,10 +67,7 @@ class ColoredLevelFormatter(logging.Formatter):
def format(self, record): def format(self, record):
fmt = self._level_to_fmt_mapping.get(record.levelno, self._original_fmt) fmt = self._level_to_fmt_mapping.get(record.levelno, self._original_fmt)
if six.PY2: self._style._fmt = fmt
self._fmt = fmt
else:
self._style._fmt = fmt
return super(ColoredLevelFormatter, self).format(record) return super(ColoredLevelFormatter, self).format(record)

View File

@ -4,7 +4,6 @@ from __future__ import absolute_import
from __future__ import division from __future__ import division
from __future__ import print_function from __future__ import print_function
import contextlib
import fnmatch import fnmatch
import functools import functools
import os import os
@ -342,46 +341,6 @@ def pytest_collection_modifyitems(items, config):
items[:] = remaining items[:] = remaining
@contextlib.contextmanager
def _patched_find_module():
"""Patch bug in pkgutil.ImpImporter.find_module
When using pkgutil.find_loader on python<3.4 it removes symlinks
from the path due to a call to os.path.realpath. This is not consistent
with actually doing the import (in these versions, pkgutil and __import__
did not share the same underlying code). This can break conftest
discovery for pytest where symlinks are involved.
The only supported python<3.4 by pytest is python 2.7.
"""
if six.PY2: # python 3.4+ uses importlib instead
def find_module_patched(self, fullname, path=None):
# Note: we ignore 'path' argument since it is only used via meta_path
subname = fullname.split(".")[-1]
if subname != fullname and self.path is None:
return None
if self.path is None:
path = None
else:
# original: path = [os.path.realpath(self.path)]
path = [self.path]
try:
file, filename, etc = pkgutil.imp.find_module(subname, path)
except ImportError:
return None
return pkgutil.ImpLoader(fullname, file, filename, etc)
old_find_module = pkgutil.ImpImporter.find_module
pkgutil.ImpImporter.find_module = find_module_patched
try:
yield
finally:
pkgutil.ImpImporter.find_module = old_find_module
else:
yield
class FSHookProxy(object): class FSHookProxy(object):
def __init__(self, fspath, pm, remove_mods): def __init__(self, fspath, pm, remove_mods):
self.fspath = fspath self.fspath = fspath
@ -662,23 +621,14 @@ class Session(nodes.FSCollector):
ihook.pytest_collect_directory(path=dirpath, parent=self) ihook.pytest_collect_directory(path=dirpath, parent=self)
return True return True
if six.PY2: @staticmethod
def _visit_filter(f):
@staticmethod return f.check(file=1)
def _visit_filter(f):
return f.check(file=1) and not f.strpath.endswith("*.pyc")
else:
@staticmethod
def _visit_filter(f):
return f.check(file=1)
def _tryconvertpyarg(self, x): def _tryconvertpyarg(self, x):
"""Convert a dotted module name to path.""" """Convert a dotted module name to path."""
try: try:
with _patched_find_module(): loader = pkgutil.find_loader(x)
loader = pkgutil.find_loader(x)
except ImportError: except ImportError:
return x return x
if loader is None: if loader is None:
@ -686,8 +636,7 @@ class Session(nodes.FSCollector):
# This method is sometimes invoked when AssertionRewritingHook, which # This method is sometimes invoked when AssertionRewritingHook, which
# does not define a get_filename method, is already in place: # does not define a get_filename method, is already in place:
try: try:
with _patched_find_module(): path = loader.get_filename(x)
path = loader.get_filename(x)
except AttributeError: except AttributeError:
# Retrieve path from AssertionRewritingHook: # Retrieve path from AssertionRewritingHook:
path = loader.modules[x][0].co_filename path = loader.modules[x][0].co_filename

View File

@ -2,6 +2,7 @@
import inspect import inspect
import warnings import warnings
from collections import namedtuple from collections import namedtuple
from collections.abc import MutableMapping
from operator import attrgetter from operator import attrgetter
import attr import attr
@ -9,7 +10,6 @@ import six
from ..compat import ascii_escaped from ..compat import ascii_escaped
from ..compat import getfslineno from ..compat import getfslineno
from ..compat import MappingMixin
from ..compat import NOTSET from ..compat import NOTSET
from _pytest.deprecated import PYTEST_PARAM_UNKNOWN_KWARGS from _pytest.deprecated import PYTEST_PARAM_UNKNOWN_KWARGS
from _pytest.outcomes import fail from _pytest.outcomes import fail
@ -343,7 +343,7 @@ class MarkGenerator(object):
MARK_GEN = MarkGenerator() MARK_GEN = MarkGenerator()
class NodeKeywords(MappingMixin): class NodeKeywords(MutableMapping):
def __init__(self, node): def __init__(self, node):
self.node = node self.node = node
self.parent = node.parent self.parent = node.parent

View File

@ -222,15 +222,6 @@ class MonkeyPatch(object):
self._setitem.append((dic, name, dic.get(name, notset))) self._setitem.append((dic, name, dic.get(name, notset)))
del dic[name] del dic[name]
def _warn_if_env_name_is_not_str(self, name):
"""On Python 2, warn if the given environment variable name is not a native str (#4056)"""
if six.PY2 and not isinstance(name, str):
warnings.warn(
pytest.PytestWarning(
"Environment variable name {!r} should be str".format(name)
)
)
def setenv(self, name, value, prepend=None): def setenv(self, name, value, prepend=None):
""" Set environment variable ``name`` to ``value``. If ``prepend`` """ Set environment variable ``name`` to ``value``. If ``prepend``
is a character, read the current environment variable value is a character, read the current environment variable value
@ -248,7 +239,6 @@ class MonkeyPatch(object):
value = str(value) value = str(value)
if prepend and name in os.environ: if prepend and name in os.environ:
value = value + prepend + os.environ[name] value = value + prepend + os.environ[name]
self._warn_if_env_name_is_not_str(name)
self.setitem(os.environ, name, value) self.setitem(os.environ, name, value)
def delenv(self, name, raising=True): def delenv(self, name, raising=True):
@ -258,7 +248,6 @@ class MonkeyPatch(object):
If ``raising`` is set to False, no exception will be raised if the If ``raising`` is set to False, no exception will be raised if the
environment variable is missing. environment variable is missing.
""" """
self._warn_if_env_name_is_not_str(name)
self.delitem(os.environ, name, raising=raising) self.delitem(os.environ, name, raising=raising)
def syspath_prepend(self, path): def syspath_prepend(self, path):
@ -279,10 +268,9 @@ class MonkeyPatch(object):
# since then the mtime based FileFinder cache (that gets created in # since then the mtime based FileFinder cache (that gets created in
# this case already) gets not invalidated when writing the new files # this case already) gets not invalidated when writing the new files
# quickly afterwards. # quickly afterwards.
if sys.version_info >= (3, 3): from importlib import invalidate_caches
from importlib import invalidate_caches
invalidate_caches() invalidate_caches()
def chdir(self, path): def chdir(self, path):
""" Change the current working directory to the specified path. """ Change the current working directory to the specified path.

View File

@ -4,7 +4,6 @@ from __future__ import absolute_import
from __future__ import division from __future__ import division
from __future__ import print_function from __future__ import print_function
import sys
import tempfile import tempfile
import six import six
@ -70,18 +69,10 @@ def create_new_paste(contents):
:returns: url to the pasted contents :returns: url to the pasted contents
""" """
import re import re
from urllib.request import urlopen
from urllib.parse import urlencode
if sys.version_info < (3, 0): params = {"code": contents, "lexer": "python3", "expiry": "1week"}
from urllib import urlopen, urlencode
else:
from urllib.request import urlopen
from urllib.parse import urlencode
params = {
"code": contents,
"lexer": "python3" if sys.version_info[0] == 3 else "python",
"expiry": "1week",
}
url = "https://bpaste.net" url = "https://bpaste.net"
response = urlopen(url, data=urlencode(params).encode("ascii")).read() response = urlopen(url, data=urlencode(params).encode("ascii")).read()
m = re.search(r'href="/raw/(\w+)"', response.decode("utf-8")) m = re.search(r'href="/raw/(\w+)"', response.decode("utf-8"))

View File

@ -8,7 +8,6 @@ import os
import shutil import shutil
import sys import sys
import uuid import uuid
from functools import reduce
from os.path import expanduser from os.path import expanduser
from os.path import expandvars from os.path import expandvars
from os.path import isabs from os.path import isabs
@ -18,9 +17,8 @@ from posixpath import sep as posix_sep
import six import six
from six.moves import map from six.moves import map
from .compat import PY36
if PY36: if sys.version_info[:2] >= (3, 6):
from pathlib import Path, PurePath from pathlib import Path, PurePath
else: else:
from pathlib2 import Path, PurePath from pathlib2 import Path, PurePath
@ -84,17 +82,6 @@ def parse_num(maybe_num):
return -1 return -1
if six.PY2:
def _max(iterable, default):
"""needed due to python2.7 lacking the default argument for max"""
return reduce(max, iterable, default)
else:
_max = max
def _force_symlink(root, target, link_to): def _force_symlink(root, target, link_to):
"""helper to create the current symlink """helper to create the current symlink
@ -119,7 +106,7 @@ def make_numbered_dir(root, prefix):
"""create a directory with an increased number as suffix for the given prefix""" """create a directory with an increased number as suffix for the given prefix"""
for i in range(10): for i in range(10):
# try up to 10 times to create the folder # try up to 10 times to create the folder
max_existing = _max(map(parse_num, find_suffixes(root, prefix)), default=-1) max_existing = max(map(parse_num, find_suffixes(root, prefix)), default=-1)
new_number = max_existing + 1 new_number = max_existing + 1
new_path = root.joinpath("{}{}".format(prefix, new_number)) new_path = root.joinpath("{}{}".format(prefix, new_number))
try: try:
@ -230,7 +217,7 @@ def try_cleanup(path, consider_lock_dead_if_created_before):
def cleanup_candidates(root, prefix, keep): def cleanup_candidates(root, prefix, keep):
"""lists candidates for numbered directories to be removed - follows py.path""" """lists candidates for numbered directories to be removed - follows py.path"""
max_existing = _max(map(parse_num, find_suffixes(root, prefix)), default=-1) max_existing = max(map(parse_num, find_suffixes(root, prefix)), default=-1)
max_delete = max_existing - keep max_delete = max_existing - keep
paths = find_prefixed(root, prefix) paths = find_prefixed(root, prefix)
paths, paths2 = itertools.tee(paths) paths, paths2 = itertools.tee(paths)

View File

@ -13,6 +13,7 @@ import subprocess
import sys import sys
import time import time
import traceback import traceback
from collections.abc import Sequence
from fnmatch import fnmatch from fnmatch import fnmatch
from weakref import WeakKeyDictionary from weakref import WeakKeyDictionary
@ -25,8 +26,6 @@ from _pytest._io.saferepr import saferepr
from _pytest.assertion.rewrite import AssertionRewritingHook from _pytest.assertion.rewrite import AssertionRewritingHook
from _pytest.capture import MultiCapture from _pytest.capture import MultiCapture
from _pytest.capture import SysCapture from _pytest.capture import SysCapture
from _pytest.compat import safe_str
from _pytest.compat import Sequence
from _pytest.main import EXIT_INTERRUPTED from _pytest.main import EXIT_INTERRUPTED
from _pytest.main import EXIT_OK from _pytest.main import EXIT_OK
from _pytest.main import Session from _pytest.main import Session
@ -911,7 +910,7 @@ class Testdir(object):
def _ensure_basetemp(self, args): def _ensure_basetemp(self, args):
args = list(args) args = list(args)
for x in args: for x in args:
if safe_str(x).startswith("--basetemp"): if str(x).startswith("--basetemp"):
break break
else: else:
args.append("--basetemp=%s" % self.tmpdir.dirpath("basetemp")) args.append("--basetemp=%s" % self.tmpdir.dirpath("basetemp"))
@ -1124,25 +1123,11 @@ class Testdir(object):
if timeout is None: if timeout is None:
ret = popen.wait() ret = popen.wait()
elif six.PY3: else:
try: try:
ret = popen.wait(timeout) ret = popen.wait(timeout)
except subprocess.TimeoutExpired: except subprocess.TimeoutExpired:
handle_timeout() handle_timeout()
else:
end = time.time() + timeout
resolution = min(0.1, timeout / 10)
while True:
ret = popen.poll()
if ret is not None:
break
if time.time() > end:
handle_timeout()
time.sleep(resolution)
finally: finally:
f1.close() f1.close()
f2.close() f2.close()

View File

@ -5,6 +5,7 @@ from __future__ import division
from __future__ import print_function from __future__ import print_function
import collections import collections
import enum
import fnmatch import fnmatch
import inspect import inspect
import os import os
@ -22,7 +23,6 @@ from _pytest import fixtures
from _pytest import nodes from _pytest import nodes
from _pytest._code import filter_traceback from _pytest._code import filter_traceback
from _pytest.compat import ascii_escaped from _pytest.compat import ascii_escaped
from _pytest.compat import enum
from _pytest.compat import get_default_arg_names from _pytest.compat import get_default_arg_names
from _pytest.compat import get_real_func from _pytest.compat import get_real_func
from _pytest.compat import getfslineno from _pytest.compat import getfslineno
@ -35,7 +35,6 @@ from _pytest.compat import NOTSET
from _pytest.compat import REGEX_TYPE from _pytest.compat import REGEX_TYPE
from _pytest.compat import safe_getattr from _pytest.compat import safe_getattr
from _pytest.compat import safe_isclass from _pytest.compat import safe_isclass
from _pytest.compat import safe_str
from _pytest.compat import STRING_TYPES from _pytest.compat import STRING_TYPES
from _pytest.config import hookimpl from _pytest.config import hookimpl
from _pytest.main import FSHookProxy from _pytest.main import FSHookProxy
@ -531,7 +530,7 @@ class Module(nodes.File, PyCollector):
if exc_info.traceback if exc_info.traceback
else exc_info.exconly() else exc_info.exconly()
) )
formatted_tb = safe_str(exc_repr) formatted_tb = str(exc_repr)
raise self.CollectError( raise self.CollectError(
"ImportError while importing test module '{fspath}'.\n" "ImportError while importing test module '{fspath}'.\n"
"Hint: make sure your test modules/packages have valid Python names.\n" "Hint: make sure your test modules/packages have valid Python names.\n"

View File

@ -5,6 +5,9 @@ import math
import pprint import pprint
import sys import sys
import warnings import warnings
from collections.abc import Iterable
from collections.abc import Mapping
from collections.abc import Sized
from decimal import Decimal from decimal import Decimal
from numbers import Number from numbers import Number
@ -15,9 +18,6 @@ from six.moves import zip
import _pytest._code import _pytest._code
from _pytest import deprecated from _pytest import deprecated
from _pytest.compat import isclass from _pytest.compat import isclass
from _pytest.compat import Iterable
from _pytest.compat import Mapping
from _pytest.compat import Sized
from _pytest.compat import STRING_TYPES from _pytest.compat import STRING_TYPES
from _pytest.outcomes import fail from _pytest.outcomes import fail
@ -81,9 +81,6 @@ class ApproxBase(object):
def __ne__(self, actual): def __ne__(self, actual):
return not (actual == self) return not (actual == self)
if sys.version_info[0] == 2:
__cmp__ = _cmp_raises_type_error
def _approx_scalar(self, x): def _approx_scalar(self, x):
return ApproxScalar(x, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok) return ApproxScalar(x, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok)
@ -122,9 +119,6 @@ class ApproxNumpy(ApproxBase):
list_scalars = _recursive_list_map(self._approx_scalar, self.expected.tolist()) list_scalars = _recursive_list_map(self._approx_scalar, self.expected.tolist())
return "approx({!r})".format(list_scalars) return "approx({!r})".format(list_scalars)
if sys.version_info[0] == 2:
__cmp__ = _cmp_raises_type_error
def __eq__(self, actual): def __eq__(self, actual):
import numpy as np import numpy as np
@ -251,10 +245,7 @@ class ApproxScalar(ApproxBase):
except ValueError: except ValueError:
vetted_tolerance = "???" vetted_tolerance = "???"
if sys.version_info[0] == 2: return "{} \u00b1 {}".format(self.expected, vetted_tolerance)
return "{} +- {}".format(self.expected, vetted_tolerance)
else:
return u"{} \u00b1 {}".format(self.expected, vetted_tolerance)
def __eq__(self, actual): def __eq__(self, actual):
""" """
@ -736,8 +727,6 @@ class RaisesContext(object):
fail(self.message) fail(self.message)
self.excinfo.__init__(tp) self.excinfo.__init__(tp)
suppress_exception = issubclass(self.excinfo.type, self.expected_exception) suppress_exception = issubclass(self.excinfo.type, self.expected_exception)
if sys.version_info[0] == 2 and suppress_exception:
sys.exc_clear()
if self.match_expr is not None and suppress_exception: if self.match_expr is not None and suppress_exception:
self.excinfo.match(self.match_expr) self.excinfo.match(self.match_expr)
return suppress_exception return suppress_exception

View File

@ -9,8 +9,6 @@ import re
import sys import sys
import warnings import warnings
import six
import _pytest._code import _pytest._code
from _pytest.deprecated import PYTEST_WARNS_UNKNOWN_KWARGS from _pytest.deprecated import PYTEST_WARNS_UNKNOWN_KWARGS
from _pytest.deprecated import WARNS_EXEC from _pytest.deprecated import WARNS_EXEC
@ -156,44 +154,13 @@ class WarningsRecorder(warnings.catch_warnings):
raise RuntimeError("Cannot enter %r twice" % self) raise RuntimeError("Cannot enter %r twice" % self)
self._list = super(WarningsRecorder, self).__enter__() self._list = super(WarningsRecorder, self).__enter__()
warnings.simplefilter("always") warnings.simplefilter("always")
# python3 keeps track of a "filter version", when the filters are
# updated previously seen warnings can be re-warned. python2 has no
# concept of this so we must reset the warnings registry manually.
# trivial patching of `warnings.warn` seems to be enough somehow?
if six.PY2:
def warn(message, category=None, stacklevel=1):
# duplicate the stdlib logic due to
# bad handing in the c version of warnings
if isinstance(message, Warning):
category = message.__class__
# Check category argument
if category is None:
category = UserWarning
assert issubclass(category, Warning)
# emulate resetting the warn registry
f_globals = sys._getframe(stacklevel).f_globals
if "__warningregistry__" in f_globals:
orig = f_globals["__warningregistry__"]
f_globals["__warningregistry__"] = None
try:
return self._saved_warn(message, category, stacklevel + 1)
finally:
f_globals["__warningregistry__"] = orig
else:
return self._saved_warn(message, category, stacklevel + 1)
warnings.warn, self._saved_warn = warn, warnings.warn
return self return self
def __exit__(self, *exc_info): def __exit__(self, *exc_info):
if not self._entered: if not self._entered:
__tracebackhide__ = True __tracebackhide__ = True
raise RuntimeError("Cannot exit %r without entering first" % self) raise RuntimeError("Cannot exit %r without entering first" % self)
# see above where `self._saved_warn` is assigned
if six.PY2:
warnings.warn = self._saved_warn
super(WarningsRecorder, self).__exit__(*exc_info) super(WarningsRecorder, self).__exit__(*exc_info)
# Built-in catch_warnings does not reset entered state so we do it # Built-in catch_warnings does not reset entered state so we do it

View File

@ -129,17 +129,13 @@ def pytest_runtest_makereport(item, call):
evalxfail = getattr(item, "_evalxfail", None) evalxfail = getattr(item, "_evalxfail", None)
# unitttest special case, see setting of _unexpectedsuccess # unitttest special case, see setting of _unexpectedsuccess
if hasattr(item, "_unexpectedsuccess") and rep.when == "call": if hasattr(item, "_unexpectedsuccess") and rep.when == "call":
from _pytest.compat import _is_unittest_unexpected_success_a_failure
if item._unexpectedsuccess: if item._unexpectedsuccess:
rep.longrepr = "Unexpected success: {}".format(item._unexpectedsuccess) rep.longrepr = "Unexpected success: {}".format(item._unexpectedsuccess)
else: else:
rep.longrepr = "Unexpected success" rep.longrepr = "Unexpected success"
if _is_unittest_unexpected_success_a_failure(): rep.outcome = "failed"
rep.outcome = "failed"
else:
rep.outcome = "passed"
rep.wasxfail = rep.longrepr
elif item.config.option.runxfail: elif item.config.option.runxfail:
pass # don't interefere pass # don't interefere
elif call.excinfo and call.excinfo.errisinstance(xfail.Exception): elif call.excinfo and call.excinfo.errisinstance(xfail.Exception):

View File

@ -992,21 +992,6 @@ def _get_line_with_reprcrash_message(config, rep, termwidth):
msg = msg[:max_len_msg] msg = msg[:max_len_msg]
while wcswidth(msg) > max_len_msg: while wcswidth(msg) > max_len_msg:
msg = msg[:-1] msg = msg[:-1]
if six.PY2:
# on python 2 systems with narrow unicode compilation, trying to
# get a single character out of a multi-byte unicode character such as
# u'😄' will result in a High Surrogate (U+D83D) character, which is
# rendered as u'<27>'; in this case we just strip that character out as it
# serves no purpose being rendered
try:
surrogate = six.unichr(0xD83D)
msg = msg.rstrip(surrogate)
except ValueError: # pragma: no cover
# Jython cannot represent this lone surrogate at all (#5256):
# ValueError: unichr() arg is a lone surrogate in range
# (0xD800, 0xDFFF) (Jython UTF-16 encoding)
# ignore this case as it shouldn't appear in the string anyway
pass
msg += ellipsis msg += ellipsis
line += sep + msg line += sep + msg
return line return line

View File

@ -11,7 +11,6 @@ import warnings
import attr import attr
import py import py
import six
import pytest import pytest
from .pathlib import ensure_reset_dir from .pathlib import ensure_reset_dir
@ -32,9 +31,7 @@ class TempPathFactory(object):
# using os.path.abspath() to get absolute path instead of resolve() as it # using os.path.abspath() to get absolute path instead of resolve() as it
# does not work the same in all platforms (see #4427) # does not work the same in all platforms (see #4427)
# Path.absolute() exists, but it is not public (see https://bugs.python.org/issue25012) # Path.absolute() exists, but it is not public (see https://bugs.python.org/issue25012)
converter=attr.converters.optional( converter=attr.converters.optional(lambda p: Path(os.path.abspath(str(p))))
lambda p: Path(os.path.abspath(six.text_type(p)))
)
) )
_trace = attr.ib() _trace = attr.ib()
_basetemp = attr.ib(default=None) _basetemp = attr.ib(default=None)

View File

@ -113,23 +113,9 @@ class TestCaseFunction(Function):
def setup(self): def setup(self):
self._testcase = self.parent.obj(self.name) self._testcase = self.parent.obj(self.name)
self._fix_unittest_skip_decorator()
if hasattr(self, "_request"): if hasattr(self, "_request"):
self._request._fillfixtures() self._request._fillfixtures()
def _fix_unittest_skip_decorator(self):
"""
The @unittest.skip decorator calls functools.wraps(self._testcase)
The call to functools.wraps() fails unless self._testcase
has a __name__ attribute. This is usually automatically supplied
if the test is a function or method, but we need to add manually
here.
See issue #1169
"""
if sys.version_info[0] == 2:
setattr(self._testcase, "__name__", self.name)
def teardown(self): def teardown(self):
self._testcase = None self._testcase = None
@ -208,12 +194,7 @@ class TestCaseFunction(Function):
skip_why = getattr( skip_why = getattr(
self._testcase.__class__, "__unittest_skip_why__", "" self._testcase.__class__, "__unittest_skip_why__", ""
) or getattr(testMethod, "__unittest_skip_why__", "") ) or getattr(testMethod, "__unittest_skip_why__", "")
try: # PY3, unittest2 on PY2 self._testcase._addSkip(self, self._testcase, skip_why)
self._testcase._addSkip(self, self._testcase, skip_why)
except TypeError: # PY2
if sys.version_info[0] != 2:
raise
self._testcase._addSkip(self, skip_why)
return True return True
return False return False

View File

@ -8,7 +8,6 @@ import warnings
from contextlib import contextmanager from contextlib import contextmanager
import pytest import pytest
from _pytest import compat
SHOW_PYTEST_WARNINGS_ARG = "-Walways::pytest.RemovedInPytest4Warning" SHOW_PYTEST_WARNINGS_ARG = "-Walways::pytest.RemovedInPytest4Warning"
@ -104,22 +103,8 @@ def catch_warnings_for_item(config, ihook, when, item):
def warning_record_to_str(warning_message): def warning_record_to_str(warning_message):
"""Convert a warnings.WarningMessage to a string. """Convert a warnings.WarningMessage to a string."""
This takes lot of unicode shenaningans into account for Python 2.
When Python 2 support is dropped this function can be greatly simplified.
"""
warn_msg = warning_message.message warn_msg = warning_message.message
unicode_warning = False
if compat._PY2 and any(isinstance(m, compat.UNICODE_TYPES) for m in warn_msg.args):
new_args = []
for m in warn_msg.args:
new_args.append(
compat.ascii_escaped(m) if isinstance(m, compat.UNICODE_TYPES) else m
)
unicode_warning = list(warn_msg.args) != new_args
warn_msg.args = new_args
msg = warnings.formatwarning( msg = warnings.formatwarning(
warn_msg, warn_msg,
warning_message.category, warning_message.category,
@ -127,12 +112,6 @@ def warning_record_to_str(warning_message):
warning_message.lineno, warning_message.lineno,
warning_message.line, warning_message.line,
) )
if unicode_warning:
warnings.warn(
"Warning is using unicode non convertible to ascii, "
"converting to a safe representation:\n {!r}".format(compat.safe_str(msg)),
UnicodeWarning,
)
return msg return msg

View File

@ -176,7 +176,6 @@ class TestGeneralUsage(object):
result = testdir.runpytest(p) result = testdir.runpytest(p)
result.stdout.fnmatch_lines( result.stdout.fnmatch_lines(
[ [
# XXX on jython this fails: "> import import_fails",
"ImportError while importing test module*", "ImportError while importing test module*",
"*No module named *does_not_work*", "*No module named *does_not_work*",
] ]
@ -222,9 +221,7 @@ class TestGeneralUsage(object):
" foo()", " foo()",
"conftest.py:2: in foo", "conftest.py:2: in foo",
" import qwerty", " import qwerty",
"E {}: No module named {q}qwerty{q}".format( "E {}: No module named 'qwerty'".format(exc_name),
exc_name, q="'" if six.PY3 else ""
),
] ]
) )
@ -540,7 +537,6 @@ class TestInvocationVariants(object):
result = testdir.runpython(p) result = testdir.runpython(p)
assert result.ret == 0 assert result.ret == 0
@pytest.mark.xfail("sys.platform.startswith('java')")
def test_pydoc(self, testdir): def test_pydoc(self, testdir):
for name in ("py.test", "pytest"): for name in ("py.test", "pytest"):
result = testdir.runpython_c("import {};help({})".format(name, name)) result = testdir.runpython_c("import {};help({})".format(name, name))
@ -783,10 +779,7 @@ class TestInvocationVariants(object):
d_local = testdir.mkdir("local") d_local = testdir.mkdir("local")
symlink_location = os.path.join(str(d_local), "lib") symlink_location = os.path.join(str(d_local), "lib")
if six.PY2: os.symlink(str(d), symlink_location, target_is_directory=True)
os.symlink(str(d), symlink_location)
else:
os.symlink(str(d), symlink_location, target_is_directory=True)
# The structure of the test directory is now: # The structure of the test directory is now:
# . # .
@ -1185,9 +1178,6 @@ def test_usage_error_code(testdir):
assert result.ret == EXIT_USAGEERROR assert result.ret == EXIT_USAGEERROR
@pytest.mark.skipif(
sys.version_info[:2] < (3, 5), reason="async def syntax python 3.5+ only"
)
@pytest.mark.filterwarnings("default") @pytest.mark.filterwarnings("default")
def test_warn_on_async_function(testdir): def test_warn_on_async_function(testdir):
testdir.makepyfile( testdir.makepyfile(

View File

@ -4,6 +4,7 @@ from __future__ import division
from __future__ import print_function from __future__ import print_function
import sys import sys
from unittest import mock
from six import text_type from six import text_type
from test_excinfo import TWMock from test_excinfo import TWMock
@ -11,11 +12,6 @@ from test_excinfo import TWMock
import _pytest._code import _pytest._code
import pytest import pytest
try:
import mock
except ImportError:
import unittest.mock as mock
def test_ne(): def test_ne():
code1 = _pytest._code.Code(compile('foo = "bar"', "", "exec")) code1 = _pytest._code.Code(compile('foo = "bar"', "", "exec"))
@ -92,21 +88,6 @@ def test_unicode_handling():
excinfo = pytest.raises(Exception, f) excinfo = pytest.raises(Exception, f)
text_type(excinfo) text_type(excinfo)
if sys.version_info < (3,):
bytes(excinfo)
@pytest.mark.skipif(sys.version_info[0] >= 3, reason="python 2 only issue")
def test_unicode_handling_syntax_error():
value = u"ąć".encode("UTF-8")
def f():
raise SyntaxError("invalid syntax", (None, 1, 3, value))
excinfo = pytest.raises(Exception, f)
str(excinfo)
if sys.version_info[0] < 3:
text_type(excinfo)
def test_code_getargs(): def test_code_getargs():
@ -202,10 +183,8 @@ class TestReprFuncArgs(object):
r = ReprFuncArgs(args) r = ReprFuncArgs(args)
r.toterminal(tw) r.toterminal(tw)
if sys.version_info[0] >= 3:
assert ( assert (
tw.lines[0] tw.lines[0]
== r"unicode_string = São Paulo, utf8_string = b'S\xc3\xa3o Paulo'" == r"unicode_string = São Paulo, utf8_string = b'S\xc3\xa3o Paulo'"
) )
else:
assert tw.lines[0] == "unicode_string = São Paulo, utf8_string = São Paulo"

View File

@ -17,7 +17,7 @@ import pytest
from _pytest._code.code import ExceptionChainRepr from _pytest._code.code import ExceptionChainRepr
from _pytest._code.code import ExceptionInfo from _pytest._code.code import ExceptionInfo
from _pytest._code.code import FormattedExcinfo from _pytest._code.code import FormattedExcinfo
from _pytest._code.code import ReprExceptionInfo
try: try:
import importlib import importlib
@ -26,8 +26,6 @@ except ImportError:
else: else:
invalidate_import_caches = getattr(importlib, "invalidate_caches", None) invalidate_import_caches = getattr(importlib, "invalidate_caches", None)
failsonjython = pytest.mark.xfail("sys.platform.startswith('java')")
pytest_version_info = tuple(map(int, pytest.__version__.split(".")[:3])) pytest_version_info = tuple(map(int, pytest.__version__.split(".")[:3]))
@ -146,7 +144,6 @@ class TestTraceback_f_g_h(object):
assert s.startswith("def f():") assert s.startswith("def f():")
assert s.endswith("raise ValueError") assert s.endswith("raise ValueError")
@failsonjython
def test_traceback_entry_getsource_in_construct(self): def test_traceback_entry_getsource_in_construct(self):
source = _pytest._code.Source( source = _pytest._code.Source(
"""\ """\
@ -500,8 +497,7 @@ class TestFormattedExcinfo(object):
excinfo = _pytest._code.ExceptionInfo.from_current() excinfo = _pytest._code.ExceptionInfo.from_current()
repr = pr.repr_excinfo(excinfo) repr = pr.repr_excinfo(excinfo)
assert repr.reprtraceback.reprentries[1].lines[0] == "> ???" assert repr.reprtraceback.reprentries[1].lines[0] == "> ???"
if sys.version_info[0] >= 3: assert repr.chain[0][0].reprentries[1].lines[0] == "> ???"
assert repr.chain[0][0].reprentries[1].lines[0] == "> ???"
def test_repr_many_line_source_not_existing(self): def test_repr_many_line_source_not_existing(self):
pr = FormattedExcinfo() pr = FormattedExcinfo()
@ -519,8 +515,7 @@ raise ValueError()
excinfo = _pytest._code.ExceptionInfo.from_current() excinfo = _pytest._code.ExceptionInfo.from_current()
repr = pr.repr_excinfo(excinfo) repr = pr.repr_excinfo(excinfo)
assert repr.reprtraceback.reprentries[1].lines[0] == "> ???" assert repr.reprtraceback.reprentries[1].lines[0] == "> ???"
if sys.version_info[0] >= 3: assert repr.chain[0][0].reprentries[1].lines[0] == "> ???"
assert repr.chain[0][0].reprentries[1].lines[0] == "> ???"
def test_repr_source_failing_fullsource(self): def test_repr_source_failing_fullsource(self):
pr = FormattedExcinfo() pr = FormattedExcinfo()
@ -577,14 +572,12 @@ raise ValueError()
fail = IOError() fail = IOError()
repr = pr.repr_excinfo(excinfo) repr = pr.repr_excinfo(excinfo)
assert repr.reprtraceback.reprentries[0].lines[0] == "> ???" assert repr.reprtraceback.reprentries[0].lines[0] == "> ???"
if sys.version_info[0] >= 3: assert repr.chain[0][0].reprentries[0].lines[0] == "> ???"
assert repr.chain[0][0].reprentries[0].lines[0] == "> ???"
fail = py.error.ENOENT # noqa fail = py.error.ENOENT # noqa
repr = pr.repr_excinfo(excinfo) repr = pr.repr_excinfo(excinfo)
assert repr.reprtraceback.reprentries[0].lines[0] == "> ???" assert repr.reprtraceback.reprentries[0].lines[0] == "> ???"
if sys.version_info[0] >= 3: assert repr.chain[0][0].reprentries[0].lines[0] == "> ???"
assert repr.chain[0][0].reprentries[0].lines[0] == "> ???"
def test_repr_local(self): def test_repr_local(self):
p = FormattedExcinfo(showlocals=True) p = FormattedExcinfo(showlocals=True)
@ -828,9 +821,9 @@ raise ValueError()
repr = p.repr_excinfo(excinfo) repr = p.repr_excinfo(excinfo)
assert repr.reprtraceback assert repr.reprtraceback
assert len(repr.reprtraceback.reprentries) == len(reprtb.reprentries) assert len(repr.reprtraceback.reprentries) == len(reprtb.reprentries)
if sys.version_info[0] >= 3:
assert repr.chain[0][0] assert repr.chain[0][0]
assert len(repr.chain[0][0].reprentries) == len(reprtb.reprentries) assert len(repr.chain[0][0].reprentries) == len(reprtb.reprentries)
assert repr.reprcrash.path.endswith("mod.py") assert repr.reprcrash.path.endswith("mod.py")
assert repr.reprcrash.message == "ValueError: 0" assert repr.reprcrash.message == "ValueError: 0"
@ -916,13 +909,11 @@ raise ValueError()
for style in ("short", "long", "no"): for style in ("short", "long", "no"):
for showlocals in (True, False): for showlocals in (True, False):
repr = excinfo.getrepr(style=style, showlocals=showlocals) repr = excinfo.getrepr(style=style, showlocals=showlocals)
if sys.version_info[0] < 3:
assert isinstance(repr, ReprExceptionInfo)
assert repr.reprtraceback.style == style assert repr.reprtraceback.style == style
if sys.version_info[0] >= 3:
assert isinstance(repr, ExceptionChainRepr) assert isinstance(repr, ExceptionChainRepr)
for repr in repr.chain: for repr in repr.chain:
assert repr[0].style == style assert repr[0].style == style
def test_reprexcinfo_unicode(self): def test_reprexcinfo_unicode(self):
from _pytest._code.code import TerminalRepr from _pytest._code.code import TerminalRepr
@ -1133,7 +1124,6 @@ raise ValueError()
msg.endswith("mod.py") msg.endswith("mod.py")
assert tw.lines[20] == ":9: ValueError" assert tw.lines[20] == ":9: ValueError"
@pytest.mark.skipif("sys.version_info[0] < 3")
def test_exc_chain_repr(self, importasmod): def test_exc_chain_repr(self, importasmod):
mod = importasmod( mod = importasmod(
""" """
@ -1219,7 +1209,6 @@ raise ValueError()
assert line.endswith("mod.py") assert line.endswith("mod.py")
assert tw.lines[47] == ":15: AttributeError" assert tw.lines[47] == ":15: AttributeError"
@pytest.mark.skipif("sys.version_info[0] < 3")
@pytest.mark.parametrize("mode", ["from_none", "explicit_suppress"]) @pytest.mark.parametrize("mode", ["from_none", "explicit_suppress"])
def test_exc_repr_chain_suppression(self, importasmod, mode): def test_exc_repr_chain_suppression(self, importasmod, mode):
"""Check that exc repr does not show chained exceptions in Python 3. """Check that exc repr does not show chained exceptions in Python 3.
@ -1261,7 +1250,6 @@ raise ValueError()
assert tw.lines[9] == ":6: AttributeError" assert tw.lines[9] == ":6: AttributeError"
assert len(tw.lines) == 10 assert len(tw.lines) == 10
@pytest.mark.skipif("sys.version_info[0] < 3")
@pytest.mark.parametrize( @pytest.mark.parametrize(
"reason, description", "reason, description",
[ [
@ -1321,7 +1309,6 @@ raise ValueError()
] ]
) )
@pytest.mark.skipif("sys.version_info[0] < 3")
def test_exc_chain_repr_cycle(self, importasmod): def test_exc_chain_repr_cycle(self, importasmod):
mod = importasmod( mod = importasmod(
""" """

View File

@ -16,8 +16,6 @@ import _pytest._code
import pytest import pytest
from _pytest._code import Source from _pytest._code import Source
failsonjython = pytest.mark.xfail("sys.platform.startswith('java')")
def test_source_str_function(): def test_source_str_function():
x = Source("3") x = Source("3")
@ -122,7 +120,7 @@ def test_source_strip_multiline():
def test_syntaxerror_rerepresentation(): def test_syntaxerror_rerepresentation():
ex = pytest.raises(SyntaxError, _pytest._code.compile, "xyz xyz") ex = pytest.raises(SyntaxError, _pytest._code.compile, "xyz xyz")
assert ex.value.lineno == 1 assert ex.value.lineno == 1
assert ex.value.offset in (4, 5, 7) # XXX pypy/jython versus cpython? assert ex.value.offset == 7
assert ex.value.text.strip(), "x x" assert ex.value.text.strip(), "x x"

View File

@ -1,10 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""Reproduces issue #3774""" """Reproduces issue #3774"""
from unittest import mock
try:
import mock
except ImportError:
import unittest.mock as mock
import pytest import pytest

View File

@ -1,7 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import doctest import doctest
import operator import operator
import sys
from decimal import Decimal from decimal import Decimal
from fractions import Fraction from fractions import Fraction
from operator import eq from operator import eq
@ -28,7 +27,7 @@ class MyDocTestRunner(doctest.DocTestRunner):
class TestApprox(object): class TestApprox(object):
@pytest.fixture @pytest.fixture
def plus_minus(self): def plus_minus(self):
return u"\u00b1" if sys.version_info[0] > 2 else u"+-" return u"\u00b1"
def test_repr_string(self, plus_minus): def test_repr_string(self, plus_minus):
tol1, tol2, infr = "1.0e-06", "2.0e-06", "inf" tol1, tol2, infr = "1.0e-06", "2.0e-06", "inf"

View File

@ -679,8 +679,6 @@ class TestSorting(object):
assert fn1 == fn2 assert fn1 == fn2
assert fn1 != modcol assert fn1 != modcol
if sys.version_info < (3, 0):
assert cmp(fn1, fn2) == 0 # NOQA
assert hash(fn1) == hash(fn2) assert hash(fn1) == hash(fn2)
fn3 = testdir.collect_by_name(modcol, "test_fail") fn3 = testdir.collect_by_name(modcol, "test_fail")

View File

@ -13,8 +13,6 @@ from _pytest import fixtures
from _pytest import python from _pytest import python
from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG
PY3 = sys.version_info >= (3, 0)
class TestMetafunc(object): class TestMetafunc(object):
def Metafunc(self, func, config=None): def Metafunc(self, func, config=None):

View File

@ -1,8 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import sys import sys
import six
import pytest import pytest
from _pytest.outcomes import Failed from _pytest.outcomes import Failed
from _pytest.warning_types import PytestDeprecationWarning from _pytest.warning_types import PytestDeprecationWarning
@ -265,16 +263,7 @@ class TestRaises(object):
def __class__(self): def __class__(self):
assert False, "via __class__" assert False, "via __class__"
if six.PY2: with pytest.raises(AssertionError) as excinfo:
with pytest.raises(pytest.fail.Exception) as excinfo: with pytest.raises(CrappyClass()):
with pytest.raises(CrappyClass()): pass
pass assert "via __class__" in excinfo.value.args[0]
assert "DID NOT RAISE" in excinfo.value.args[0]
with pytest.raises(CrappyClass) as excinfo:
raise CrappyClass()
else:
with pytest.raises(AssertionError) as excinfo:
with pytest.raises(CrappyClass()):
pass
assert "via __class__" in excinfo.value.args[0]

View File

@ -30,19 +30,7 @@ def equal_with_bash(prefix, ffc, fc, out=None):
def _wrapcall(*args, **kargs): def _wrapcall(*args, **kargs):
try: try:
if sys.version_info > (2, 7): return subprocess.check_output(*args, **kargs).decode().splitlines()
return subprocess.check_output(*args, **kargs).decode().splitlines()
if "stdout" in kargs:
raise ValueError("stdout argument not allowed, it will be overridden.")
process = subprocess.Popen(stdout=subprocess.PIPE, *args, **kargs)
output, unused_err = process.communicate()
retcode = process.poll()
if retcode:
cmd = kargs.get("args")
if cmd is None:
cmd = args[0]
raise subprocess.CalledProcessError(retcode, cmd)
return output.decode().splitlines()
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
return [] return []

View File

@ -3,6 +3,7 @@ from __future__ import absolute_import
from __future__ import division from __future__ import division
from __future__ import print_function from __future__ import print_function
import collections.abc as collections_abc
import sys import sys
import textwrap import textwrap
@ -15,8 +16,6 @@ from _pytest import outcomes
from _pytest.assertion import truncate from _pytest.assertion import truncate
from _pytest.assertion import util from _pytest.assertion import util
PY3 = sys.version_info >= (3, 0)
def mock_config(): def mock_config():
class Config(object): class Config(object):
@ -372,14 +371,6 @@ class TestAssert_reprcompare(object):
{0, 2}, {0, 2},
""" """
Full diff: Full diff:
- set([0, 1])
? ^
+ set([0, 2])
? ^
"""
if not PY3
else """
Full diff:
- {0, 1} - {0, 1}
? ^ ? ^
+ {0, 2} + {0, 2}
@ -483,10 +474,7 @@ class TestAssert_reprcompare(object):
assert len(expl) > 1 assert len(expl) > 1
def test_Sequence(self): def test_Sequence(self):
if sys.version_info >= (3, 3):
import collections.abc as collections_abc
else:
import collections as collections_abc
if not hasattr(collections_abc, "MutableSequence"): if not hasattr(collections_abc, "MutableSequence"):
pytest.skip("cannot import MutableSequence") pytest.skip("cannot import MutableSequence")
MutableSequence = collections_abc.MutableSequence MutableSequence = collections_abc.MutableSequence
@ -589,10 +577,7 @@ class TestAssert_reprcompare(object):
return "\xff" return "\xff"
expl = callequal(A(), "1") expl = callequal(A(), "1")
if PY3: assert expl == ["ÿ == '1'", "+ 1"]
assert expl == ["ÿ == '1'", "+ 1"]
else:
assert expl == [u"\ufffd == '1'", u"+ 1"]
def test_format_nonascii_explanation(self): def test_format_nonascii_explanation(self):
assert util.format_explanation("λ") assert util.format_explanation("λ")
@ -1100,10 +1085,6 @@ def test_traceback_failure(testdir):
) )
@pytest.mark.skipif(
sys.version_info[:2] <= (3, 3),
reason="Python 3.4+ shows chained exceptions on multiprocess",
)
def test_exception_handling_no_traceback(testdir): def test_exception_handling_no_traceback(testdir):
""" """
Handle chain exceptions in tasks submitted by the multiprocess module (#1984). Handle chain exceptions in tasks submitted by the multiprocess module (#1984).
@ -1137,9 +1118,7 @@ def test_exception_handling_no_traceback(testdir):
) )
@pytest.mark.skipif( @pytest.mark.skipif("'__pypy__' in sys.builtin_module_names")
"'__pypy__' in sys.builtin_module_names or sys.platform.startswith('java')"
)
def test_warn_missing(testdir): def test_warn_missing(testdir):
testdir.makepyfile("") testdir.makepyfile("")
result = testdir.run(sys.executable, "-OO", "-m", "pytest", "-h") result = testdir.run(sys.executable, "-OO", "-m", "pytest", "-h")
@ -1187,45 +1166,6 @@ def test_AssertionError_message(testdir):
) )
@pytest.mark.skipif(PY3, reason="This bug does not exist on PY3")
def test_set_with_unsortable_elements():
# issue #718
class UnsortableKey(object):
def __init__(self, name):
self.name = name
def __lt__(self, other):
raise RuntimeError()
def __repr__(self):
return "repr({})".format(self.name)
def __eq__(self, other):
return self.name == other.name
def __hash__(self):
return hash(self.name)
left_set = {UnsortableKey(str(i)) for i in range(1, 3)}
right_set = {UnsortableKey(str(i)) for i in range(2, 4)}
expl = callequal(left_set, right_set, verbose=True)
# skip first line because it contains the "construction" of the set, which does not have a guaranteed order
expl = expl[1:]
dedent = textwrap.dedent(
"""
Extra items in the left set:
repr(1)
Extra items in the right set:
repr(3)
Full diff (fallback to calling repr on each item):
- repr(1)
repr(2)
+ repr(3)
"""
).strip()
assert "\n".join(expl) == dedent
def test_diff_newline_at_end(monkeypatch, testdir): def test_diff_newline_at_end(monkeypatch, testdir):
testdir.makepyfile( testdir.makepyfile(
r""" r"""

View File

@ -3,6 +3,7 @@ from __future__ import absolute_import
from __future__ import division from __future__ import division
from __future__ import print_function from __future__ import print_function
import ast
import glob import glob
import os import os
import py_compile import py_compile
@ -22,11 +23,6 @@ from _pytest.assertion.rewrite import PYTEST_TAG
from _pytest.assertion.rewrite import rewrite_asserts from _pytest.assertion.rewrite import rewrite_asserts
from _pytest.main import EXIT_NOTESTSCOLLECTED from _pytest.main import EXIT_NOTESTSCOLLECTED
ast = pytest.importorskip("ast")
if sys.platform.startswith("java"):
# XXX should be xfail
pytest.skip("assert rewrite does currently not work on jython")
def setup_module(mod): def setup_module(mod):
mod._old_reprcompare = util._reprcompare mod._old_reprcompare = util._reprcompare
@ -166,18 +162,12 @@ class TestAssertionRewrite(object):
msg = getmsg(f, {"cls": X}).splitlines() msg = getmsg(f, {"cls": X}).splitlines()
if verbose > 0: if verbose > 0:
if six.PY2:
assert msg == [ assert msg == [
"assert <class 'test_assertrewrite.X'> == 42", "assert <class 'test_...e.<locals>.X'> == 42",
" -<class 'test_assertrewrite.X'>", " -<class 'test_assertrewrite.TestAssertionRewrite.test_name.<locals>.X'>",
" +42", " +42",
] ]
else:
assert msg == [
"assert <class 'test_...e.<locals>.X'> == 42",
" -<class 'test_assertrewrite.TestAssertionRewrite.test_name.<locals>.X'>",
" +42",
]
else: else:
assert msg == ["assert cls == 42"] assert msg == ["assert cls == 42"]
@ -277,9 +267,6 @@ class TestAssertionRewrite(object):
["*AssertionError: To be escaped: %", "*assert 1 == 2"] ["*AssertionError: To be escaped: %", "*assert 1 == 2"]
) )
@pytest.mark.skipif(
sys.version_info < (3,), reason="bytes is a string type in python 2"
)
def test_assertion_messages_bytes(self, testdir): def test_assertion_messages_bytes(self, testdir):
testdir.makepyfile("def test_bytes_assertion():\n assert False, b'ohai!'\n") testdir.makepyfile("def test_bytes_assertion():\n assert False, b'ohai!'\n")
result = testdir.runpytest() result = testdir.runpytest()
@ -426,7 +413,6 @@ class TestAssertionRewrite(object):
assert getmsg(f) == "assert (False or (4 % 2))" assert getmsg(f) == "assert (False or (4 % 2))"
@pytest.mark.skipif("sys.version_info < (3,5)")
def test_at_operator_issue1290(self, testdir): def test_at_operator_issue1290(self, testdir):
testdir.makepyfile( testdir.makepyfile(
""" """
@ -441,7 +427,6 @@ class TestAssertionRewrite(object):
) )
testdir.runpytest().assert_outcomes(passed=1) testdir.runpytest().assert_outcomes(passed=1)
@pytest.mark.skipif("sys.version_info < (3,5)")
def test_starred_with_side_effect(self, testdir): def test_starred_with_side_effect(self, testdir):
"""See #4412""" """See #4412"""
testdir.makepyfile( testdir.makepyfile(
@ -822,9 +807,6 @@ def test_rewritten():
assert testdir.runpytest_subprocess().ret == 0 assert testdir.runpytest_subprocess().ret == 0
def test_orphaned_pyc_file(self, testdir): 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( testdir.makepyfile(
""" """
import orphan import orphan
@ -890,10 +872,6 @@ def test_rewritten():
testdir.tmpdir.join("test_newlines.py").write(b, "wb") testdir.tmpdir.join("test_newlines.py").write(b, "wb")
assert testdir.runpytest().ret == 0 assert testdir.runpytest().ret == 0
@pytest.mark.skipif(
sys.version_info < (3, 4),
reason="packages without __init__.py not supported on python 2",
)
def test_package_without__init__py(self, testdir): def test_package_without__init__py(self, testdir):
pkg = testdir.mkdir("a_package_without_init_py") pkg = testdir.mkdir("a_package_without_init_py")
pkg.join("module.py").ensure() pkg.join("module.py").ensure()
@ -976,26 +954,6 @@ def test_rewritten():
result.stdout.fnmatch_lines(["*= 1 passed in *=*"]) result.stdout.fnmatch_lines(["*= 1 passed in *=*"])
assert "pytest-warning summary" not in result.stdout.str() assert "pytest-warning summary" not in result.stdout.str()
@pytest.mark.skipif(sys.version_info[0] > 2, reason="python 2 only")
def test_rewrite_future_imports(self, testdir):
"""Test that rewritten modules don't inherit the __future__ flags
from the assertrewrite module.
assertion.rewrite imports __future__.division (and others), so
ensure rewritten modules don't inherit those flags.
The test below will fail if __future__.division is enabled
"""
testdir.makepyfile(
"""
def test():
x = 1 / 2
assert type(x) is int
"""
)
result = testdir.runpytest()
assert result.ret == 0
class TestAssertionRewriteHookDetails(object): class TestAssertionRewriteHookDetails(object):
def test_loader_is_package_false_for_module(self, testdir): def test_loader_is_package_false_for_module(self, testdir):
@ -1025,48 +983,6 @@ class TestAssertionRewriteHookDetails(object):
result = testdir.runpytest() result = testdir.runpytest()
result.stdout.fnmatch_lines(["* 3 passed*"]) result.stdout.fnmatch_lines(["* 3 passed*"])
@pytest.mark.skipif("sys.version_info[0] >= 3")
@pytest.mark.xfail("hasattr(sys, 'pypy_translation_info')")
def test_assume_ascii(self, testdir):
content = "u'\xe2\x99\xa5\x01\xfe'"
testdir.tmpdir.join("test_encoding.py").write(content, "wb")
res = testdir.runpytest()
assert res.ret != 0
assert "SyntaxError: Non-ASCII character" in res.stdout.str()
@pytest.mark.skipif("sys.version_info[0] >= 3")
def test_detect_coding_cookie(self, testdir):
testdir.makepyfile(
test_cookie="""
# -*- coding: utf-8 -*-
u"St\xc3\xa4d"
def test_rewritten():
assert "@py_builtins" in globals()"""
)
assert testdir.runpytest().ret == 0
@pytest.mark.skipif("sys.version_info[0] >= 3")
def test_detect_coding_cookie_second_line(self, testdir):
testdir.makepyfile(
test_cookie="""
# -*- coding: utf-8 -*-
u"St\xc3\xa4d"
def test_rewritten():
assert "@py_builtins" in globals()"""
)
assert testdir.runpytest().ret == 0
@pytest.mark.skipif("sys.version_info[0] >= 3")
def test_detect_coding_cookie_crlf(self, testdir):
testdir.makepyfile(
test_cookie="""
# -*- coding: utf-8 -*-
u"St\xc3\xa4d"
def test_rewritten():
assert "@py_builtins" in globals()"""
)
assert testdir.runpytest().ret == 0
def test_sys_meta_path_munged(self, testdir): def test_sys_meta_path_munged(self, testdir):
testdir.makepyfile( testdir.makepyfile(
""" """

View File

@ -13,12 +13,10 @@ import textwrap
from io import UnsupportedOperation from io import UnsupportedOperation
import py import py
from six import text_type
import pytest import pytest
from _pytest import capture from _pytest import capture
from _pytest.capture import CaptureManager from _pytest.capture import CaptureManager
from _pytest.compat import _PY3
from _pytest.main import EXIT_NOTESTSCOLLECTED from _pytest.main import EXIT_NOTESTSCOLLECTED
# note: py.io capture tests where copied from # note: py.io capture tests where copied from
@ -100,10 +98,7 @@ class TestCaptureManager(object):
def test_capturing_unicode(testdir, method): def test_capturing_unicode(testdir, method):
if hasattr(sys, "pypy_version_info") and sys.pypy_version_info < (2, 2): if hasattr(sys, "pypy_version_info") and sys.pypy_version_info < (2, 2):
pytest.xfail("does not work on pypy < 2.2") pytest.xfail("does not work on pypy < 2.2")
if sys.version_info >= (3, 0): obj = "'b\u00f6y'"
obj = "'b\u00f6y'"
else:
obj = "u'\u00f6y'"
testdir.makepyfile( testdir.makepyfile(
""" """
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
@ -545,9 +540,6 @@ class TestCaptureFixture(object):
) )
reprec.assertoutcome(passed=1) reprec.assertoutcome(passed=1)
@pytest.mark.skipif(
sys.version_info < (3,), reason="only have capsysbinary in python 3"
)
def test_capsysbinary(self, testdir): def test_capsysbinary(self, testdir):
reprec = testdir.inline_runsource( reprec = testdir.inline_runsource(
"""\ """\
@ -562,25 +554,6 @@ class TestCaptureFixture(object):
) )
reprec.assertoutcome(passed=1) reprec.assertoutcome(passed=1)
@pytest.mark.skipif(
sys.version_info >= (3,), reason="only have capsysbinary in python 3"
)
def test_capsysbinary_forbidden_in_python2(self, testdir):
testdir.makepyfile(
"""\
def test_hello(capsysbinary):
pass
"""
)
result = testdir.runpytest()
result.stdout.fnmatch_lines(
[
"*test_hello*",
"*capsysbinary is only supported on Python 3*",
"*1 error in*",
]
)
def test_partial_setup_failure(self, testdir): def test_partial_setup_failure(self, testdir):
p = testdir.makepyfile( p = testdir.makepyfile(
"""\ """\
@ -843,17 +816,9 @@ class TestCaptureIO(object):
def test_unicode_and_str_mixture(self): def test_unicode_and_str_mixture(self):
f = capture.CaptureIO() f = capture.CaptureIO()
if sys.version_info >= (3, 0): f.write("\u00f6")
f.write("\u00f6") pytest.raises(TypeError, f.write, b"hello")
pytest.raises(TypeError, f.write, b"hello")
else:
f.write(u"\u00f6")
f.write(b"hello")
s = f.getvalue()
f.close()
assert isinstance(s, text_type)
@pytest.mark.skipif(sys.version_info[0] == 2, reason="python 3 only behaviour")
def test_write_bytes_to_buffer(self): def test_write_bytes_to_buffer(self):
"""In python3, stdout / stderr are text io wrappers (exposing a buffer """In python3, stdout / stderr are text io wrappers (exposing a buffer
property of the underlying bytestream). See issue #1407 property of the underlying bytestream). See issue #1407
@ -876,7 +841,6 @@ def test_dontreadfrominput():
f.close() # just for completeness f.close() # just for completeness
@pytest.mark.skipif("sys.version_info < (3,)", reason="python2 has no buffer")
def test_dontreadfrominput_buffer_python3(): def test_dontreadfrominput_buffer_python3():
from _pytest.capture import DontReadFromInput from _pytest.capture import DontReadFromInput
@ -891,17 +855,7 @@ def test_dontreadfrominput_buffer_python3():
f.close() # just for completeness f.close() # just for completeness
@pytest.mark.skipif("sys.version_info >= (3,)", reason="python2 has no buffer") @pytest.fixture
def test_dontreadfrominput_buffer_python2():
from _pytest.capture import DontReadFromInput
f = DontReadFromInput()
with pytest.raises(AttributeError):
f.buffer
f.close() # just for completeness
@pytest.yield_fixture
def tmpfile(testdir): def tmpfile(testdir):
f = testdir.makepyfile("").open("wb+") f = testdir.makepyfile("").open("wb+")
yield f yield f
@ -1118,16 +1072,6 @@ class TestStdCapture(object):
out, err = cap.readouterr() out, err = cap.readouterr()
assert out == u"hxąć\n" assert out == u"hxąć\n"
@pytest.mark.skipif(
"sys.version_info >= (3,)", reason="text output different for bytes on python3"
)
def test_capturing_readouterr_decode_error_handling(self):
with self.getcapture() as cap:
# triggered an internal error in pytest
print("\xa6")
out, err = cap.readouterr()
assert out == u"\ufffd\n"
def test_reset_twice_error(self): def test_reset_twice_error(self):
with self.getcapture() as cap: with self.getcapture() as cap:
print("hello") print("hello")
@ -1571,9 +1515,6 @@ def test_typeerror_encodedfile_write(testdir):
assert result_with_capture.ret == result_without_capture.ret assert result_with_capture.ret == result_without_capture.ret
if _PY3: result_with_capture.stdout.fnmatch_lines(
result_with_capture.stdout.fnmatch_lines( ["E TypeError: write() argument must be str, not bytes"]
["E TypeError: write() argument must be str, not bytes"] )
)
else:
assert result_with_capture.ret == 0

View File

@ -38,8 +38,6 @@ class TestCollector(object):
assert fn1 == fn2 assert fn1 == fn2
assert fn1 != modcol assert fn1 != modcol
if sys.version_info < (3, 0):
assert cmp(fn1, fn2) == 0 # NOQA
assert hash(fn1) == hash(fn2) assert hash(fn1) == hash(fn2)
fn3 = testdir.collect_by_name(modcol, "test_fail") fn3 = testdir.collect_by_name(modcol, "test_fail")

View File

@ -6,8 +6,6 @@ from __future__ import print_function
import sys import sys
from functools import wraps from functools import wraps
import six
import pytest import pytest
from _pytest.compat import _PytestWrapper from _pytest.compat import _PytestWrapper
from _pytest.compat import get_real_func from _pytest.compat import get_real_func
@ -62,8 +60,6 @@ def test_get_real_func():
def inner(): def inner():
pass # pragma: no cover pass # pragma: no cover
if six.PY2:
inner.__wrapped__ = f
return inner return inner
def func(): def func():
@ -81,9 +77,6 @@ def test_get_real_func():
assert get_real_func(wrapped_func2) is wrapped_func assert get_real_func(wrapped_func2) is wrapped_func
@pytest.mark.skipif(
sys.version_info < (3, 4), reason="asyncio available in Python 3.4+"
)
def test_is_generator_asyncio(testdir): def test_is_generator_asyncio(testdir):
testdir.makepyfile( testdir.makepyfile(
""" """

View File

@ -218,12 +218,9 @@ class TestConfigAPI(object):
assert config.getoption(x) == "this" assert config.getoption(x) == "this"
pytest.raises(ValueError, config.getoption, "qweqwe") pytest.raises(ValueError, config.getoption, "qweqwe")
@pytest.mark.skipif("sys.version_info[0] < 3")
def test_config_getoption_unicode(self, testdir): def test_config_getoption_unicode(self, testdir):
testdir.makeconftest( testdir.makeconftest(
""" """
from __future__ import unicode_literals
def pytest_addoption(parser): def pytest_addoption(parser):
parser.addoption('--hello', type=str) parser.addoption('--hello', type=str)
""" """

View File

@ -3,7 +3,6 @@ from __future__ import absolute_import
from __future__ import division from __future__ import division
from __future__ import print_function from __future__ import print_function
import sys
import textwrap import textwrap
import pytest import pytest
@ -830,13 +829,11 @@ class TestLiterals(object):
""" """
) )
reprec = testdir.inline_run() reprec = testdir.inline_run()
passed = int(sys.version_info[0] >= 3) reprec.assertoutcome(passed=1)
reprec.assertoutcome(passed=passed, failed=int(not passed))
def test_bytes_literal(self, testdir): def test_bytes_literal(self, testdir):
"""Test that doctests which output bytes fail in Python 3 when """Test that doctests which output bytes fail in Python 3 when
the ALLOW_BYTES option is not used. The same test should pass the ALLOW_BYTES option is not used. (#1287).
in Python 2 (#1287).
""" """
testdir.maketxtfile( testdir.maketxtfile(
test_doc=""" test_doc="""
@ -845,8 +842,7 @@ class TestLiterals(object):
""" """
) )
reprec = testdir.inline_run() reprec = testdir.inline_run()
passed = int(sys.version_info[0] == 2) reprec.assertoutcome(failed=1)
reprec.assertoutcome(passed=passed, failed=int(not passed))
class TestDoctestSkips(object): class TestDoctestSkips(object):

View File

@ -4,7 +4,6 @@ from __future__ import division
from __future__ import print_function from __future__ import print_function
import os import os
import sys
from xml.dom import minidom from xml.dom import minidom
import py import py
@ -585,8 +584,7 @@ class TestPython(object):
assert result.ret == 1 assert result.ret == 1
tnode = dom.find_first_by_tag("testcase") tnode = dom.find_first_by_tag("testcase")
fnode = tnode.find_first_by_tag("failure") fnode = tnode.find_first_by_tag("failure")
if not sys.platform.startswith("java"): assert "hx" in fnode.toxml()
assert "hx" in fnode.toxml()
def test_assertion_binchars(self, testdir): def test_assertion_binchars(self, testdir):
"""this test did fail when the escaping wasnt strict""" """this test did fail when the escaping wasnt strict"""

View File

@ -5,8 +5,7 @@ from __future__ import print_function
import os import os
import sys import sys
from unittest import mock
import six
import pytest import pytest
from _pytest.main import EXIT_INTERRUPTED from _pytest.main import EXIT_INTERRUPTED
@ -17,11 +16,6 @@ from _pytest.nodes import Node
from _pytest.warning_types import PytestDeprecationWarning from _pytest.warning_types import PytestDeprecationWarning
from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG
try:
import mock
except ImportError:
import unittest.mock as mock
ignore_markinfo = pytest.mark.filterwarnings( ignore_markinfo = pytest.mark.filterwarnings(
"ignore:MarkInfo objects:pytest.RemovedInPytest4Warning" "ignore:MarkInfo objects:pytest.RemovedInPytest4Warning"
) )
@ -1009,10 +1003,7 @@ def test_pytest_param_id_requires_string():
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
pytest.param(id=True) pytest.param(id=True)
msg, = excinfo.value.args msg, = excinfo.value.args
if six.PY2: assert msg == "Expected id to be a string, got <class 'bool'>: True"
assert msg == "Expected id to be a string, got <type 'bool'>: True"
else:
assert msg == "Expected id to be a string, got <class 'bool'>: True"
@pytest.mark.parametrize("s", (None, "hello world")) @pytest.mark.parametrize("s", (None, "hello world"))

View File

@ -8,8 +8,6 @@ import re
import sys import sys
import textwrap import textwrap
import six
import pytest import pytest
from _pytest.monkeypatch import MonkeyPatch from _pytest.monkeypatch import MonkeyPatch
@ -209,22 +207,6 @@ class TestEnvironWarnings(object):
VAR_NAME = u"PYTEST_INTERNAL_MY_VAR" VAR_NAME = u"PYTEST_INTERNAL_MY_VAR"
@pytest.mark.skipif(six.PY3, reason="Python 2 only test")
def test_setenv_unicode_key(self, monkeypatch):
with pytest.warns(
pytest.PytestWarning,
match="Environment variable name {!r} should be str".format(self.VAR_NAME),
):
monkeypatch.setenv(self.VAR_NAME, "2")
@pytest.mark.skipif(six.PY3, reason="Python 2 only test")
def test_delenv_unicode_key(self, monkeypatch):
with pytest.warns(
pytest.PytestWarning,
match="Environment variable name {!r} should be str".format(self.VAR_NAME),
):
monkeypatch.delenv(self.VAR_NAME, raising=False)
def test_setenv_non_str_warning(self, monkeypatch): def test_setenv_non_str_warning(self, monkeypatch):
value = 2 value = 2
msg = ( msg = (
@ -349,10 +331,8 @@ def test_importerror(testdir):
result = testdir.runpytest() result = testdir.runpytest()
result.stdout.fnmatch_lines( result.stdout.fnmatch_lines(
""" """
*import error in package.a: No module named {0}doesnotexist{0}* *import error in package.a: No module named 'doesnotexist'*
""".format( """
"'" if sys.version_info > (3, 0) else ""
)
) )

View File

@ -3,8 +3,6 @@ from __future__ import absolute_import
from __future__ import division from __future__ import division
from __future__ import print_function from __future__ import print_function
import sys
import pytest import pytest
@ -74,10 +72,7 @@ class TestPasteCapture(object):
""" """
) )
result = testdir.runpytest("--pastebin=all") result = testdir.runpytest("--pastebin=all")
if sys.version_info[0] == 3: expected_msg = "*assert '' == 1*"
expected_msg = "*assert '' == 1*"
else:
expected_msg = "*assert '\\xe2\\x98\\xba' == 1*"
result.stdout.fnmatch_lines( result.stdout.fnmatch_lines(
[ [
expected_msg, expected_msg,
@ -110,14 +105,9 @@ class TestPaste(object):
return DummyFile() return DummyFile()
if sys.version_info < (3, 0): import urllib.request
import urllib
monkeypatch.setattr(urllib, "urlopen", mocked) monkeypatch.setattr(urllib.request, "urlopen", mocked)
else:
import urllib.request
monkeypatch.setattr(urllib.request, "urlopen", mocked)
return calls return calls
def test_create_new_paste(self, pastebin, mocked_urlopen): def test_create_new_paste(self, pastebin, mocked_urlopen):
@ -126,7 +116,7 @@ class TestPaste(object):
assert len(mocked_urlopen) == 1 assert len(mocked_urlopen) == 1
url, data = mocked_urlopen[0] url, data = mocked_urlopen[0]
assert type(data) is bytes assert type(data) is bytes
lexer = "python3" if sys.version_info[0] == 3 else "python" lexer = "python3"
assert url == "https://bpaste.net" assert url == "https://bpaste.net"
assert "lexer=%s" % lexer in data.decode() assert "lexer=%s" % lexer in data.decode()
assert "code=full-paste-contents" in data.decode() assert "code=full-paste-contents" in data.decode()

View File

@ -6,8 +6,6 @@ from __future__ import print_function
import os import os
import sys import sys
import six
import _pytest._code import _pytest._code
import pytest import pytest
from _pytest.debugging import _validate_usepdb_cls from _pytest.debugging import _validate_usepdb_cls
@ -537,10 +535,7 @@ class TestPDB(object):
import sys import sys
import types import types
if sys.version_info < (3, ): do_debug_func = pdb.Pdb.do_debug
do_debug_func = pdb.Pdb.do_debug.im_func
else:
do_debug_func = pdb.Pdb.do_debug
newglobals = do_debug_func.__globals__.copy() newglobals = do_debug_func.__globals__.copy()
newglobals['Pdb'] = self.__class__ newglobals['Pdb'] = self.__class__
@ -866,8 +861,6 @@ class TestDebuggingBreakpoints(object):
assert SUPPORTS_BREAKPOINT_BUILTIN is True assert SUPPORTS_BREAKPOINT_BUILTIN is True
if sys.version_info.major == 3 and sys.version_info.minor == 5: if sys.version_info.major == 3 and sys.version_info.minor == 5:
assert SUPPORTS_BREAKPOINT_BUILTIN is False assert SUPPORTS_BREAKPOINT_BUILTIN is False
if sys.version_info.major == 2 and sys.version_info.minor == 7:
assert SUPPORTS_BREAKPOINT_BUILTIN is False
@pytest.mark.skipif( @pytest.mark.skipif(
not SUPPORTS_BREAKPOINT_BUILTIN, reason="Requires breakpoint() builtin" not SUPPORTS_BREAKPOINT_BUILTIN, reason="Requires breakpoint() builtin"
@ -1183,24 +1176,17 @@ def test_pdbcls_via_local_module(testdir):
def test_raises_bdbquit_with_eoferror(testdir): def test_raises_bdbquit_with_eoferror(testdir):
"""It is not guaranteed that DontReadFromInput's read is called.""" """It is not guaranteed that DontReadFromInput's read is called."""
if six.PY2:
builtin_module = "__builtin__"
input_func = "raw_input"
else:
builtin_module = "builtins"
input_func = "input"
p1 = testdir.makepyfile( p1 = testdir.makepyfile(
""" """
def input_without_read(*args, **kwargs): def input_without_read(*args, **kwargs):
raise EOFError() raise EOFError()
def test(monkeypatch): def test(monkeypatch):
import {builtin_module} import builtins
monkeypatch.setattr({builtin_module}, {input_func!r}, input_without_read) monkeypatch.setattr(builtins, "input", input_without_read)
__import__('pdb').set_trace() __import__('pdb').set_trace()
""".format( """
builtin_module=builtin_module, input_func=input_func
)
) )
result = testdir.runpytest(str(p1)) result = testdir.runpytest(str(p1))
result.stdout.fnmatch_lines(["E *BdbQuit", "*= 1 failed in*"]) result.stdout.fnmatch_lines(["E *BdbQuit", "*= 1 failed in*"])

View File

@ -654,10 +654,7 @@ def test_pytest_fail_notrace_non_ascii(testdir, str_prefix):
% str_prefix % str_prefix
) )
result = testdir.runpytest() result = testdir.runpytest()
if sys.version_info[0] >= 3: result.stdout.fnmatch_lines(["*test_hello*", "oh oh: ☺"])
result.stdout.fnmatch_lines(["*test_hello*", "oh oh: ☺"])
else:
result.stdout.fnmatch_lines(["*test_hello*", "oh oh: *"])
assert "def test_hello" not in result.stdout.str() assert "def test_hello" not in result.stdout.str()

View File

@ -49,22 +49,6 @@ class TestEvaluator(object):
expl = ev.getexplanation() expl = ev.getexplanation()
assert expl == "condition: hasattr(os, 'sep')" assert expl == "condition: hasattr(os, 'sep')"
@pytest.mark.skipif("sys.version_info[0] >= 3")
def test_marked_one_arg_unicode(self, testdir):
item = testdir.getitem(
"""
import pytest
@pytest.mark.xyz(u"hasattr(os, 'sep')")
def test_func():
pass
"""
)
ev = MarkEvaluator(item, "xyz")
assert ev
assert ev.istrue()
expl = ev.getexplanation()
assert expl == "condition: hasattr(os, 'sep')"
def test_marked_one_arg_with_reason(self, testdir): def test_marked_one_arg_with_reason(self, testdir):
item = testdir.getitem( item = testdir.getitem(
""" """
@ -893,10 +877,7 @@ def test_errors_in_xfail_skip_expressions(testdir):
) )
result = testdir.runpytest() result = testdir.runpytest()
markline = " ^" markline = " ^"
if sys.platform.startswith("java"): if hasattr(sys, "pypy_version_info") and sys.pypy_version_info < (6,):
# XXX report this to java
markline = "*" + markline[8:]
elif hasattr(sys, "pypy_version_info") and sys.pypy_version_info < (6,):
markline = markline[5:] markline = markline[5:]
elif sys.version_info >= (3, 8) or hasattr(sys, "pypy_version_info"): elif sys.version_info >= (3, 8) or hasattr(sys, "pypy_version_info"):
markline = markline[4:] markline = markline[4:]

View File

@ -6,7 +6,6 @@ from __future__ import print_function
import sys import sys
import attr import attr
import six
import pytest import pytest
from _pytest import pathlib from _pytest import pathlib
@ -348,8 +347,6 @@ class TestNumberedDir(object):
def attempt_symlink_to(path, to_path): def attempt_symlink_to(path, to_path):
"""Try to make a symlink from "path" to "to_path", skipping in case this platform """Try to make a symlink from "path" to "to_path", skipping in case this platform
does not support it or we don't have sufficient privileges (common on Windows).""" does not support it or we don't have sufficient privileges (common on Windows)."""
if sys.platform.startswith("win") and six.PY2:
pytest.skip("pathlib for some reason cannot make symlinks on Python 2")
try: try:
Path(path).symlink_to(Path(to_path)) Path(path).symlink_to(Path(to_path))
except OSError: except OSError:

View File

@ -459,9 +459,6 @@ class TestTrialUnittest(object):
pass pass
""" """
) )
from _pytest.compat import _is_unittest_unexpected_success_a_failure
should_fail = _is_unittest_unexpected_success_a_failure()
result = testdir.runpytest("-rxs", *self.ignore_unclosed_socket_warning) result = testdir.runpytest("-rxs", *self.ignore_unclosed_socket_warning)
result.stdout.fnmatch_lines_random( result.stdout.fnmatch_lines_random(
[ [
@ -472,12 +469,10 @@ class TestTrialUnittest(object):
"*i2wanto*", "*i2wanto*",
"*sys.version_info*", "*sys.version_info*",
"*skip_in_method*", "*skip_in_method*",
"*1 failed*4 skipped*3 xfailed*" "*1 failed*4 skipped*3 xfailed*",
if should_fail
else "*4 skipped*3 xfail*1 xpass*",
] ]
) )
assert result.ret == (1 if should_fail else 0) assert result.ret == 1
def test_trial_error(self, testdir): def test_trial_error(self, testdir):
testdir.makepyfile( testdir.makepyfile(
@ -745,22 +740,17 @@ def test_unittest_expected_failure_for_passing_test_is_fail(testdir, runner):
unittest.main() unittest.main()
""" """
) )
from _pytest.compat import _is_unittest_unexpected_success_a_failure
should_fail = _is_unittest_unexpected_success_a_failure()
if runner == "pytest": if runner == "pytest":
result = testdir.runpytest("-rxX") result = testdir.runpytest("-rxX")
result.stdout.fnmatch_lines( result.stdout.fnmatch_lines(
[ ["*MyTestCase*test_passing_test_is_fail*", "*1 failed*"]
"*MyTestCase*test_passing_test_is_fail*",
"*1 failed*" if should_fail else "*1 xpassed*",
]
) )
else: else:
result = testdir.runpython(script) result = testdir.runpython(script)
result.stderr.fnmatch_lines(["*1 test in*", "*(unexpected successes=1)*"]) result.stderr.fnmatch_lines(["*1 test in*", "*(unexpected successes=1)*"])
assert result.ret == (1 if should_fail else 0) assert result.ret == 1
@pytest.mark.parametrize( @pytest.mark.parametrize(

View File

@ -1,11 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
import sys
import warnings import warnings
import six
import pytest import pytest
WARNINGS_SUMMARY_HEADER = "warnings summary" WARNINGS_SUMMARY_HEADER = "warnings summary"
@ -126,9 +123,6 @@ def test_ignore(testdir, pyfile_with_warnings, method):
assert WARNINGS_SUMMARY_HEADER not in result.stdout.str() assert WARNINGS_SUMMARY_HEADER not in result.stdout.str()
@pytest.mark.skipif(
sys.version_info < (3, 0), reason="warnings message is unicode is ok in python3"
)
@pytest.mark.filterwarnings("always") @pytest.mark.filterwarnings("always")
def test_unicode(testdir, pyfile_with_warnings): def test_unicode(testdir, pyfile_with_warnings):
testdir.makepyfile( testdir.makepyfile(
@ -157,68 +151,6 @@ def test_unicode(testdir, pyfile_with_warnings):
) )
@pytest.mark.skipif(
sys.version_info >= (3, 0),
reason="warnings message is broken as it is not str instance",
)
def test_py2_unicode(testdir, pyfile_with_warnings):
if getattr(sys, "pypy_version_info", ())[:2] == (5, 9) and sys.platform.startswith(
"win"
):
pytest.xfail("fails with unicode error on PyPy2 5.9 and Windows (#2905)")
testdir.makepyfile(
"""
# -*- coding: utf-8 -*-
import warnings
import pytest
@pytest.fixture
def fix():
warnings.warn(u"测试")
yield
@pytest.mark.filterwarnings('always')
def test_func(fix):
pass
"""
)
result = testdir.runpytest()
result.stdout.fnmatch_lines(
[
"*== %s ==*" % WARNINGS_SUMMARY_HEADER,
"*test_py2_unicode.py:8: UserWarning: \\u6d4b\\u8bd5",
'*warnings.warn(u"\u6d4b\u8bd5")',
"*warnings.py:*: UnicodeWarning: Warning is using unicode non*",
"* 1 passed, 2 warnings*",
]
)
def test_py2_unicode_ascii(testdir):
"""Ensure that our warning about 'unicode warnings containing non-ascii messages'
does not trigger with ascii-convertible messages"""
testdir.makeini("[pytest]")
testdir.makepyfile(
"""
import pytest
import warnings
@pytest.mark.filterwarnings('always')
def test_func():
warnings.warn(u"hello")
"""
)
result = testdir.runpytest()
result.stdout.fnmatch_lines(
[
"*== %s ==*" % WARNINGS_SUMMARY_HEADER,
'*warnings.warn(u"hello")',
"* 1 passed, 1 warnings in*",
]
)
def test_works_with_filterwarnings(testdir): def test_works_with_filterwarnings(testdir):
"""Ensure our warnings capture does not mess with pre-installed filters (#2430).""" """Ensure our warnings capture does not mess with pre-installed filters (#2430)."""
testdir.makepyfile( testdir.makepyfile(
@ -569,33 +501,6 @@ class TestDeprecationWarningsByDefault:
assert WARNINGS_SUMMARY_HEADER not in result.stdout.str() assert WARNINGS_SUMMARY_HEADER not in result.stdout.str()
@pytest.mark.skipif(six.PY3, reason="Python 2 only issue")
def test_infinite_loop_warning_against_unicode_usage_py2(testdir):
"""
We need to be careful when raising the warning about unicode usage with "warnings.warn"
because it might be overwritten by users and this itself causes another warning (#3691).
"""
testdir.makepyfile(
"""
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import warnings
import pytest
def _custom_showwarning(message, *a, **b):
return "WARNING: {}".format(message)
warnings.formatwarning = _custom_showwarning
@pytest.mark.filterwarnings("default")
def test_custom_warning_formatter():
warnings.warn("¥")
"""
)
result = testdir.runpytest_subprocess()
result.stdout.fnmatch_lines(["*1 passed, * warnings in*"])
@pytest.mark.parametrize("change_default", [None, "ini", "cmdline"]) @pytest.mark.parametrize("change_default", [None, "ini", "cmdline"])
def test_removed_in_pytest4_warning_as_error(testdir, change_default): def test_removed_in_pytest4_warning_as_error(testdir, change_default):
testdir.makepyfile( testdir.makepyfile(

19
tox.ini
View File

@ -5,16 +5,13 @@ distshare = {homedir}/.tox/distshare
# make sure to update environment list in travis.yml and appveyor.yml # make sure to update environment list in travis.yml and appveyor.yml
envlist = envlist =
linting linting
py27
py34
py35 py35
py36 py36
py37 py37
py38 py38
pypy pypy
pypy3 pypy3
{py27,py37}-{pexpect,xdist,twisted,numpy,pluggymaster} py37-{pexpect,xdist,twisted,numpy,pluggymaster}
py27-nobyte-xdist
doctesting doctesting
py37-freeze py37-freeze
docs docs
@ -56,15 +53,6 @@ deps =
{env:_PYTEST_TOX_EXTRA_DEP:} {env:_PYTEST_TOX_EXTRA_DEP:}
platform = {env:_PYTEST_TOX_PLATFORM:.*} platform = {env:_PYTEST_TOX_PLATFORM:.*}
[testenv:py27-subprocess]
deps =
pytest-xdist>=1.13
py27: mock
nose
commands =
pytest -n auto --runpytest=subprocess {posargs}
[testenv:linting] [testenv:linting]
skip_install = True skip_install = True
basepython = python3 basepython = python3
@ -109,11 +97,6 @@ commands =
rm -rf {envdir}/.pytest_cache rm -rf {envdir}/.pytest_cache
make regen make regen
[testenv:jython]
changedir = testing
commands =
{envpython} {envbindir}/py.test-jython {posargs}
[testenv:py37-freeze] [testenv:py37-freeze]
changedir = testing/freeze changedir = testing/freeze
# Disable PEP 517 with pip, which does not work with PyInstaller currently. # Disable PEP 517 with pip, which does not work with PyInstaller currently.