[Python-modules-commits] [pytest-expect] 01/03: import pytest-expect_1.1.0.orig.tar.gz

Diane Trout diane at moszumanska.debian.org
Tue Nov 15 19:25:09 UTC 2016


This is an automated email from the git hooks/post-receive script.

diane pushed a commit to branch master
in repository pytest-expect.

commit f877c7d02afc6f25160ccae3565965e5acd506db
Author: Diane Trout <diane at ghic.org>
Date:   Mon Nov 14 22:31:33 2016 -0800

    import pytest-expect_1.1.0.orig.tar.gz
---
 LICENSE                   |  19 +++++
 README.rst                |  25 ++++++
 pytest_expect/__init__.py |   0
 pytest_expect/expect.py   | 198 ++++++++++++++++++++++++++++++++++++++++++++++
 setup.py                  |  41 ++++++++++
 5 files changed, 283 insertions(+)

diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..e4d6d89
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015 Geoffrey Sneddon
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..a91fe21
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,25 @@
+pytest-expect
+=============
+
+A `py.test <http://pytest.org/latest/>`_ plugin that stores test
+expectations by saving the set of failing tests, allowing them to be
+marked as xfail when running them in future. The tests expectations
+are stored such that they can be distributed alongside the
+tests. However, note that test expectations can only be reliably
+shared between Python 2 and Python 3 if they only use ASCII characters
+in their node ids: this likely isn't a limitation if tests are using
+the normal Python format, as Python 2 only allows ASCII characters in
+identifiers.
+
+Compatibility
+-------------
+
+We follow `semver <http://semver.org/spec/v2.0.0.html>`_. Our public
+API is defined by the expectation file and the command line parameters.
+
+In other words: for a given major release (e.g., 1.y.z), expectation
+files and command line parameters will remain compatible. Across major
+versions, we'll try to break as little as possible. All pre-release
+(0.y.z) versions should be compatible with each other, but are
+unlikely to be compatible with 1.0.0.
+
diff --git a/pytest_expect/__init__.py b/pytest_expect/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/pytest_expect/expect.py b/pytest_expect/expect.py
new file mode 100644
index 0000000..dae1c8d
--- /dev/null
+++ b/pytest_expect/expect.py
@@ -0,0 +1,198 @@
+"""py.test plugin to store test expectations
+
+Stores test expectations by storing the set of failing tests, allowing
+them to be marked as xfail when running them in future. The tests
+expectations are stored such that they can be distributed alongside
+the tests. However, note that test expectations can only be reliably
+shared between Python 2 and Python 3 if they only use ASCII characters
+in their node ids: this likely isn't a limitation if tests are using
+the normal Python format, as Python 2 only allows ASCII characters in
+identifiers.
+"""
+
+import ast
+import os.path
+import sys
+
+import pytest
+import umsgpack
+from six import PY2, PY3, text_type, binary_type
+
+_magic_file_line = b"pytest-expect file v"
+
+
+def pytest_addoption(parser):
+    group = parser.getgroup("general")
+    group._addoption(
+        '--xfail-file',
+        action="store", dest="xfail_file", default=None, metavar="FILE",
+        help="store test expectations in FILE"
+    )
+
+    group._addoption(
+        "--update-xfail",
+        action="store_true", dest="update_xfail", default=False,
+        help="rebaseline test expectations with current failing tests"
+    )
+
+    group.addoption(
+        "--warn-on-python-xfail",
+        action="store_true", dest="warn_on_python_xfail", default=False,
+        help="trigger a warning when a Python test fails"
+    )
+
+
+def pytest_configure(config):
+    exp = ExpectationPlugin(config)
+    config.pluginmanager.register(exp)
+    if not exp.update_xfail:
+        try:
+            exp.load_expectations()
+        except:
+            config.warn("W1", "failed to load expectation file")
+
+
+class ExpectationPlugin(object):
+    def __init__(self, config):
+        self.config = config
+        self.xfail_file = config.option.xfail_file
+        self.update_xfail = config.option.update_xfail
+        self.warn_on_python_xfail = config.option.warn_on_python_xfail
+        self.fails = set()
+        self.expect_xfail = set()
+
+        if self.xfail_file is None:
+            self.xfail_file = config.rootdir.join(".pytest.expect").strpath
+
+    def load_expectations(self):
+        if not os.path.isfile(self.xfail_file):
+            return
+
+        with open(self.xfail_file, "rb") as fp:
+            new_type = fp.read(len(_magic_file_line)) == _magic_file_line
+            fp.seek(0)
+            if new_type:
+                self.expect_xfail = self._parse_file(fp)
+            else:
+                self.expect_xfail = self._parse_legacy_file(fp)
+
+    def _parse_file(self, fp):
+        # parse header line
+        try:
+            line = next(fp)
+            if not line.startswith(_magic_file_line):
+                raise SyntaxError("invalid pytest-expect file")
+            version = int(line[len(_magic_file_line):].rstrip())
+        except StopIteration:
+            raise SyntaxError("invalid pytest-expect file")
+
+        # parse Python version line
+        try:
+            line = next(fp)
+            if PY3:
+                line = line.decode("ascii")
+            version_info = ast.literal_eval(line)
+            major, minor, micro, releaselevel, serial = version_info
+        except (StopIteration, ValueError):
+            raise SyntaxError("invalid pytest-expect file")
+
+        # parse the actual file
+        if version == 1:
+            fails = set()
+            for line in fp:
+                if PY3:
+                    line = line.decode("ascii")
+                try:
+                    name, result = line.rsplit(":", 1)
+                except ValueError:
+                    raise SyntaxError("invalid pytest-expect file")
+                if result.strip() != "FAIL":
+                    raise SyntaxError("invalid pytest-expect file")
+                name = ast.literal_eval(name)
+                fails.add(name)
+                if PY3 and major == 2 and isinstance(name, bytes):
+                    try:
+                        fails.add(name.decode("latin1"))
+                    except UnicodeDecodeError:
+                        pass
+                if PY2 and major == 3 and isinstance(name, unicode):
+                    try:
+                        fails.add(name.encode("latin1"))
+                    except UnicodeEncodeError:
+                        pass
+            return fails
+        else:
+            raise SyntaxError("unknown pytest-expect file version")
+
+    def _raw_make_file(self, fails):
+        yield _magic_file_line + b"1\n"
+        yield repr(tuple(sys.version_info)) + "\n"
+        for fail in sorted(fails):
+            r = repr(fail)
+            if PY2 and isinstance(fail, str):
+                r = "b" + r
+            elif PY3 and isinstance(fail, str):
+                r = "u" + r
+            yield "%s: FAIL\n" % r
+
+    def _make_file(self, fp, fails):
+        for line in self._raw_make_file(fails):
+            if isinstance(line, text_type):
+                line = line.encode("ascii")
+            fp.write(line)
+
+    def _parse_legacy_file(self, fp):
+        state = umsgpack.unpack(fp)
+
+        if PY3 and b'py_version' in state:
+            for key in list(state.keys()):
+                state[key.decode("ASCII")] = state[key]
+                del state[key]
+
+        version = state["version"]
+        py_version = state["py_version"]
+
+        fails = set()
+
+        if version >= 0x0200:
+            self.config.warn("W1", "test expectation file in unsupported version")
+        elif version >= 0x0100:
+            xfail = state["expect_xfail"]
+            for s in xfail:
+                fails.add(s)
+                if PY3 and py_version == 2 and isinstance(s, binary_type):
+                    fails.add(s.decode('latin1'))
+                elif PY2 and py_version == 3 and isinstance(s, text_type):
+                    try:
+                        fails.add(s.encode("latin1"))
+                    except UnicodeEncodeError:
+                        pass
+        else:
+            self.config.warn("W1", "test expectation file in unsupported version")
+        return fails
+
+    def pytest_collectreport(self, report):
+        passed = report.outcome in ('passed', 'skipped')
+        if passed and not self.update_xfail:
+            if report.nodeid in self.expect_xfail:
+                self.expect_xfail.remove(report.nodeid)
+                for item in report.result:
+                    self.expect_xfail.add(item.nodeid)
+        elif not passed and self.update_xfail:
+            self.fails.add(report.nodeid)
+
+    def pytest_collection_modifyitems(self, session, config, items):
+        if not self.update_xfail:
+            for item in items:
+                if item.nodeid in self.expect_xfail:
+                    item.add_marker(pytest.mark.xfail)
+
+    def pytest_runtest_logreport(self, report):
+        if self.update_xfail and report.failed and "xfail" not in report.keywords:
+            self.fails.add(report.nodeid)
+
+    def pytest_sessionfinish(self, session):
+        if (self.update_xfail and
+                not hasattr(session.config, 'slaveinput')):
+            with open(self.xfail_file, "wb") as fp:
+                self._make_file(fp, self.fails)
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..7ad98d2
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,41 @@
+from setuptools import setup
+
+_classifiers = [
+    'Development Status :: 4 - Beta',
+    'Intended Audience :: Developers',
+    'License :: OSI Approved :: MIT License',
+    'Operating System :: OS Independent',
+    'Programming Language :: Python',
+    'Programming Language :: Python :: 2',
+    'Programming Language :: Python :: 2.6',
+    'Programming Language :: Python :: 2.7',
+    'Programming Language :: Python :: 3',
+    'Programming Language :: Python :: 3.2',
+    'Programming Language :: Python :: 3.3',
+    'Programming Language :: Python :: 3.4',
+    'Topic :: Software Development :: Testing'
+    ]
+
+setup(
+    name="pytest-expect",
+    version="1.1.0",
+    url="https://github.com/gsnedders/pytest-expect",
+    license="MIT License",
+    description="py.test plugin to store test expectations and mark tests based on them",
+    classifiers=_classifiers,
+    maintainer="Geoffrey Sneddon",
+    maintainer_email="geoffers at gmail.com",
+
+    install_requires=[
+        "pytest",
+        "u-msgpack-python"
+    ],
+
+    packages=["pytest_expect"],
+
+    entry_points={
+        'pytest11': [
+            'pytest_expect = pytest_expect.expect',
+        ]
+    }
+)

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/pytest-expect.git



More information about the Python-modules-commits mailing list