diff --git a/_pytest/_code/code.py b/_pytest/_code/code.py index 0230c5660..bec236876 100644 --- a/_pytest/_code/code.py +++ b/_pytest/_code/code.py @@ -8,8 +8,6 @@ from _pytest.compat import _PY2, _PY3, PY35, safe_str import py builtin_repr = repr -reprlib = py.builtin._tryimport('repr', 'reprlib') - if _PY3: from traceback import format_exception_only else: @@ -235,7 +233,7 @@ class TracebackEntry(object): except KeyError: return False - if py.builtin.callable(tbh): + if callable(tbh): return tbh(None if self._excinfo is None else self._excinfo()) else: return tbh diff --git a/_pytest/_code/source.py b/_pytest/_code/source.py index e21fecb1e..2959d635a 100644 --- a/_pytest/_code/source.py +++ b/_pytest/_code/source.py @@ -2,6 +2,7 @@ from __future__ import absolute_import, division, generators, print_function from bisect import bisect_right import sys +import six import inspect import tokenize import py @@ -32,7 +33,7 @@ class Source(object): partlines = part.lines elif isinstance(part, (tuple, list)): partlines = [x.rstrip("\n") for x in part] - elif isinstance(part, py.builtin._basestring): + elif isinstance(part, six.string_types): partlines = part.split('\n') if rstrip: while partlines: diff --git a/_pytest/assertion/__init__.py b/_pytest/assertion/__init__.py index b0ef667d5..e9e39dae0 100644 --- a/_pytest/assertion/__init__.py +++ b/_pytest/assertion/__init__.py @@ -2,8 +2,8 @@ support for presenting detailed information in failing assertions. """ from __future__ import absolute_import, division, print_function -import py import sys +import six from _pytest.assertion import util from _pytest.assertion import rewrite @@ -126,7 +126,7 @@ def pytest_runtest_setup(item): if new_expl: new_expl = truncate.truncate_if_required(new_expl, item) new_expl = [line.replace("\n", "\\n") for line in new_expl] - res = py.builtin._totext("\n~").join(new_expl) + res = six.text_type("\n~").join(new_expl) if item.config.getvalue("assertmode") == "rewrite": res = res.replace("%", "%%") return res diff --git a/_pytest/assertion/rewrite.py b/_pytest/assertion/rewrite.py index 992002b81..956ff487f 100644 --- a/_pytest/assertion/rewrite.py +++ b/_pytest/assertion/rewrite.py @@ -8,6 +8,7 @@ import imp import marshal import os import re +import six import struct import sys import types @@ -405,10 +406,10 @@ def _saferepr(obj): """ repr = py.io.saferepr(obj) - if py.builtin._istext(repr): - t = py.builtin.text + if isinstance(repr, six.text_type): + t = six.text_type else: - t = py.builtin.bytes + t = six.binary_type return repr.replace(t("\n"), t("\\n")) @@ -427,16 +428,16 @@ def _format_assertmsg(obj): # contains a newline it gets escaped, however if an object has a # .__repr__() which contains newlines it does not get escaped. # However in either case we want to preserve the newline. - if py.builtin._istext(obj) or py.builtin._isbytes(obj): + if isinstance(obj, six.text_type) or isinstance(obj, six.binary_type): s = obj is_repr = False else: s = py.io.saferepr(obj) is_repr = True - if py.builtin._istext(s): - t = py.builtin.text + if isinstance(s, six.text_type): + t = six.text_type else: - t = py.builtin.bytes + t = six.binary_type s = s.replace(t("\n"), t("\n~")).replace(t("%"), t("%%")) if is_repr: s = s.replace(t("\\n"), t("\n~")) @@ -444,15 +445,15 @@ def _format_assertmsg(obj): def _should_repr_global_name(obj): - return not hasattr(obj, "__name__") and not py.builtin.callable(obj) + return not hasattr(obj, "__name__") and not callable(obj) def _format_boolop(explanations, is_or): explanation = "(" + (is_or and " or " or " and ").join(explanations) + ")" - if py.builtin._istext(explanation): - t = py.builtin.text + if isinstance(explanation, six.text_type): + t = six.text_type else: - t = py.builtin.bytes + t = six.binary_type return explanation.replace(t('%'), t('%%')) diff --git a/_pytest/assertion/truncate.py b/_pytest/assertion/truncate.py index 1e1306356..2ed12e2e5 100644 --- a/_pytest/assertion/truncate.py +++ b/_pytest/assertion/truncate.py @@ -7,7 +7,7 @@ Current default behaviour is to truncate assertion explanations at from __future__ import absolute_import, division, print_function import os -import py +import six DEFAULT_MAX_LINES = 8 @@ -74,8 +74,8 @@ def _truncate_explanation(input_lines, max_lines=None, max_chars=None): msg += ' ({0} lines hidden)'.format(truncated_line_count) msg += ", {0}" .format(USAGE_MSG) truncated_explanation.extend([ - py.builtin._totext(""), - py.builtin._totext(msg), + six.text_type(""), + six.text_type(msg), ]) return truncated_explanation diff --git a/_pytest/assertion/util.py b/_pytest/assertion/util.py index 41e66448d..69cd9c630 100644 --- a/_pytest/assertion/util.py +++ b/_pytest/assertion/util.py @@ -4,13 +4,14 @@ import pprint import _pytest._code import py +import six try: from collections import Sequence except ImportError: Sequence = list -u = py.builtin._totext +u = six.text_type # The _reprcompare attribute on the util module is used by the new assertion # interpretation code and assertion rewriter to detect this plugin was @@ -174,9 +175,9 @@ def _diff_text(left, right, verbose=False): """ from difflib import ndiff explanation = [] - if isinstance(left, py.builtin.bytes): + if isinstance(left, six.binary_type): left = u(repr(left)[1:-1]).replace(r'\n', '\n') - if isinstance(right, py.builtin.bytes): + if isinstance(right, six.binary_type): right = u(repr(right)[1:-1]).replace(r'\n', '\n') if not verbose: i = 0 # just in case left or right has zero length diff --git a/_pytest/capture.py b/_pytest/capture.py index a4171f0fa..1a5d8cf8d 100644 --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -11,11 +11,10 @@ import io from io import UnsupportedOperation from tempfile import TemporaryFile -import py +import six import pytest from _pytest.compat import CaptureIO -unicode = py.builtin.text patchsysdict = {0: 'stdin', 1: 'stdout', 2: 'stderr'} @@ -246,7 +245,7 @@ class EncodedFile(object): self.encoding = encoding def write(self, obj): - if isinstance(obj, unicode): + if isinstance(obj, six.text_type): obj = obj.encode(self.encoding, "replace") self.buffer.write(obj) @@ -377,7 +376,7 @@ class FDCapture: if res: enc = getattr(f, "encoding", None) if enc and isinstance(res, bytes): - res = py.builtin._totext(res, enc, "replace") + res = six.text_type(res, enc, "replace") f.truncate(0) f.seek(0) return res @@ -402,7 +401,7 @@ class FDCapture: def writeorg(self, data): """ write to original file descriptor. """ - if py.builtin._istext(data): + if isinstance(data, six.text_type): data = data.encode("utf8") # XXX use encoding of original stream os.write(self.targetfd_save, data) diff --git a/_pytest/config.py b/_pytest/config.py index bdfe99b99..dedffd5a6 100644 --- a/_pytest/config.py +++ b/_pytest/config.py @@ -6,6 +6,7 @@ import traceback import types import warnings +import six import py # DON't import pytest here because it causes import cycle troubles import sys @@ -149,7 +150,7 @@ def _prepareconfig(args=None, plugins=None): try: if plugins: for plugin in plugins: - if isinstance(plugin, py.builtin._basestring): + if isinstance(plugin, six.string_types): pluginmanager.consider_pluginarg(plugin) else: pluginmanager.register(plugin) @@ -421,7 +422,7 @@ class PytestPluginManager(PluginManager): # "terminal" or "capture". Those plugins are registered under their # basename for historic purposes but must be imported with the # _pytest prefix. - assert isinstance(modname, (py.builtin.text, str)), "module name as text required, got %r" % modname + assert isinstance(modname, (six.text_type, str)), "module name as text required, got %r" % modname modname = str(modname) if self.get_plugin(modname) is not None: return @@ -634,7 +635,7 @@ class Argument: pass else: # this might raise a keyerror as well, don't want to catch that - if isinstance(typ, py.builtin._basestring): + if isinstance(typ, six.string_types): if typ == 'choice': warnings.warn( 'type argument to addoption() is a string %r.' @@ -947,7 +948,7 @@ class Config(object): ) res = self.hook.pytest_internalerror(excrepr=excrepr, excinfo=excinfo) - if not py.builtin.any(res): + if not any(res): for line in str(excrepr).split("\n"): sys.stderr.write("INTERNALERROR> %s\n" % line) sys.stderr.flush() diff --git a/_pytest/main.py b/_pytest/main.py index bfcbd6b48..f05cb7ff3 100644 --- a/_pytest/main.py +++ b/_pytest/main.py @@ -3,6 +3,7 @@ from __future__ import absolute_import, division, print_function import functools import os +import six import sys import _pytest @@ -371,7 +372,7 @@ class Node(object): ``marker`` can be a string or pytest.mark.* instance. """ from _pytest.mark import MarkDecorator, MARK_GEN - if isinstance(marker, py.builtin._basestring): + if isinstance(marker, six.string_types): marker = getattr(MARK_GEN, marker) elif not isinstance(marker, MarkDecorator): raise ValueError("is not a string or pytest.mark.* Marker") diff --git a/_pytest/monkeypatch.py b/_pytest/monkeypatch.py index 39ac77013..40ae560f0 100644 --- a/_pytest/monkeypatch.py +++ b/_pytest/monkeypatch.py @@ -4,8 +4,7 @@ from __future__ import absolute_import, division, print_function import os import sys import re - -from py.builtin import _basestring +import six from _pytest.fixtures import fixture RE_IMPORT_ERROR_NAME = re.compile("^No module named (.*)$") @@ -79,7 +78,7 @@ def annotated_getattr(obj, name, ann): def derive_importpath(import_path, raising): - if not isinstance(import_path, _basestring) or "." not in import_path: + if not isinstance(import_path, six.string_types) or "." not in import_path: raise TypeError("must be absolute import path string, not %r" % (import_path,)) module, attr = import_path.rsplit('.', 1) @@ -125,7 +124,7 @@ class MonkeyPatch: import inspect if value is notset: - if not isinstance(target, _basestring): + if not isinstance(target, six.string_types): raise TypeError("use setattr(target, name, value) or " "setattr(target, value) with target being a dotted " "import string") @@ -155,7 +154,7 @@ class MonkeyPatch: """ __tracebackhide__ = True if name is notset: - if not isinstance(target, _basestring): + if not isinstance(target, six.string_types): raise TypeError("use delattr(target, name) or " "delattr(target) with target being a dotted " "import string") diff --git a/_pytest/nose.py b/_pytest/nose.py index d246c5603..c81542ead 100644 --- a/_pytest/nose.py +++ b/_pytest/nose.py @@ -3,7 +3,6 @@ from __future__ import absolute_import, division, print_function import sys -import py from _pytest import unittest, runner, python from _pytest.config import hookimpl @@ -66,7 +65,7 @@ def is_potential_nosetest(item): def call_optional(obj, name): method = getattr(obj, name, None) isfixture = hasattr(method, "_pytestfixturefunction") - if method is not None and not isfixture and py.builtin.callable(method): + if method is not None and not isfixture and callable(method): # If there's any problems allow the exception to raise rather than # silently ignoring them method() diff --git a/_pytest/pastebin.py b/_pytest/pastebin.py index 9d689819f..b588b021b 100644 --- a/_pytest/pastebin.py +++ b/_pytest/pastebin.py @@ -2,6 +2,7 @@ from __future__ import absolute_import, division, print_function import pytest +import six import sys import tempfile @@ -16,7 +17,6 @@ def pytest_addoption(parser): @pytest.hookimpl(trylast=True) def pytest_configure(config): - import py if config.option.pastebin == "all": tr = config.pluginmanager.getplugin('terminalreporter') # if no terminal reporter plugin is present, nothing we can do here; @@ -29,7 +29,7 @@ def pytest_configure(config): def tee_write(s, **kwargs): oldwrite(s, **kwargs) - if py.builtin._istext(s): + if isinstance(s, six.text_type): s = s.encode('utf-8') config._pastebinfile.write(s) diff --git a/_pytest/pytester.py b/_pytest/pytester.py index 2c558183b..2d2683574 100644 --- a/_pytest/pytester.py +++ b/_pytest/pytester.py @@ -7,6 +7,7 @@ import os import platform import re import subprocess +import six import sys import time import traceback @@ -482,8 +483,8 @@ class Testdir: def _makefile(self, ext, args, kwargs, encoding="utf-8"): items = list(kwargs.items()) if args: - source = py.builtin._totext("\n").join( - map(py.builtin._totext, args)) + py.builtin._totext("\n") + source = six.text_type("\n").join( + map(six.text_type, args)) + six.text_type("\n") basename = self.request.function.__name__ items.insert(0, (basename, source)) ret = None @@ -493,12 +494,12 @@ class Testdir: source = Source(value) def my_totext(s, encoding="utf-8"): - if py.builtin._isbytes(s): - s = py.builtin._totext(s, encoding=encoding) + if isinstance(s, six.binary_type): + s = six.text_type(s, encoding=encoding) return s source_unicode = "\n".join([my_totext(line) for line in source.lines]) - source = py.builtin._totext(source_unicode) + source = six.text_type(source_unicode) content = source.strip().encode(encoding) # + "\n" # content = content.rstrip() + "\n" p.write(content, "wb") diff --git a/_pytest/python.py b/_pytest/python.py index e7bb9ad62..bdf14d841 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -10,6 +10,7 @@ from textwrap import dedent from itertools import count import py +import six from _pytest.mark import MarkerError from _pytest.config import hookimpl @@ -613,7 +614,7 @@ class Generator(FunctionMixin, PyCollector): if not isinstance(obj, (tuple, list)): obj = (obj,) # explicit naming - if isinstance(obj[0], py.builtin._basestring): + if isinstance(obj[0], six.string_types): name = obj[0] obj = obj[1:] else: @@ -725,7 +726,7 @@ class Metafunc(fixtures.FuncargnamesCompatAttr): self.cls = cls self._calls = [] - self._ids = py.builtin.set() + self._ids = set() self._arg2fixturedefs = fixtureinfo.name2fixturedefs def parametrize(self, argnames, argvalues, indirect=False, ids=None, @@ -827,7 +828,7 @@ class Metafunc(fixtures.FuncargnamesCompatAttr): raise ValueError('%d tests specified with %d ids' % ( len(parameters), len(ids))) for id_value in ids: - if id_value is not None and not isinstance(id_value, py.builtin._basestring): + if id_value is not None and not isinstance(id_value, six.string_types): msg = 'ids must be list of strings, found: %s (type: %s)' raise ValueError(msg % (saferepr(id_value), type(id_value).__name__)) ids = idmaker(argnames, parameters, idfn, ids, self.config) diff --git a/_pytest/runner.py b/_pytest/runner.py index a1daa9761..003ce242f 100644 --- a/_pytest/runner.py +++ b/_pytest/runner.py @@ -431,7 +431,7 @@ class SetupState(object): is called at the end of teardown_all(). """ assert colitem and not isinstance(colitem, tuple) - assert py.builtin.callable(finalizer) + assert callable(finalizer) # assert colitem in self.stack # some unit tests don't setup stack :/ self._finalizers.setdefault(colitem, []).append(finalizer) diff --git a/_pytest/skipping.py b/_pytest/skipping.py index 2fd61448a..5f927a6b4 100644 --- a/_pytest/skipping.py +++ b/_pytest/skipping.py @@ -2,10 +2,10 @@ from __future__ import absolute_import, division, print_function import os +import six import sys import traceback -import py from _pytest.config import hookimpl from _pytest.mark import MarkInfo, MarkDecorator from _pytest.outcomes import fail, skip, xfail, TEST_OUTCOME @@ -120,7 +120,7 @@ class MarkEvaluator: args = (kwargs['condition'],) for expr in args: self.expr = expr - if isinstance(expr, py.builtin._basestring): + if isinstance(expr, six.string_types): d = self._getglobals() result = cached_eval(self.item.config, expr, d) else: diff --git a/_pytest/terminal.py b/_pytest/terminal.py index 7dd10924a..0a023d1f3 100644 --- a/_pytest/terminal.py +++ b/_pytest/terminal.py @@ -9,6 +9,7 @@ from _pytest.main import EXIT_OK, EXIT_TESTSFAILED, EXIT_INTERRUPTED, \ EXIT_USAGEERROR, EXIT_NOTESTSCOLLECTED import pytest import py +import six import sys import time import platform @@ -174,8 +175,8 @@ class TerminalReporter: self._tw.write(content, **markup) def write_line(self, line, **markup): - if not py.builtin._istext(line): - line = py.builtin.text(line, errors="replace") + if not isinstance(line, six.text_type): + line = six.text_type(line, errors="replace") self.ensure_newline() self._tw.line(line, **markup) @@ -194,7 +195,7 @@ class TerminalReporter: self._tw.line(msg, **kw) def pytest_internalerror(self, excrepr): - for line in py.builtin.text(excrepr).split("\n"): + for line in six.text_type(excrepr).split("\n"): self.write_line("INTERNALERROR> " + line) return 1 diff --git a/changelog/2642.trivial b/changelog/2642.trivial new file mode 100644 index 000000000..bae449dba --- /dev/null +++ b/changelog/2642.trivial @@ -0,0 +1 @@ +Refactored internal Python 2/3 compatibility code to use ``six``. diff --git a/setup.py b/setup.py index 55607912b..792be6b41 100644 --- a/setup.py +++ b/setup.py @@ -43,7 +43,7 @@ def has_environment_marker_support(): def main(): - install_requires = ['py>=1.4.33', 'setuptools'] # pluggy is vendored in _pytest.vendored_packages + install_requires = ['py>=1.4.33', 'six>=1.10.0','setuptools'] # pluggy is vendored in _pytest.vendored_packages extras_require = {} if has_environment_marker_support(): extras_require[':python_version=="2.6"'] = ['argparse', 'ordereddict']