Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-outcome for openSUSE:Factory checked in at 2021-10-20 20:23:21 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-outcome (Old) and /work/SRC/openSUSE:Factory/.python-outcome.new.1890 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-outcome" Wed Oct 20 20:23:21 2021 rev:5 rq:925639 version:1.1.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-outcome/python-outcome.changes 2020-12-12 20:31:13.637780858 +0100 +++ /work/SRC/openSUSE:Factory/.python-outcome.new.1890/python-outcome.changes 2021-10-20 20:24:04.057370050 +0200 @@ -1,0 +2,11 @@ +Wed Sep 1 20:18:41 UTC 2021 - Torsten Gruner <[email protected]> + +- update to version 1.1.0 + * Tweaked the implementation of Error.unwrap to avoid creating a + reference cycle between the exception object and the unwrap + method's frame. This shouldn't affect most users, but it + slightly reduces the amount of work that CPython's cycle + collector has to do, and may reduce GC pauses in some cases. (#29) + * Drop support for Python 2.7, 3.4, and 3.5. (#27) + +------------------------------------------------------------------- Old: ---- outcome-1.0.1.tar.gz New: ---- outcome-1.1.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-outcome.spec ++++++ --- /var/tmp/diff_new_pack.VKXiuQ/_old 2021-10-20 20:24:05.109370700 +0200 +++ /var/tmp/diff_new_pack.VKXiuQ/_new 2021-10-20 20:24:05.113370702 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-outcome # -# Copyright (c) 2020 SUSE LLC +# Copyright (c) 2021 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,8 +17,9 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} +%define skip_python2 1 Name: python-outcome -Version: 1.0.1 +Version: 1.1.0 Release: 0 Summary: Function for capturing the outcome of Python function calls License: MIT OR Apache-2.0 ++++++ outcome-1.0.1.tar.gz -> outcome-1.1.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/outcome-1.0.1/.appveyor.yml new/outcome-1.1.0/.appveyor.yml --- old/outcome-1.0.1/.appveyor.yml 2019-10-17 08:37:07.000000000 +0200 +++ new/outcome-1.1.0/.appveyor.yml 2020-11-17 04:06:26.000000000 +0100 @@ -4,16 +4,12 @@ environment: matrix: - - PYTHON: "C:\\Python27" - - PYTHON: "C:\\Python27-x64" - - PYTHON: "C:\\Python34" - - PYTHON: "C:\\Python34-x64" - - PYTHON: "C:\\Python35" - - PYTHON: "C:\\Python35-x64" - PYTHON: "C:\\Python36" - PYTHON: "C:\\Python36-x64" - PYTHON: "C:\\Python37" - PYTHON: "C:\\Python37-x64" + - PYTHON: "C:\\Python38" + - PYTHON: "C:\\Python38-x64" build_script: - "git --no-pager log -n2" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/outcome-1.0.1/.travis.yml new/outcome-1.1.0/.travis.yml --- old/outcome-1.0.1/.travis.yml 2019-10-17 08:37:07.000000000 +0200 +++ new/outcome-1.1.0/.travis.yml 2020-11-17 04:06:26.000000000 +0100 @@ -1,26 +1,16 @@ language: python -sudo: false -dist: trusty +dist: bionic matrix: include: - - python: 3.6 + - python: 3.8 env: CHECK_DOCS=1 - - python: 3.6 + - python: 3.8 env: CHECK_FORMATTING=1 - - python: pypy - - python: pypy3.5 - - python: 2.7 - - python: 3.4 - - python: 3.5.0 - - python: 3.5.2 + - python: pypy3.6-7.2.0 - python: 3.6 - python: 3.7 - dist: xenial - sudo: required - - python: 3.8-dev - dist: xenial - sudo: required + - python: 3.8 script: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/outcome-1.0.1/ci/travis.sh new/outcome-1.1.0/ci/travis.sh --- old/outcome-1.0.1/ci/travis.sh 2019-10-17 08:37:07.000000000 +0200 +++ new/outcome-1.1.0/ci/travis.sh 2020-11-17 04:06:26.000000000 +0100 @@ -45,7 +45,7 @@ pip install dist/*.zip if [ "$CHECK_FORMATTING" = "1" ]; then - pip install yapf==${YAPF_VERSION} isort + pip install yapf==${YAPF_VERSION} isort>=5 if ! yapf -rpd setup.py src tests; then cat <<EOF !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! @@ -67,7 +67,7 @@ # required for isort to order test imports correctly pip install -Ur test-requirements.txt - if ! isort --recursive --check-only --diff . ; then + if ! isort --check-only --diff . ; then cat <<EOF !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! @@ -75,7 +75,7 @@ Formatting problems were found (listed above). To fix them, run pip install isort - isort --recursive . + isort . in your local checkout. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/outcome-1.0.1/docs/source/conf.py new/outcome-1.1.0/docs/source/conf.py --- old/outcome-1.0.1/docs/source/conf.py 2019-10-17 08:37:07.000000000 +0200 +++ new/outcome-1.1.0/docs/source/conf.py 2020-11-17 04:06:26.000000000 +0100 @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # # Documentation build configuration file, created by # sphinx-quickstart on Sat Jan 21 19:11:14 2017. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/outcome-1.0.1/docs/source/history.rst new/outcome-1.1.0/docs/source/history.rst --- old/outcome-1.0.1/docs/source/history.rst 2019-10-17 08:37:07.000000000 +0200 +++ new/outcome-1.1.0/docs/source/history.rst 2020-11-17 04:06:26.000000000 +0100 @@ -5,6 +5,25 @@ .. towncrier release notes start +Outcome 1.1.0 (2020-11-16) +-------------------------- + +Bugfixes +~~~~~~~~ + +- Tweaked the implementation of ``Error.unwrap`` to avoid creating a + reference cycle between the exception object and the ``unwrap`` + method's frame. This shouldn't affect most users, but it slightly + reduces the amount of work that CPython's cycle collector has to do, + and may reduce GC pauses in some cases. (`#29 <https://github.com/python-trio/outcome/issues/29>`__) + + +Deprecations and Removals +~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Drop support for Python 2.7, 3.4, and 3.5. (`#27 <https://github.com/python-trio/outcome/issues/27>`__) + + Outcome 1.0.1 (2019-10-16) -------------------------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/outcome-1.0.1/docs/source/tutorial.rst new/outcome-1.1.0/docs/source/tutorial.rst --- old/outcome-1.0.1/docs/source/tutorial.rst 2019-10-17 08:37:07.000000000 +0200 +++ new/outcome-1.1.0/docs/source/tutorial.rst 2020-11-17 04:06:26.000000000 +0100 @@ -16,7 +16,7 @@ even if ``f`` raises an error. -On Python 3.5+, there's also :func:`acapture`:: +There's also :func:`acapture`:: result = await outcome.acapture(f, *args, **kwargs) x = result.unwrap() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/outcome-1.0.1/setup.cfg new/outcome-1.1.0/setup.cfg --- old/outcome-1.0.1/setup.cfg 2019-10-17 08:37:07.000000000 +0200 +++ new/outcome-1.1.0/setup.cfg 2020-11-17 04:06:26.000000000 +0100 @@ -4,7 +4,6 @@ [isort] multi_line_output = 4 skip = ./build, ./docs -not_skip = __init__.py # ci/travis.sh installs outcome normally, so isort assumes it's third party known_first_party = outcome diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/outcome-1.0.1/setup.py new/outcome-1.1.0/setup.py --- old/outcome-1.0.1/setup.py 2019-10-17 08:37:07.000000000 +0200 +++ new/outcome-1.1.0/setup.py 2020-11-17 04:06:26.000000000 +0100 @@ -1,20 +1,13 @@ -# coding: utf-8 -from __future__ import absolute_import, division, print_function - -from io import open - from setuptools import find_packages, setup version = dict() -# read _version.py as bytes, otherwise exec will complain about -# 'coding: utf-8', which we want there for the normal Python 2 import -with open('src/outcome/_version.py', 'rb') as fp: +with open('src/outcome/_version.py') as fp: version_mod = fp.read() exec(version_mod, version) -LONG_DESC = open('README.rst', encoding='utf-8').read() +LONG_DESC = open('README.rst').read() setup( name='outcome', @@ -33,7 +26,7 @@ packages=find_packages('src'), package_dir={'': 'src'}, install_requires=['attrs>=19.2.0'], - python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*', + python_requires='>=3.6', keywords='result', classifiers=[ 'Development Status :: 5 - Production/Stable', @@ -44,10 +37,9 @@ 'Operating System :: POSIX :: Linux', 'Operating System :: MacOS :: MacOS X', 'Operating System :: Microsoft :: Windows', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', ], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/outcome-1.0.1/src/outcome/__init__.py new/outcome-1.1.0/src/outcome/__init__.py --- old/outcome-1.0.1/src/outcome/__init__.py 2019-10-17 08:37:07.000000000 +0200 +++ new/outcome-1.1.0/src/outcome/__init__.py 2020-11-17 04:06:26.000000000 +0100 @@ -1,20 +1,12 @@ -# coding: utf-8 """Top-level package for outcome.""" -from __future__ import absolute_import, division, print_function - -import sys +from ._impl import Error, Outcome, Value, acapture, capture from ._util import AlreadyUsedError, fixup_module_metadata from ._version import __version__ -if sys.version_info >= (3, 5): - from ._async import Error, Outcome, Value, acapture, capture - __all__ = ( - 'Error', 'Outcome', 'Value', 'acapture', 'capture', 'AlreadyUsedError' - ) -else: - from ._sync import Error, Outcome, Value, capture - __all__ = ('Error', 'Outcome', 'Value', 'capture', 'AlreadyUsedError') +__all__ = ( + 'Error', 'Outcome', 'Value', 'acapture', 'capture', 'AlreadyUsedError' +) fixup_module_metadata(__name__, globals()) del fixup_module_metadata diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/outcome-1.0.1/src/outcome/_async.py new/outcome-1.1.0/src/outcome/_async.py --- old/outcome-1.0.1/src/outcome/_async.py 2019-10-17 08:37:07.000000000 +0200 +++ new/outcome-1.1.0/src/outcome/_async.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,68 +0,0 @@ -import abc - -from ._sync import Error as ErrorBase -from ._sync import Outcome as OutcomeBase -from ._sync import Value as ValueBase -from ._util import remove_tb_frames - -__all__ = ['Error', 'Outcome', 'Value', 'acapture', 'capture'] - - -def capture(sync_fn, *args, **kwargs): - """Run ``sync_fn(*args, **kwargs)`` and capture the result. - - Returns: - Either a :class:`Value` or :class:`Error` as appropriate. - - """ - # _sync.capture references ErrorBase and ValueBase - try: - return Value(sync_fn(*args, **kwargs)) - except BaseException as exc: - exc = remove_tb_frames(exc, 1) - return Error(exc) - - -async def acapture(async_fn, *args, **kwargs): - """Run ``await async_fn(*args, **kwargs)`` and capture the result. - - Returns: - Either a :class:`Value` or :class:`Error` as appropriate. - - """ - try: - return Value(await async_fn(*args, **kwargs)) - except BaseException as exc: - exc = remove_tb_frames(exc, 1) - return Error(exc) - - -class Outcome(OutcomeBase): - @abc.abstractmethod - async def asend(self, agen): - """Send or throw the contained value or exception into the given async - generator object. - - Args: - agen: An async generator object supporting ``.asend()`` and - ``.athrow()`` methods. - - """ - - -class Value(Outcome, ValueBase): - async def asend(self, agen): - self._set_unwrapped() - return await agen.asend(self.value) - - -class Error(Outcome, ErrorBase): - async def asend(self, agen): - self._set_unwrapped() - return await agen.athrow(self.error) - - -# We don't need this for Sphinx, but do it anyway for IPython, IDEs, etc -Outcome.__doc__ = OutcomeBase.__doc__ -Value.__doc__ = ValueBase.__doc__ -Error.__doc__ = ErrorBase.__doc__ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/outcome-1.0.1/src/outcome/_impl.py new/outcome-1.1.0/src/outcome/_impl.py --- old/outcome-1.0.1/src/outcome/_impl.py 1970-01-01 01:00:00.000000000 +0100 +++ new/outcome-1.1.0/src/outcome/_impl.py 2020-11-17 04:06:26.000000000 +0100 @@ -0,0 +1,160 @@ +import abc + +import attr + +from ._util import AlreadyUsedError, remove_tb_frames + +__all__ = ['Error', 'Outcome', 'Value', 'acapture', 'capture'] + + +def capture(sync_fn, *args, **kwargs): + """Run ``sync_fn(*args, **kwargs)`` and capture the result. + + Returns: + Either a :class:`Value` or :class:`Error` as appropriate. + + """ + try: + return Value(sync_fn(*args, **kwargs)) + except BaseException as exc: + exc = remove_tb_frames(exc, 1) + return Error(exc) + + +async def acapture(async_fn, *args, **kwargs): + """Run ``await async_fn(*args, **kwargs)`` and capture the result. + + Returns: + Either a :class:`Value` or :class:`Error` as appropriate. + + """ + try: + return Value(await async_fn(*args, **kwargs)) + except BaseException as exc: + exc = remove_tb_frames(exc, 1) + return Error(exc) + + [email protected](repr=False, init=False, slots=True) +class Outcome(abc.ABC): + """An abstract class representing the result of a Python computation. + + This class has two concrete subclasses: :class:`Value` representing a + value, and :class:`Error` representing an exception. + + In addition to the methods described below, comparison operators on + :class:`Value` and :class:`Error` objects (``==``, ``<``, etc.) check that + the other object is also a :class:`Value` or :class:`Error` object + respectively, and then compare the contained objects. + + :class:`Outcome` objects are hashable if the contained objects are + hashable. + + """ + _unwrapped = attr.ib(default=False, eq=False, init=False) + + def _set_unwrapped(self): + if self._unwrapped: + raise AlreadyUsedError + object.__setattr__(self, '_unwrapped', True) + + @abc.abstractmethod + def unwrap(self): + """Return or raise the contained value or exception. + + These two lines of code are equivalent:: + + x = fn(*args) + x = outcome.capture(fn, *args).unwrap() + + """ + + @abc.abstractmethod + def send(self, gen): + """Send or throw the contained value or exception into the given + generator object. + + Args: + gen: A generator object supporting ``.send()`` and ``.throw()`` + methods. + + """ + + @abc.abstractmethod + async def asend(self, agen): + """Send or throw the contained value or exception into the given async + generator object. + + Args: + agen: An async generator object supporting ``.asend()`` and + ``.athrow()`` methods. + + """ + + [email protected](frozen=True, repr=False, slots=True) +class Value(Outcome): + """Concrete :class:`Outcome` subclass representing a regular value. + + """ + + value = attr.ib() + """The contained value.""" + + def __repr__(self): + return f'Value({self.value!r})' + + def unwrap(self): + self._set_unwrapped() + return self.value + + def send(self, gen): + self._set_unwrapped() + return gen.send(self.value) + + async def asend(self, agen): + self._set_unwrapped() + return await agen.asend(self.value) + + [email protected](frozen=True, repr=False, slots=True) +class Error(Outcome): + """Concrete :class:`Outcome` subclass representing a raised exception. + + """ + + error = attr.ib(validator=attr.validators.instance_of(BaseException)) + """The contained exception object.""" + + def __repr__(self): + return f'Error({self.error!r})' + + def unwrap(self): + self._set_unwrapped() + # Tracebacks show the 'raise' line below out of context, so let's give + # this variable a name that makes sense out of context. + captured_error = self.error + try: + raise captured_error + finally: + # We want to avoid creating a reference cycle here. Python does + # collect cycles just fine, so it wouldn't be the end of the world + # if we did create a cycle, but the cyclic garbage collector adds + # latency to Python programs, and the more cycles you create, the + # more often it runs, so it's nicer to avoid creating them in the + # first place. For more details see: + # + # https://github.com/python-trio/trio/issues/1770 + # + # In particuar, by deleting this local variables from the 'unwrap' + # methods frame, we avoid the 'captured_error' object's + # __traceback__ from indirectly referencing 'captured_error'. + del captured_error, self + + def send(self, it): + self._set_unwrapped() + return it.throw(self.error) + + async def asend(self, agen): + self._set_unwrapped() + return await agen.athrow(self.error) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/outcome-1.0.1/src/outcome/_sync.py new/outcome-1.1.0/src/outcome/_sync.py --- old/outcome-1.0.1/src/outcome/_sync.py 2019-10-17 08:37:07.000000000 +0200 +++ new/outcome-1.1.0/src/outcome/_sync.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,115 +0,0 @@ -# coding: utf-8 -from __future__ import absolute_import, division, print_function - -import abc - -import attr - -from ._util import ABC, AlreadyUsedError, remove_tb_frames - -__all__ = ['Error', 'Outcome', 'Value', 'capture'] - - -def capture(sync_fn, *args, **kwargs): - """Run ``sync_fn(*args, **kwargs)`` and capture the result. - - Returns: - Either a :class:`Value` or :class:`Error` as appropriate. - - """ - try: - return Value(sync_fn(*args, **kwargs)) - except BaseException as exc: - exc = remove_tb_frames(exc, 1) - return Error(exc) - - [email protected](repr=False, init=False, slots=True) -class Outcome(ABC): - """An abstract class representing the result of a Python computation. - - This class has two concrete subclasses: :class:`Value` representing a - value, and :class:`Error` representing an exception. - - In addition to the methods described below, comparison operators on - :class:`Value` and :class:`Error` objects (``==``, ``<``, etc.) check that - the other object is also a :class:`Value` or :class:`Error` object - respectively, and then compare the contained objects. - - :class:`Outcome` objects are hashable if the contained objects are - hashable. - - """ - _unwrapped = attr.ib(default=False, eq=False, init=False) - - def _set_unwrapped(self): - if self._unwrapped: - raise AlreadyUsedError - object.__setattr__(self, '_unwrapped', True) - - @abc.abstractmethod - def unwrap(self): - """Return or raise the contained value or exception. - - These two lines of code are equivalent:: - - x = fn(*args) - x = outcome.capture(fn, *args).unwrap() - - """ - - @abc.abstractmethod - def send(self, gen): - """Send or throw the contained value or exception into the given - generator object. - - Args: - gen: A generator object supporting ``.send()`` and ``.throw()`` - methods. - - """ - - [email protected](frozen=True, repr=False, slots=True) -class Value(Outcome): - """Concrete :class:`Outcome` subclass representing a regular value. - - """ - - value = attr.ib() - """The contained value.""" - - def __repr__(self): - return 'Value({!r})'.format(self.value) - - def unwrap(self): - self._set_unwrapped() - return self.value - - def send(self, gen): - self._set_unwrapped() - return gen.send(self.value) - - [email protected](frozen=True, repr=False, slots=True) -class Error(Outcome): - """Concrete :class:`Outcome` subclass representing a raised exception. - - """ - - error = attr.ib(validator=attr.validators.instance_of(BaseException)) - """The contained exception object.""" - - def __repr__(self): - return 'Error({!r})'.format(self.error) - - def unwrap(self): - self._set_unwrapped() - # Tracebacks show the 'raise' line below out of context, so let's give - # this variable a name that makes sense out of context. - captured_error = self.error - raise captured_error - - def send(self, it): - self._set_unwrapped() - return it.throw(self.error) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/outcome-1.0.1/src/outcome/_util.py new/outcome-1.1.0/src/outcome/_util.py --- old/outcome-1.0.1/src/outcome/_util.py 2019-10-17 08:37:07.000000000 +0200 +++ new/outcome-1.1.0/src/outcome/_util.py 2020-11-17 04:06:26.000000000 +0100 @@ -1,10 +1,3 @@ -# coding: utf-8 -from __future__ import absolute_import, division, print_function - -import abc -import sys - - class AlreadyUsedError(RuntimeError): """An Outcome can only be unwrapped once.""" pass @@ -25,17 +18,7 @@ def remove_tb_frames(exc, n): - if sys.version_info < (3,): - return exc tb = exc.__traceback__ for _ in range(n): tb = tb.tb_next return exc.with_traceback(tb) - - -if sys.version_info < (3,): - - class ABC(object): - __metaclass__ = abc.ABCMeta -else: - ABC = abc.ABC diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/outcome-1.0.1/src/outcome/_version.py new/outcome-1.1.0/src/outcome/_version.py --- old/outcome-1.0.1/src/outcome/_version.py 2019-10-17 08:37:07.000000000 +0200 +++ new/outcome-1.1.0/src/outcome/_version.py 2020-11-17 04:06:26.000000000 +0100 @@ -1,4 +1,3 @@ -# coding: utf-8 # This file is imported from __init__.py and exec'd from setup.py -__version__ = "1.0.1" +__version__ = "1.1.0" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/outcome-1.0.1/test-requirements.txt new/outcome-1.1.0/test-requirements.txt --- old/outcome-1.0.1/test-requirements.txt 2019-10-17 08:37:07.000000000 +0200 +++ new/outcome-1.1.0/test-requirements.txt 2020-11-17 04:06:26.000000000 +0100 @@ -1,4 +1,3 @@ pytest pytest-cov -pytest-asyncio; python_version >= '3.5' -async-generator; python_version >= '3.5' +pytest-asyncio diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/outcome-1.0.1/tests/__init__.py new/outcome-1.1.0/tests/__init__.py --- old/outcome-1.0.1/tests/__init__.py 2019-10-17 08:37:07.000000000 +0200 +++ new/outcome-1.1.0/tests/__init__.py 2020-11-17 04:06:26.000000000 +0100 @@ -1,2 +0,0 @@ -# coding: utf-8 -from __future__ import absolute_import, division, print_function diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/outcome-1.0.1/tests/conftest.py new/outcome-1.1.0/tests/conftest.py --- old/outcome-1.0.1/tests/conftest.py 2019-10-17 08:37:07.000000000 +0200 +++ new/outcome-1.1.0/tests/conftest.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,18 +0,0 @@ -# coding: utf-8 -from __future__ import absolute_import, division, print_function - -import sys - -import pytest - - -class DummyCollector(pytest.collect.File): - def collect(self): - return [] - - -def pytest_pycollect_makemodule(path, parent): - # skip asyncio tests unless on Python 3.5+, because async/await - # is a SyntaxError. - if 'async' in path.basename and sys.version_info < (3, 5): - return DummyCollector(path, parent=parent) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/outcome-1.0.1/tests/test_async.py new/outcome-1.1.0/tests/test_async.py --- old/outcome-1.0.1/tests/test_async.py 2019-10-17 08:37:07.000000000 +0200 +++ new/outcome-1.1.0/tests/test_async.py 2020-11-17 04:06:26.000000000 +0100 @@ -1,9 +1,7 @@ import asyncio -import sys import traceback import pytest -from async_generator import async_generator, yield_ import outcome from outcome import AlreadyUsedError, Error, Value @@ -29,16 +27,13 @@ async def test_asend(): - @async_generator async def my_agen_func(): - assert (await yield_(1)) == "value" + assert (yield 1) == "value" with pytest.raises(KeyError): - await yield_(2) - await yield_(3) + yield 2 + yield 3 my_agen = my_agen_func().__aiter__() - if sys.version_info < (3, 5, 2): - my_agen = await my_agen v = Value("value") e = Error(KeyError()) assert (await my_agen.asend(None)) == 1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/outcome-1.0.1/tests/test_sync.py new/outcome-1.1.0/tests/test_sync.py --- old/outcome-1.0.1/tests/test_sync.py 2019-10-17 08:37:07.000000000 +0200 +++ new/outcome-1.1.0/tests/test_sync.py 2020-11-17 04:06:26.000000000 +0100 @@ -1,6 +1,3 @@ -# coding: utf-8 -from __future__ import absolute_import, division, print_function - import sys import traceback @@ -28,7 +25,7 @@ e.unwrap() with pytest.raises(AlreadyUsedError): e.unwrap() - assert repr(e) == "Error({!r})".format(exc) + assert repr(e) == f"Error({exc!r})" e = Error(exc) with pytest.raises(TypeError): @@ -82,7 +79,6 @@ assert {e1, e2, e3, e4} == {e1, e3} [email protected](sys.version_info < (3,), reason="requires python 3") def test_Value_compare(): assert Value(1) < Value(2) assert not Value(3) < Value(2) @@ -112,7 +108,6 @@ assert issubclass(Error, outcome.Outcome) [email protected](sys.version_info < (3,), reason="requires python 3") def test_traceback_frame_removal(): def raise_ValueError(x): raise ValueError(x) @@ -123,3 +118,22 @@ frames = traceback.extract_tb(exc_info.value.__traceback__) functions = [function for _, _, function, _ in frames] assert functions[-2:] == ['unwrap', 'raise_ValueError'] + + +def test_Error_unwrap_does_not_create_reference_cycles(): + # See comment in Error.unwrap for why reference cycles are tricky + exc = ValueError() + err = Error(exc) + try: + err.unwrap() + except ValueError: + pass + # Top frame in the traceback is the current test function; we don't care + # about its references + assert exc.__traceback__.tb_frame is sys._getframe() + # The next frame down is the 'unwrap' frame; we want to make sure it + # doesn't reference the exception (or anything else for that matter, just + # to be thorough) + unwrap_frame = exc.__traceback__.tb_next.tb_frame + assert unwrap_frame.f_code.co_name == "unwrap" + assert unwrap_frame.f_locals == {}
