Hello community,
here is the log from the commit of package python-testfixtures for
openSUSE:Factory checked in at 2020-03-27 00:21:36
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-testfixtures (Old)
and /work/SRC/openSUSE:Factory/.python-testfixtures.new.3160 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-testfixtures"
Fri Mar 27 00:21:36 2020 rev:11 rq:784177 version:6.14.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-testfixtures/python-testfixtures.changes
2019-07-22 17:19:51.969903066 +0200
+++
/work/SRC/openSUSE:Factory/.python-testfixtures.new.3160/python-testfixtures.changes
2020-03-27 00:21:40.156146535 +0100
@@ -1,0 +2,49 @@
+Thu Mar 12 08:05:21 UTC 2020 - Tomáš Chvátal <[email protected]>
+
+- Fix build without python2
+
+-------------------------------------------------------------------
+Wed Mar 11 12:16:59 UTC 2020 - [email protected]
+
+- version update to 6.14.0
+ 6.14.0 (24 Feb 2020)
+ --------------------
+ - Add support for non-deterministic logging order when using
:meth:`twisted.LogCapture`.
+ 6.13.1 (20 Feb 2020)
+ --------------------
+ - Fix for using :func:`compare` to compare two-element
:func:`~unittest.mock.call`
+ objects.
+ 6.13.0 (18 Feb 2020)
+ --------------------
+ - Allow any attributes that need to be ignored to be specified directly when
calling
+ :func:`~testfixtures.comparison.compare_object`. This is handy when writing
+ comparers for :func:`compare`.
+ 6.12.1 (16 Feb 2020)
+ --------------------
+ - Fix a bug that occured when using :func:`compare` to compare a string with
a
+ slotted object that had the same :func:`repr` as the string.
+ 6.12.0 (6 Feb 2020)
+ -------------------
+ - Add support for ``universal_newlines``, ``text``, ``encoding`` and
``errors`` to
+ :class:`popen.MockPopen`, but only for Python 3.
+ 6.11.0 (29 Jan 2020)
+ --------------------
+ - :class:`decimal.Decimal` now has better representation when
:func:`compare` displays a failed
+ comparison, particularly on Python 2.
+ - Add support to :func:`compare` for explicitly naming objects to be
compared as ``x`` and ``y``.
+ This allows symmetry with the ``x_label`` and ``y_label`` parameters that
are now documented.
+ - Restore ability for :class:`Comparison` to compare properties and methods,
although these uses
+ are not recommended.
+ 6.10.3 (22 Nov 2019)
+ --------------------
+ - Fix bug where new-style classes had their attributes checked with
:func:`compare` even
+ when they were of different types.
+ 6.10.2 (15 Nov 2019)
+ --------------------
+ - Fix bugs in :func:`compare` when comparing objects which have both
``__slots__``
+ and a ``__dict__``.
+ 6.10.1 (1 Nov 2019)
+ -------------------
+ - Fix edge case where string interning made dictionary comparison output
much less useful.
+
+-------------------------------------------------------------------
Old:
----
testfixtures-6.10.0.tar.gz
New:
----
testfixtures-6.14.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-testfixtures.spec ++++++
--- /var/tmp/diff_new_pack.mQrO5Q/_old 2020-03-27 00:21:42.848147898 +0100
+++ /var/tmp/diff_new_pack.mQrO5Q/_new 2020-03-27 00:21:42.852147900 +0100
@@ -1,7 +1,7 @@
#
# spec file for package python-testfixtures
#
-# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2020 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -17,30 +17,32 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
+%bcond_without python2
Name: python-testfixtures
-Version: 6.10.0
+Version: 6.14.0
Release: 0
Summary: A collection of helpers and mock objects for unit tests and
doc tests
License: MIT
-Group: Development/Languages/Python
URL: https://github.com/Simplistix/testfixtures
Source:
https://files.pythonhosted.org/packages/source/t/testfixtures/testfixtures-%{version}.tar.gz
-BuildRequires: %{python_module pytest >= 3.6}
-BuildRequires: %{python_module setuptools}
-BuildRequires: fdupes
-BuildRequires: python-rpm-macros
-BuildRequires: python2-mock
# Test case dependencies
BuildRequires: %{python_module Django}
BuildRequires: %{python_module Twisted}
+BuildRequires: %{python_module pytest >= 3.6}
BuildRequires: %{python_module pytest-django}
+BuildRequires: %{python_module setuptools}
BuildRequires: %{python_module sybil}
BuildRequires: %{python_module zope.component}
+BuildRequires: fdupes
+BuildRequires: python-rpm-macros
Suggests: python-Django
Suggests: python-Twisted
Suggests: python-sybil
Suggests: python-zope.component
BuildArch: noarch
+%if %{with python2}
+BuildRequires: python2-mock
+%endif
%python_subpackages
%description
@@ -68,7 +70,7 @@
%check
export DJANGO_SETTINGS_MODULE=testfixtures.tests.test_django.settings
-%python_expand $python -m pytest testfixtures/tests
+%pytest testfixtures/tests
%files %{python_files}
%license LICENSE.txt
++++++ testfixtures-6.10.0.tar.gz -> testfixtures-6.14.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/testfixtures-6.10.0/.circleci/config.yml
new/testfixtures-6.14.0/.circleci/config.yml
--- old/testfixtures-6.10.0/.circleci/config.yml 2019-06-19
08:07:46.000000000 +0200
+++ new/testfixtures-6.14.0/.circleci/config.yml 2020-02-24
16:21:42.000000000 +0100
@@ -38,6 +38,9 @@
name: python37
image: circleci/python:3.7
- python/pip-run-tests:
+ name: python38
+ image: circleci/python:3.8
+ - python/pip-run-tests:
name: python36-mock-backport
# so we test the mock monkey patches aren't used:
image: circleci/python:3.6.4
@@ -71,6 +74,7 @@
- python27
- python36
- python37
+ - python38
- python36-mock-backport
- python37-mock-backport
- python27-django-1-9
@@ -99,8 +103,8 @@
- package
- check-package:
- name: check-package-python37
- image: circleci/python:3.7
+ name: check-package-python38
+ image: circleci/python:3.8
requires:
- package
@@ -113,8 +117,8 @@
- package
- check-package:
- name: check-package-python37-mock
- image: circleci/python:3.7
+ name: check-package-python38-mock
+ image: circleci/python:3.8
extra_package: mock
imports: "testfixtures, testfixtures.mock"
requires:
@@ -129,8 +133,8 @@
- package
- check-package:
- name: check-package-python37-django
- image: circleci/python:3.7
+ name: check-package-python38-django
+ image: circleci/python:3.8
extra_package: django
imports: "testfixtures, testfixtures.django"
requires:
@@ -143,9 +147,9 @@
- check-package-python27
- check-package-python27-mock
- check-package-python27-django
- - check-package-python37
- - check-package-python37-mock
- - check-package-python37-django
+ - check-package-python38
+ - check-package-python38-mock
+ - check-package-python38-django
workflows:
push:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/testfixtures-6.10.0/.readthedocs.yml
new/testfixtures-6.14.0/.readthedocs.yml
--- old/testfixtures-6.10.0/.readthedocs.yml 2019-06-19 08:07:46.000000000
+0200
+++ new/testfixtures-6.14.0/.readthedocs.yml 2020-02-24 16:21:42.000000000
+0100
@@ -5,4 +5,6 @@
- method: pip
path: .
extra_requirements:
- - build
+ - docs
+sphinx:
+ fail_on_warning: true
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/testfixtures-6.10.0/CHANGELOG.rst
new/testfixtures-6.14.0/CHANGELOG.rst
--- old/testfixtures-6.10.0/CHANGELOG.rst 2019-06-19 08:07:46.000000000
+0200
+++ new/testfixtures-6.14.0/CHANGELOG.rst 2020-02-24 16:21:42.000000000
+0100
@@ -3,9 +3,72 @@
.. currentmodule:: testfixtures
-6.10.0 (19 Jun 2019)
+6.14.0 (24 Feb 2020)
+--------------------
+
+- Add support for non-deterministic logging order when using
:meth:`twisted.LogCapture`.
+
+6.13.1 (20 Feb 2020)
+--------------------
+
+- Fix for using :func:`compare` to compare two-element
:func:`~unittest.mock.call`
+ objects.
+
+Thanks to Daniel Fortunov for the fix.
+
+6.13.0 (18 Feb 2020)
+--------------------
+
+- Allow any attributes that need to be ignored to be specified directly when
calling
+ :func:`~testfixtures.comparison.compare_object`. This is handy when writing
+ comparers for :func:`compare`.
+
+6.12.1 (16 Feb 2020)
+--------------------
+
+- Fix a bug that occured when using :func:`compare` to compare a string with a
+ slotted object that had the same :func:`repr` as the string.
+
+6.12.0 (6 Feb 2020)
-------------------
+- Add support for ``universal_newlines``, ``text``, ``encoding`` and
``errors`` to
+ :class:`popen.MockPopen`, but only for Python 3.
+
+6.11.0 (29 Jan 2020)
+--------------------
+
+- :class:`decimal.Decimal` now has better representation when :func:`compare`
displays a failed
+ comparison, particularly on Python 2.
+
+- Add support to :func:`compare` for explicitly naming objects to be compared
as ``x`` and ``y``.
+ This allows symmetry with the ``x_label`` and ``y_label`` parameters that
are now documented.
+
+- Restore ability for :class:`Comparison` to compare properties and methods,
although these uses
+ are not recommended.
+
+Thanks to Daniel Fortunov for all of the above.
+
+6.10.3 (22 Nov 2019)
+--------------------
+
+- Fix bug where new-style classes had their attributes checked with
:func:`compare` even
+ when they were of different types.
+
+6.10.2 (15 Nov 2019)
+--------------------
+
+- Fix bugs in :func:`compare` when comparing objects which have both
``__slots__``
+ and a ``__dict__``.
+
+6.10.1 (1 Nov 2019)
+-------------------
+
+- Fix edge case where string interning made dictionary comparison output much
less useful.
+
+6.10.0 (19 Jun 2019)
+--------------------
+
- Better feedback where objects do not :func:`compare` equal but do have the
same
representation.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/testfixtures-6.10.0/PKG-INFO
new/testfixtures-6.14.0/PKG-INFO
--- old/testfixtures-6.10.0/PKG-INFO 2019-06-19 08:07:50.000000000 +0200
+++ new/testfixtures-6.14.0/PKG-INFO 2020-02-24 16:21:45.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: testfixtures
-Version: 6.10.0
+Version: 6.14.0
Summary: A collection of helpers and mock objects for unit tests and doc tests.
Home-page: https://github.com/Simplistix/testfixtures
Author: Chris Withers
@@ -75,6 +75,7 @@
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
Provides-Extra: test
Provides-Extra: docs
Provides-Extra: build
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/testfixtures-6.10.0/docs/api.txt
new/testfixtures-6.14.0/docs/api.txt
--- old/testfixtures-6.10.0/docs/api.txt 2019-06-19 08:07:46.000000000
+0200
+++ new/testfixtures-6.14.0/docs/api.txt 2020-02-24 16:21:42.000000000
+0100
@@ -3,6 +3,8 @@
.. currentmodule:: testfixtures
+.. autofunction:: compare(x, y, prefix=None, suffix=None, raises=True,
recursive=True, strict=False, comparers=None, **kw)
+
.. autoclass:: Comparison
.. autoclass:: LogCapture
@@ -42,30 +44,6 @@
.. autoclass:: TempDirectory
:members:
-.. autofunction:: compare(x, y, prefix=None, suffix=None, raises=True,
recursive=True, strict=False, comparers=None, **kw)
-
-.. autofunction:: testfixtures.comparison.register
-
-.. autofunction:: testfixtures.comparison.compare_simple
-
-.. autofunction:: testfixtures.comparison.compare_object
-
-.. autofunction:: testfixtures.comparison.compare_exception
-
-.. autofunction:: testfixtures.comparison.compare_with_type
-
-.. autofunction:: testfixtures.comparison.compare_sequence
-
-.. autofunction:: testfixtures.comparison.compare_generator
-
-.. autofunction:: testfixtures.comparison.compare_tuple
-
-.. autofunction:: testfixtures.comparison.compare_dict
-
-.. autofunction:: testfixtures.comparison.compare_set
-
-.. autofunction:: testfixtures.comparison.compare_text
-
.. autofunction:: diff
.. autofunction:: generator
@@ -347,6 +325,32 @@
A singleton used to represent the absence of a particular attribute.
+
+.. automodule:: testfixtures.comparison
+
+.. autofunction:: testfixtures.comparison.register
+
+.. autofunction:: testfixtures.comparison.compare_simple
+
+.. autofunction:: testfixtures.comparison.compare_object
+
+.. autofunction:: testfixtures.comparison.compare_exception
+
+.. autofunction:: testfixtures.comparison.compare_with_type
+
+.. autofunction:: testfixtures.comparison.compare_sequence
+
+.. autofunction:: testfixtures.comparison.compare_generator
+
+.. autofunction:: testfixtures.comparison.compare_tuple
+
+.. autofunction:: testfixtures.comparison.compare_dict
+
+.. autofunction:: testfixtures.comparison.compare_set
+
+.. autofunction:: testfixtures.comparison.compare_text
+
+
.. currentmodule:: testfixtures.popen
.. automodule:: testfixtures.popen
@@ -361,8 +365,10 @@
automatically set to make comparing django
:class:`~django.db.models.Model`
instances easier.
+
.. automodule:: testfixtures.mock
+
.. automodule:: testfixtures.twisted
:member-order: bysource
:members:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/testfixtures-6.10.0/setup.cfg
new/testfixtures-6.14.0/setup.cfg
--- old/testfixtures-6.10.0/setup.cfg 2019-06-19 08:07:50.000000000 +0200
+++ new/testfixtures-6.14.0/setup.cfg 2020-02-24 16:21:45.000000000 +0100
@@ -6,6 +6,7 @@
django_settings_module = testfixtures.tests.test_django.settings
filterwarnings =
ignore::DeprecationWarning
+ ignore::PendingDeprecationWarning
[egg_info]
tag_build =
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/testfixtures-6.10.0/setup.py
new/testfixtures-6.14.0/setup.py
--- old/testfixtures-6.10.0/setup.py 2019-06-19 08:07:46.000000000 +0200
+++ new/testfixtures-6.14.0/setup.py 2020-02-24 16:21:42.000000000 +0100
@@ -8,6 +8,15 @@
name = 'testfixtures'
base_dir = os.path.dirname(__file__)
+optional = [
+ 'mock;python_version<"3"',
+ 'zope.component',
+ 'django<2;python_version<"3"',
+ 'django;python_version>="3"',
+ 'sybil',
+ 'twisted'
+]
+
setup(
name=name,
version=open(os.path.join(base_dir, name, 'version.txt')).read().strip(),
@@ -27,6 +36,7 @@
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
+ 'Programming Language :: Python :: 3.8',
],
packages=find_packages(),
zip_safe=False,
@@ -35,13 +45,8 @@
test=['pytest>=3.6',
'pytest-cov',
'pytest-django',
- 'mock;python_version<"3"',
- 'sybil',
- 'zope.component',
- 'django<2;python_version<"3"',
- 'django;python_version>="3"',
- 'twisted'],
- docs=['sphinx'],
+ ]+optional,
+ docs=['sphinx']+optional,
build=['setuptools-git', 'wheel', 'twine']
)
)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/testfixtures-6.10.0/testfixtures/comparison.py
new/testfixtures-6.14.0/testfixtures/comparison.py
--- old/testfixtures-6.10.0/testfixtures/comparison.py 2019-06-19
08:07:46.000000000 +0200
+++ new/testfixtures-6.14.0/testfixtures/comparison.py 2020-02-24
16:21:42.000000000 +0100
@@ -1,3 +1,9 @@
+"""
+testfixtures.comparison
+-----------------------
+"""
+
+from decimal import Decimal
from difflib import unified_diff
from functools import partial
from pprint import pformat
@@ -19,6 +25,8 @@
repr_x = repr(x)
repr_y = repr(y)
if repr_x == repr_y:
+ if type(x) is not type(y):
+ return compare_with_type(x, y, context)
x_attrs = _extract_attrs(x)
y_attrs = _extract_attrs(y)
diff = _compare_mapping(x_attrs, y_attrs, context, x,
@@ -32,33 +40,45 @@
def _extract_attrs(obj, ignore=None):
+ try:
+ attrs = vars(obj).copy()
+ except TypeError:
+ attrs = None
+ else:
+ if isinstance(obj, BaseException):
+ attrs['args'] = obj.args
+
has_slots = getattr(obj, '__slots__', not_there) is not not_there
if has_slots:
slots = set()
for cls in type(obj).__mro__:
slots.update(getattr(cls, '__slots__', ()))
- attrs = {}
+ if slots and attrs is None:
+ attrs = {}
for n in slots:
value = getattr(obj, n, not_there)
if value is not not_there:
attrs[n] = value
- else:
- try:
- attrs = vars(obj).copy()
- except TypeError:
- return None
- else:
- if isinstance(obj, BaseException):
- attrs['args'] = obj.args
+
+ if attrs is None:
+ return None
+
if ignore is not None:
- if isinstance(ignore, dict):
- ignore = ignore.get(type(obj), ())
for attr in ignore:
attrs.pop(attr, None)
return attrs
-def compare_object(x, y, context):
+def _attrs_to_ignore(context, ignore_attributes, obj):
+ ignore = context.get_option('ignore_attributes', ())
+ if isinstance(ignore, dict):
+ ignore = ignore.get(type(obj), ())
+ ignore = set(ignore)
+ ignore.update(ignore_attributes)
+ return ignore
+
+
+def compare_object(x, y, context, ignore_attributes=()):
"""
Compare the two supplied objects based on their type and attributes.
@@ -67,12 +87,17 @@
Either a sequence of strings containing attribute names to be ignored
when comparing or a mapping of type to sequence of strings containing
attribute names to be ignored when comparing that type.
+
+ This may be specified as either a parameter to this function or in the
+ ``context``. If specified in both, they will both apply with precedence
+ given to whatever is specified is specified as a parameter.
+ If specified as a parameter to this fucntion, it may only be a list of
+ strings.
"""
- ignore_attributes = context.get_option('ignore_attributes', ())
- if type(x) is not type(y) or isinstance(x, ClassType):
+ if type(x) is not type(y) or isinstance(x, (ClassType, type)):
return compare_simple(x, y, context)
- x_attrs = _extract_attrs(x, ignore_attributes)
- y_attrs = _extract_attrs(y, ignore_attributes)
+ x_attrs = _extract_attrs(x, _attrs_to_ignore(context, ignore_attributes,
x))
+ y_attrs = _extract_attrs(y, _attrs_to_ignore(context, ignore_attributes,
y))
if x_attrs is None or y_attrs is None or not (x_attrs and y_attrs):
return compare_simple(x, y, context)
if x_attrs != y_attrs:
@@ -341,8 +366,17 @@
def compare_call(x, y, context):
if x == y:
return
- x_name, x_args, x_kw = x
- y_name, y_args, y_kw = y
+
+ def extract(call):
+ try:
+ name, args, kwargs = call
+ except ValueError:
+ name = None
+ args, kwargs = call
+ return name, args, kwargs
+
+ x_name, x_args, x_kw = extract(x)
+ y_name, y_args, y_kw = extract(y)
if x_name == y_name and x_args == y_args and x_kw == y_kw:
return compare_call(getattr(x, parent_name), getattr(y, parent_name),
context)
return compare_text(repr(x), repr(y), context)
@@ -372,6 +406,7 @@
Unicode: compare_text,
int: compare_simple,
float: compare_simple,
+ Decimal: compare_simple,
GeneratorType: compare_generator,
mock_call.__class__: compare_call,
unittest_mock_call.__class__: compare_call,
@@ -450,6 +485,13 @@
if actual is not not_there:
possible.append(actual)
+ x = self.options.pop('x', not_there)
+ if x is not not_there:
+ possible.append(x)
+ y = self.options.pop('y', not_there)
+ if y is not not_there:
+ possible.append(y)
+
if len(possible) != 2:
message = 'Exactly two objects needed, you supplied:'
if possible:
@@ -496,6 +538,9 @@
return '\n\nWhile comparing %s: ' % ''.join(self.breadcrumbs[1:])
def seen(self, x, y):
+ # don't get confused by string interning:
+ if isinstance(x, basestring) and isinstance(y, basestring):
+ return False
key = id(x), id(y)
if key in self._seen:
return True
@@ -547,14 +592,17 @@
def compare(*args, **kw):
"""
- Compare the two arguments passed either positionally or using
- explicit ``expected`` and ``actual`` keyword paramaters. An
- :class:`AssertionError` will be raised if they are not the same.
- The :class:`AssertionError` raised will attempt to provide
+ Compare two objects, raising an :class:`AssertionError` if they are not
+ the same. The :class:`AssertionError` raised will attempt to provide
descriptions of the differences found.
+ The two objects to compare can be passed either positionally or using
+ explicit keyword arguments named ``x`` and ``y``, or ``expected`` and
+ ``actual``.
+
Any other keyword parameters supplied will be passed to the functions
- that end up doing the comparison. See the API documentation below
+ that end up doing the comparison. See the
+ :mod:`API documentation below <testfixtures.comparison>`
for details of these.
:param prefix: If provided, in the event of an :class:`AssertionError`
@@ -565,6 +613,18 @@
being raised, the suffix supplied will be appended to the
message in the :class:`AssertionError`.
+ :param x_label: If provided, in the event of an :class:`AssertionError`
+ being raised, the object passed as the first positional
+ argument, or ``x`` keyword argument, will be labelled
+ with this string in the message in the
+ :class:`AssertionError`.
+
+ :param x_label: If provided, in the event of an :class:`AssertionError`
+ being raised, the object passed as the second positional
+ argument, or ``y`` keyword argument, will be labelled
+ with this string in the message in the
+ :class:`AssertionError`.
+
:param raises: If ``False``, the message that would be raised in the
:class:`AssertionError` will be returned instead of the
exception being raised.
@@ -666,15 +726,19 @@
if self.v is None:
return True
+ remaining_keys = set(self.v.keys())
if self.strict:
v = _extract_attrs(other)
+ remaining_keys -= set(v.keys())
else:
v = {}
- for k in self.v.keys():
- try:
- v[k] = getattr(other, k)
- except AttributeError:
- pass
+
+ while remaining_keys:
+ k = remaining_keys.pop()
+ try:
+ v[k] = getattr(other, k)
+ except AttributeError:
+ pass
kw = {'x_label': 'Comparison', 'y_label': 'actual'}
context = CompareContext(kw)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/testfixtures-6.10.0/testfixtures/compat.py
new/testfixtures-6.14.0/testfixtures/compat.py
--- old/testfixtures-6.10.0/testfixtures/compat.py 2019-06-19
08:07:46.000000000 +0200
+++ new/testfixtures-6.14.0/testfixtures/compat.py 2020-02-24
16:21:42.000000000 +0100
@@ -28,6 +28,7 @@
from itertools import zip_longest
from functools import reduce
from collections.abc import Iterable
+ from abc import ABC
else:
@@ -50,3 +51,5 @@
from itertools import izip_longest as zip_longest
reduce = reduce
from collections import Iterable
+ from abc import ABCMeta
+ ABC = ABCMeta('ABC', (object,), {}) # compatible with Python 2 *and* 3
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/testfixtures-6.10.0/testfixtures/django.py
new/testfixtures-6.14.0/testfixtures/django.py
--- old/testfixtures-6.10.0/testfixtures/django.py 2019-06-19
08:07:46.000000000 +0200
+++ new/testfixtures-6.14.0/testfixtures/django.py 2020-02-24
16:21:42.000000000 +0100
@@ -1,3 +1,7 @@
+"""
+testfixtures.django
+-------------------
+"""
from __future__ import absolute_import
from functools import partial
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/testfixtures-6.10.0/testfixtures/popen.py
new/testfixtures-6.14.0/testfixtures/popen.py
--- old/testfixtures-6.10.0/testfixtures/popen.py 2019-06-19
08:07:46.000000000 +0200
+++ new/testfixtures-6.14.0/testfixtures/popen.py 2020-02-24
16:21:42.000000000 +0100
@@ -1,9 +1,15 @@
+"""
+testfixtures.popen
+------------------
+"""
+
import pipes
from functools import wraps, partial
+from io import TextIOWrapper
from itertools import chain
from subprocess import STDOUT, PIPE
from tempfile import TemporaryFile
-from testfixtures.compat import basestring, PY3, zip_longest, reduce
+from testfixtures.compat import basestring, PY3, zip_longest, reduce, PY2
from testfixtures.utils import extend_docstring
from .mock import Mock, call
@@ -61,7 +67,7 @@
env=None, universal_newlines=False,
startupinfo=None, creationflags=0, restore_signals=True,
start_new_session=False, pass_fds=(),
- encoding=None, errors=None):
+ encoding=None, errors=None, text=None):
self.mock = Mock()
self.class_instance_mock = mock_class.mock.Popen_instance
#: A :func:`unittest.mock.call` representing the call made to
instantiate
@@ -105,6 +111,8 @@
value.write(mock_value)
value.flush()
value.seek(0)
+ if PY3 and (universal_newlines or text or encoding):
+ value = TextIOWrapper(value, encoding=encoding,
errors=errors)
setattr(self, name, value)
if stdin == PIPE:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/testfixtures-6.10.0/testfixtures/tests/test_compare.py
new/testfixtures-6.14.0/testfixtures/tests/test_compare.py
--- old/testfixtures-6.10.0/testfixtures/tests/test_compare.py 2019-06-19
08:07:46.000000000 +0200
+++ new/testfixtures-6.14.0/testfixtures/tests/test_compare.py 2020-02-24
16:21:42.000000000 +0100
@@ -1,4 +1,5 @@
from datetime import date, datetime
+from decimal import Decimal
from functools import partial
@@ -19,9 +20,9 @@
from testfixtures.compat import (
class_type_name, exception_module, PY3, xrange,
BytesLiteral, UnicodeLiteral,
- PY2, PY_37_PLUS
+ PY2, PY_37_PLUS, ABC
)
-from testfixtures.comparison import compare_sequence
+from testfixtures.comparison import compare_sequence, compare_object
from unittest import TestCase
hexaddr = compile('0x[0-9A-Fa-f]+')
@@ -36,31 +37,41 @@
_compare = compare
-class CompareHelper(object):
- def check_raises(self, x=marker, y=marker, message=None, regex=None,
- compare=compare, **kw):
- args = []
- for value in x, y:
- if value is not marker:
- args.append(value)
- try:
- compare(*args, **kw)
- except Exception as e:
- if not isinstance(e, AssertionError): # pragma: no cover
- raise
- actual = hexsub(e.args[0])
- if message is not None:
- # handy for debugging, but can't be relied on for tests!
- _compare(actual, expected=message, show_whitespace=True)
- assert actual == message
- else:
- if not regex.match(actual): # pragma: no cover
- raise AssertionError(
- '%r did not match %r' % (actual, regex.pattern)
- )
+def check_raises(x=marker, y=marker, message=None, regex=None,
+ compare=compare, **kw):
+ args = []
+ for value in x, y:
+ if value is not marker:
+ args.append(value)
+ for value in 'x', 'y':
+ explicit = 'explicit_{}'.format(value)
+ if explicit in kw:
+ kw[value] = kw[explicit]
+ del kw[explicit]
+ try:
+ compare(*args, **kw)
+ except Exception as e:
+ if not isinstance(e, AssertionError): # pragma: no cover
+ raise
+ actual = hexsub(e.args[0])
+ if message is not None:
+ # handy for debugging, but can't be relied on for tests!
+ _compare(actual, expected=message, show_whitespace=True)
+ assert actual == message
else:
- raise AssertionError('No exception raised!')
+ if not regex.match(actual): # pragma: no cover
+ raise AssertionError(
+ '%r did not match %r' % (actual, regex.pattern)
+ )
+ else:
+ raise AssertionError('No exception raised!')
+
+
+class CompareHelper(object):
+
+ def check_raises(self, *args, **kw):
+ check_raises(*args, **kw)
class TestCompare(CompareHelper, TestCase):
@@ -84,6 +95,10 @@
def test_number_different(self):
self.check_raises(1, 2, '1 != 2')
+ def test_decimal_different(self):
+ self.check_raises(Decimal(1), Decimal(2),
+ "Decimal('1') != Decimal('2')")
+
def test_different_with_labels(self):
self.check_raises(1, 2, '1 (expected) != 2 (actual)',
x_label='expected', y_label='actual')
@@ -291,6 +306,30 @@
"[4]"
)
+ def test_list_different_float(self):
+ self.check_raises(
+ [1, 2, 3.0], [1, 2, 4.0],
+ "sequence not as expected:\n\n"
+ "same:\n"
+ "[1, 2]\n\n"
+ "first:\n"
+ "[3.0]\n\n"
+ "second:\n"
+ "[4.0]"
+ )
+
+ def test_list_different_decimal(self):
+ self.check_raises(
+ [1, 2, Decimal(3)], [1, 2, Decimal(4)],
+ "sequence not as expected:\n\n"
+ "same:\n"
+ "[1, 2]\n\n"
+ "first:\n"
+ "[Decimal('3')]\n\n"
+ "second:\n"
+ "[Decimal('4')]"
+ )
+
def test_list_totally_different(self):
self.check_raises(
[1], [2],
@@ -748,6 +787,34 @@
pass
self.check_raises(X, Y, expected)
+ def test_new_style_classes_same(self):
+ class X(object):
+ pass
+ compare(X, X)
+
+ def test_new_style_classes_different(self):
+ if PY3:
+ expected = (
+ "<class 'testfixtures.tests.test_compare.TestCompare."
+ "test_new_style_classes_different.<locals>.X'>"
+ " != "
+ "<class 'testfixtures.tests.test_compare.TestCompare."
+ "test_new_style_classes_different.<locals>.Y'>"
+ )
+ else:
+ expected = (
+ "<class 'testfixtures.tests.test_compare.X'>"
+ " != "
+ "<class 'testfixtures.tests.test_compare.Y'>"
+ )
+
+ class X(object):
+ pass
+
+ class Y(object):
+ pass
+ self.check_raises(X, Y, expected)
+
def test_show_whitespace(self):
# does nothing! ;-)
self.check_raises(
@@ -1325,13 +1392,18 @@
message="'x' (expected) != 'y' (actual)")
def test_explicit_both(self):
- self.check_raises(message="'x' (expected) != 'y' (actual)",
- expected='x', actual='y')
+ self.check_raises(expected='x', actual='y',
+ message="'x' (expected) != 'y' (actual)")
+
+ def test_implicit_and_labels(self):
+ self.check_raises('x', 'y',
+ x_label='x_label', y_label='y_label',
+ message="'x' (x_label) != 'y' (y_label)")
def test_explicit_and_labels(self):
- self.check_raises(message="'x' (x_label) != 'y' (y_label)",
- expected='x', actual='y',
- x_label='x_label', y_label='y_label')
+ self.check_raises(explicit_x='x', explicit_y='y',
+ x_label='x_label', y_label='y_label',
+ message="'x' (x_label) != 'y' (y_label)")
def test_invalid_two_args_expected(self):
with ShouldRaise(TypeError(
@@ -1447,8 +1519,8 @@
compare(m.mock_calls, m.mock_calls, strict=True)
def test_calls_different(self):
- m1 =Mock()
- m2 =Mock()
+ m1 = Mock()
+ m2 = Mock()
m1.foo(1, 2, x=3, y=4)
m2.bar(1, 3, x=7, y=4)
@@ -1472,6 +1544,16 @@
"'call.bar(1, 3, x=7, y=4)'"
)
+ def test_call_args_different(self):
+ m = Mock()
+ m.foo(1)
+
+ self.check_raises(
+ m.foo.call_args,
+ call(2),
+ "'call(1)' != 'call(2)'"
+ )
+
def test_compare_arbitrary_nested_same(self):
compare(SampleClassA([SampleClassB()]),
SampleClassA([SampleClassB()]))
@@ -1619,6 +1701,26 @@
compare(Child(1), Child(1))
+ def test_slots_and_attrs(self):
+
+ class Parent(object):
+ __slots__ = ('a',)
+
+ class Child(Parent):
+ def __init__(self, a, b):
+ self.a = a
+ self.b = b
+
+ self.check_raises(Child(1, 2), Child(1, 3), message=(
+ 'Child not as expected:\n'
+ '\n'
+ 'attributes same:\n'
+ "['a']\n"
+ '\n'
+ 'attributes differ:\n'
+ "'b': 2 != 3"
+ ))
+
def test_partial_callable_different(self):
def foo(x): pass
@@ -1719,6 +1821,36 @@
message="Both expected and actual appear as 'Wut', but are not
equal!"
)
+ def test_string_with_slotted(self):
+
+ class Slotted(object):
+ __slots__ = ['foo']
+ def __init__(self, foo):
+ self.foo = foo
+ def __repr__(self):
+ return repr(self.foo)
+
+ self.check_raises(
+ 'foo',
+ Slotted('foo'),
+ "'foo' (%s) != 'foo' (%s)" % (repr(str), repr(Slotted))
+ )
+
+ def test_not_recursive(self):
+ self.check_raises(
+ {1: 'foo', 2: 'foo'},
+ {1: 'bar', 2: 'bar'},
+ "dict not as expected:\n"
+ "\n"
+ "values differ:\n"
+ "1: 'foo' != 'bar'\n"
+ "2: 'foo' != 'bar'\n"
+ "\n"
+ "While comparing [1]: 'foo' != 'bar'"
+ "\n\n"
+ "While comparing [2]: 'foo' != 'bar'"
+ )
+
class TestIgnore(CompareHelper):
@@ -1757,3 +1889,85 @@
"'id': 1 != 2",
ignore_attributes=ignore
)
+
+
+class TestCompareObject(object):
+
+ class Thing(object):
+ def __init__(self, **kw):
+ for k, v in kw.items():
+ setattr(self, k, v)
+
+ def test_ignore(self):
+ def compare_thing(x, y, context):
+ return compare_object(x, y, context, ignore_attributes=['y'])
+ compare(self.Thing(x=1, y=2), self.Thing(x=1, y=3),
+ comparers={self.Thing: compare_thing})
+
+ def test_ignore_dict_context_list_param(self):
+ def compare_thing(x, y, context):
+ return compare_object(x, y, context, ignore_attributes=['y'])
+ compare(self.Thing(x=1, y=2, z=3), self.Thing(x=1, y=4, z=5),
+ comparers={self.Thing: compare_thing},
+ ignore_attributes={self.Thing: ['z']})
+
+ def test_ignore_list_context_list_param(self):
+ def compare_thing(x, y, context):
+ return compare_object(x, y, context, ignore_attributes=['y'])
+ compare(self.Thing(x=1, y=2, z=3), self.Thing(x=1, y=4, z=5),
+ comparers={self.Thing: compare_thing},
+ ignore_attributes=['z'])
+
+
+class BaseClass(ABC):
+ pass
+
+
+class MyDerivedClass(BaseClass):
+
+ def __init__(self, thing):
+ self.thing = thing
+
+
+class ConcreteBaseClass(object): pass
+
+
+class ConcreteDerivedClass(ConcreteBaseClass):
+
+ def __init__(self, thing):
+ self.thing = thing
+
+
+class TestBaseClasses(CompareHelper):
+
+ def test_abc_equal(self):
+ thing1 = MyDerivedClass(1)
+ thing2 = MyDerivedClass(1)
+
+ compare(thing1, thing2)
+
+ def test_abc_unequal(self):
+ thing1 = MyDerivedClass(1)
+ thing2 = MyDerivedClass(2)
+
+ self.check_raises(thing1, thing2, message=(
+ "MyDerivedClass not as expected:\n\n"
+ "attributes differ:\n"
+ "'thing': 1 != 2"
+ ))
+
+ def test_concrete_equal(self):
+ thing1 = ConcreteDerivedClass(1)
+ thing2 = ConcreteDerivedClass(1)
+
+ compare(thing1, thing2)
+
+ def test_concrete_unequal(self):
+ thing1 = ConcreteDerivedClass(1)
+ thing2 = ConcreteDerivedClass(2)
+
+ self.check_raises(thing1, thing2, message=(
+ "ConcreteDerivedClass not as expected:\n\n"
+ "attributes differ:\n"
+ "'thing': 1 != 2"
+ ))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/testfixtures-6.10.0/testfixtures/tests/test_comparison.py
new/testfixtures-6.14.0/testfixtures/tests/test_comparison.py
--- old/testfixtures-6.10.0/testfixtures/tests/test_comparison.py
2019-06-19 08:07:46.000000000 +0200
+++ new/testfixtures-6.14.0/testfixtures/tests/test_comparison.py
2020-02-24 16:21:42.000000000 +0100
@@ -427,6 +427,85 @@
AClass(1, 2),
)
+ def run_property_equal_test(self, strict):
+ class SomeClass(object):
+ @property
+ def prop(self):
+ return 1
+
+ self.assertEqual(
+ C(SomeClass, prop=1, strict=strict),
+ SomeClass()
+ )
+
+ def test_property_equal_strict(self):
+ self.run_property_equal_test(strict=True)
+
+ def test_property_equal_not_strict(self):
+ self.run_property_equal_test(strict=False)
+
+ def run_property_not_equal_test(self, strict):
+ class SomeClass(object):
+ @property
+ def prop(self):
+ return 1
+
+ c = C(SomeClass, prop=2, strict=strict)
+ self.assertNotEqual(c, SomeClass())
+ compare_repr(
+ c,
+ "\n"
+ "<C(failed):testfixtures.tests.test_comparison.SomeClass>\n"
+ "attributes differ:\n"
+ "'prop': 2 (Comparison) != 1 (actual)\n"
+ "</C>")
+
+ def test_property_not_equal_strict(self):
+ self.run_property_not_equal_test(strict=True)
+
+ def test_property_not_equal_not_strict(self):
+ self.run_property_not_equal_test(strict=False)
+
+ def run_method_equal_test(self, strict):
+ class SomeClass(object):
+ def method(self):
+ pass # pragma: no cover
+
+ instance = SomeClass()
+ self.assertEqual(
+ C(SomeClass, method=instance.method, strict=strict),
+ instance
+ )
+
+ def test_method_equal_strict(self):
+ self.run_method_equal_test(strict=True)
+
+ def test_method_equal_not_strict(self):
+ self.run_method_equal_test(strict=False)
+
+ def run_method_not_equal_test(self, strict):
+ class SomeClass(object): pass
+ instance = SomeClass()
+ instance.method = min
+
+ c = C(SomeClass, method=max, strict=strict)
+ self.assertNotEqual(c, instance)
+ compare_repr(
+ c,
+ "\n"
+ "<C(failed):testfixtures.tests.test_comparison.SomeClass>\n"
+ "attributes differ:\n"
+ "'method': <built-in function max> (Comparison)"
+ " != <built-in function min> (actual)\n"
+ "</C>"
+ )
+
+ def test_method_not_equal_strict(self):
+ self.run_method_not_equal_test(strict=True)
+
+ def test_method_not_equal_not_strict(self):
+ self.run_method_not_equal_test(strict=False)
+
def test_exception(self):
self.assertEqual(
ValueError('foo'),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/testfixtures-6.10.0/testfixtures/tests/test_popen.py
new/testfixtures-6.14.0/testfixtures/tests/test_popen.py
--- old/testfixtures-6.10.0/testfixtures/tests/test_popen.py 2019-06-19
08:07:46.000000000 +0200
+++ new/testfixtures-6.14.0/testfixtures/tests/test_popen.py 2020-02-24
16:21:42.000000000 +0100
@@ -185,6 +185,54 @@
call.Popen('a command', shell=True, stderr=PIPE, stdout=PIPE),
], Popen.mock.method_calls)
+ def test_communicate_text_mode(self):
+ Popen = MockPopen()
+ Popen.set_command('a command', stdout=b'foo', stderr=b'bar')
+ # usage
+ process = Popen('a command', stdout=PIPE, stderr=PIPE, text=True)
+ actual = process.communicate()
+ # check
+ compare(actual, expected=(u'foo', u'bar'))
+
+ def test_communicate_universal_newlines(self):
+ Popen = MockPopen()
+ Popen.set_command('a command', stdout=b'foo', stderr=b'bar')
+ # usage
+ process = Popen('a command', stdout=PIPE, stderr=PIPE,
universal_newlines=True)
+ actual = process.communicate()
+ # check
+ compare(actual, expected=(u'foo', u'bar'))
+
+ def test_communicate_encoding(self):
+ Popen = MockPopen()
+ Popen.set_command('a command', stdout=b'foo', stderr=b'bar')
+ # usage
+ process = Popen('a command', stdout=PIPE, stderr=PIPE,
encoding='ascii')
+ actual = process.communicate()
+ # check
+ compare(actual, expected=(u'foo', u'bar'))
+
+ def test_communicate_encoding_with_errors(self):
+ Popen = MockPopen()
+ Popen.set_command('a command', stdout=b'\xa3', stderr=b'\xa3')
+ # usage
+ process = Popen('a command', stdout=PIPE, stderr=PIPE,
encoding='ascii', errors='ignore')
+ actual = process.communicate()
+ # check
+ if PY2:
+ compare(actual, expected=(b'\xa3', b'\xa3'))
+ else:
+ compare(actual, expected=(u'', u''))
+
+ def test_read_from_stdout_and_stderr_text_mode(self):
+ Popen = MockPopen()
+ Popen.set_command('a command', stdout=b'foo', stderr=b'bar')
+ # usage
+ process = Popen('a command', stdout=PIPE, stderr=PIPE, text=True)
+ actual = process.stdout.read(), process.stderr.read()
+ # check
+ compare(actual, expected=(u'foo', u'bar'))
+
def test_write_to_stdin(self):
# setup
Popen = MockPopen()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/testfixtures-6.10.0/testfixtures/tests/test_twisted.py
new/testfixtures-6.14.0/testfixtures/tests/test_twisted.py
--- old/testfixtures-6.10.0/testfixtures/tests/test_twisted.py 2019-06-19
08:07:46.000000000 +0200
+++ new/testfixtures-6.14.0/testfixtures/tests/test_twisted.py 2020-02-24
16:21:42.000000000 +0100
@@ -2,7 +2,8 @@
from twisted.python.failure import Failure
from twisted.trial.unittest import TestCase
-from testfixtures import compare, ShouldRaise
+from testfixtures import compare, ShouldRaise, StringComparison as S,
ShouldAssert
+from testfixtures.compat import PY3
from testfixtures.twisted import LogCapture, INFO
log = Logger()
@@ -80,3 +81,87 @@
capture.raise_logged_failure(start_index=1)
compare(s.raised.value, expected=TypeError('all gone wrong'))
self.flushLoggedErrors()
+
+ def test_order_doesnt_matter_ok(self):
+ capture = LogCapture.make(self)
+ log.info('Failed to send BAR')
+ log.info('Sent FOO, length 1234')
+ log.info('Sent 1 Messages')
+ capture.check(
+ (INFO, S('Sent FOO, length \d+')),
+ (INFO, 'Failed to send BAR'),
+ (INFO, 'Sent 1 Messages'),
+ order_matters=False
+ )
+
+ def test_order_doesnt_matter_failure(self):
+ capture = LogCapture.make(self)
+ log.info('Failed to send BAR')
+ log.info('Sent FOO, length 1234')
+ log.info('Sent 1 Messages')
+ with ShouldAssert(
+ "entries not as expected:\n"
+ "\n"
+ "expected and found:\n"
+ "[(<LogLevel=info>, 'Failed to send BAR'), (<LogLevel=info>, 'Sent
1 Messages')]\n"
+ "\n"
+ "expected but not found:\n"
+ "[(<LogLevel=info>, <S:Sent FOO, length abc>)]\n"
+ "\n"
+ "other entries:\n"
+ "[(<LogLevel=info>, {}'Sent FOO, length 1234')]".format('' if PY3
else 'u')
+ ):
+ capture.check(
+ (INFO, S('Sent FOO, length abc')),
+ (INFO, 'Failed to send BAR'),
+ (INFO, 'Sent 1 Messages'),
+ order_matters=False
+ )
+
+ def test_order_doesnt_matter_extra_in_expected(self):
+ capture = LogCapture.make(self)
+ log.info('Failed to send BAR')
+ log.info('Sent FOO, length 1234')
+ with ShouldAssert(
+ "entries not as expected:\n"
+ "\n"
+ "expected and found:\n"
+ "[(<LogLevel=info>, 'Failed to send BAR'),\n"
+ " (<LogLevel=info>, <S:Sent FOO, length 1234>)]\n"
+ "\n"
+ "expected but not found:\n"
+ "[(<LogLevel=info>, 'Sent 1 Messages')]\n"
+ "\n"
+ "other entries:\n"
+ "[]"
+ ):
+ capture.check(
+ (INFO, S('Sent FOO, length 1234')),
+ (INFO, 'Failed to send BAR'),
+ (INFO, 'Sent 1 Messages'),
+ order_matters=False
+ )
+
+ def test_order_doesnt_matter_extra_in_actual(self):
+ capture = LogCapture.make(self)
+ log.info('Failed to send BAR')
+ log.info('Sent FOO, length 1234')
+ log.info('Sent 1 Messages')
+ with ShouldAssert(
+ "entries not as expected:\n"
+ "\n"
+ "expected and found:\n"
+ "[(<LogLevel=info>, 'Failed to send BAR'), (<LogLevel=info>, 'Sent
1 Messages')]\n"
+ "\n"
+ "expected but not found:\n"
+ "[(<LogLevel=info>, <S:Sent FOO, length abc>)]\n"
+ "\n"
+ "other entries:\n"
+ "[(<LogLevel=info>, {}'Sent FOO, length 1234')]".format('' if PY3
else 'u')
+ ):
+ capture.check(
+ (INFO, S('Sent FOO, length abc')),
+ (INFO, 'Failed to send BAR'),
+ (INFO, 'Sent 1 Messages'),
+ order_matters=False
+ )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/testfixtures-6.10.0/testfixtures/twisted.py
new/testfixtures-6.14.0/testfixtures/twisted.py
--- old/testfixtures-6.10.0/testfixtures/twisted.py 2019-06-19
08:07:46.000000000 +0200
+++ new/testfixtures-6.14.0/testfixtures/twisted.py 2020-02-24
16:21:42.000000000 +0100
@@ -6,6 +6,8 @@
"""
from __future__ import absolute_import
+from pprint import pformat
+
from . import compare
from twisted.logger import globalLogPublisher, formatEvent, LogLevel
@@ -40,19 +42,44 @@
"Stop capturing."
globalLogPublisher._observers = self.original_observers
- def check(self, *expected):
+ def check(self, *expected, **kw):
"""
Check captured events against those supplied. Please see the
``fields`` parameter
to the constructor to see how "actual" events are built.
+
+ :param order_matters:
+ This defaults to ``True``. If ``False``, the order of expected
logging versus
+ actual logging will be ignored.
"""
+ order_matters = kw.pop('order_matters', True)
+ assert not kw, 'order_matters is the only keyword parameter'
actual = []
for event in self.events:
- actual_event = [field(event) if callable(field) else
event.get(field)
- for field in self.fields]
+ actual_event = tuple(field(event) if callable(field) else
event.get(field)
+ for field in self.fields)
if len(actual_event) == 1:
actual_event = actual_event[0]
actual.append(actual_event)
- compare(expected=expected, actual=actual)
+ if order_matters:
+ compare(expected=expected, actual=actual)
+ else:
+ expected = list(expected)
+ matched = []
+ unmatched = []
+ for entry in actual:
+ try:
+ index = expected.index(entry)
+ except ValueError:
+ unmatched.append(entry)
+ else:
+ matched.append(expected.pop(index))
+ if expected:
+ raise AssertionError((
+ 'entries not as expected:\n\n'
+ 'expected and found:\n%s\n\n'
+ 'expected but not found:\n%s\n\n'
+ 'other entries:\n%s'
+ ) % (pformat(matched), pformat(expected), pformat(unmatched)))
def check_failure_text(self, expected, index=-1, attribute='value'):
"""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/testfixtures-6.10.0/testfixtures/version.txt
new/testfixtures-6.14.0/testfixtures/version.txt
--- old/testfixtures-6.10.0/testfixtures/version.txt 2019-06-19
08:07:46.000000000 +0200
+++ new/testfixtures-6.14.0/testfixtures/version.txt 2020-02-24
16:21:42.000000000 +0100
@@ -1 +1 @@
-6.10.0
+6.14.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/testfixtures-6.10.0/testfixtures.egg-info/PKG-INFO
new/testfixtures-6.14.0/testfixtures.egg-info/PKG-INFO
--- old/testfixtures-6.10.0/testfixtures.egg-info/PKG-INFO 2019-06-19
08:07:50.000000000 +0200
+++ new/testfixtures-6.14.0/testfixtures.egg-info/PKG-INFO 2020-02-24
16:21:45.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: testfixtures
-Version: 6.10.0
+Version: 6.14.0
Summary: A collection of helpers and mock objects for unit tests and doc tests.
Home-page: https://github.com/Simplistix/testfixtures
Author: Chris Withers
@@ -75,6 +75,7 @@
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
Provides-Extra: test
Provides-Extra: docs
Provides-Extra: build
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/testfixtures-6.10.0/testfixtures.egg-info/requires.txt
new/testfixtures-6.14.0/testfixtures.egg-info/requires.txt
--- old/testfixtures-6.10.0/testfixtures.egg-info/requires.txt 2019-06-19
08:07:50.000000000 +0200
+++ new/testfixtures-6.14.0/testfixtures.egg-info/requires.txt 2020-02-24
16:21:45.000000000 +0100
@@ -6,13 +6,23 @@
[docs]
sphinx
+zope.component
+sybil
+twisted
+
+[docs:python_version < "3"]
+mock
+django<2
+
+[docs:python_version >= "3"]
+django
[test]
pytest>=3.6
pytest-cov
pytest-django
-sybil
zope.component
+sybil
twisted
[test:python_version < "3"]