Skip to content

Commit

Permalink
alvistack/21.6.0
Browse files Browse the repository at this point in the history
    git clean -xdf
    tar zcvf ../python-contextlib2_21.6.0.orig.tar.gz --exclude=.git .
    debuild -uc -us
    cp python-contextlib2.spec ../python-contextlib2_21.6.0-1.spec
    cp ../python*-contextlib2*21.6.0*.{gz,xz,spec,dsc} /osc/home\:alvistack/jazzband-contextlib2-21.6.0/
    rm -rf ../python*-contextlib2*21.6.0*.*

See jazzband#52

Signed-off-by: Wong Hoi Sing Edison <hswong3i@pantarei-design.com>
  • Loading branch information
hswong3i committed Feb 7, 2024
1 parent 9ef8594 commit 5b46d24
Show file tree
Hide file tree
Showing 27 changed files with 3,693 additions and 166 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
strategy:
max-parallel: 5
matrix:
python-version: [3.6, 3.7, 3.8, 3.9, '3.10', 'pypy-3.8']
python-version: [3.6, 3.7, 3.8, 3.9, '3.10', '3.11', 'pypy-3.8']

steps:
- uses: actions/checkout@v2
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ MANIFEST
.coverage
coverage.xml
htmlcov/

.pybuild/
154 changes: 98 additions & 56 deletions contextlib2/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"""contextlib2 - backports and enhancements to the contextlib module"""

import abc
import os
import sys
import warnings
import _collections_abc
from collections import deque
from functools import wraps
Expand All @@ -17,16 +17,17 @@
class GenericAlias:
pass


__all__ = ["asynccontextmanager", "contextmanager", "closing", "nullcontext",
"AbstractContextManager", "AbstractAsyncContextManager",
"AsyncExitStack", "ContextDecorator", "ExitStack",
"redirect_stdout", "redirect_stderr", "suppress", "aclosing"]
"redirect_stdout", "redirect_stderr", "suppress", "aclosing",
"chdir"]

# Backwards compatibility
__all__ += ["ContextStack"]

class AbstractContextManager(abc.ABC):

"""An abstract base class for context managers."""

__class_getitem__ = classmethod(GenericAlias)
Expand Down Expand Up @@ -143,18 +144,20 @@ def __init__(self, func, args, kwds):
# for the class instead.
# See http://bugs.python.org/issue19404 for more details.


class _GeneratorContextManager(_GeneratorContextManagerBase,
AbstractContextManager,
ContextDecorator):
"""Helper for @contextmanager decorator."""

def _recreate_cm(self):
# _GCM instances are one-shot context managers, so the
# _GCMB instances are one-shot context managers, so the
# CM must be recreated each time a decorated function is
# called
return self.__class__(self.func, self.args, self.kwds)


class _GeneratorContextManager(
_GeneratorContextManagerBase,
AbstractContextManager,
ContextDecorator,
):
"""Helper for @contextmanager decorator."""

def __enter__(self):
# do not keep args and kwds alive unnecessarily
# they are only needed for recreation, which is not possible anymore
Expand All @@ -164,8 +167,8 @@ def __enter__(self):
except StopIteration:
raise RuntimeError("generator didn't yield") from None

def __exit__(self, type, value, traceback):
if type is None:
def __exit__(self, typ, value, traceback):
if typ is None:
try:
next(self.gen)
except StopIteration:
Expand All @@ -176,9 +179,9 @@ def __exit__(self, type, value, traceback):
if value is None:
# Need to force instantiation so we can reliably
# tell if we get the same exception back
value = type()
value = typ()
try:
self.gen.throw(type, value, traceback)
self.gen.throw(typ, value, traceback)
except StopIteration as exc:
# Suppress StopIteration *unless* it's the same exception that
# was passed to throw(). This prevents a StopIteration
Expand All @@ -187,43 +190,45 @@ def __exit__(self, type, value, traceback):
except RuntimeError as exc:
# Don't re-raise the passed in exception. (issue27122)
if exc is value:
exc.__traceback__ = traceback
return False
# Likewise, avoid suppressing if a StopIteration exception
# Avoid suppressing if a StopIteration exception
# was passed to throw() and later wrapped into a RuntimeError
# (see PEP 479).
if type is StopIteration and exc.__cause__ is value:
# (see PEP 479 for sync generators; async generators also
# have this behavior). But do this only if the exception wrapped
# by the RuntimeError is actually Stop(Async)Iteration (see
# issue29692).
if (
isinstance(value, StopIteration)
and exc.__cause__ is value
):
exc.__traceback__ = traceback
return False
raise
except:
except BaseException as exc:
# only re-raise if it's *not* the exception that was
# passed to throw(), because __exit__() must not raise
# an exception unless __exit__() itself failed. But throw()
# has to raise the exception to signal propagation, so this
# fixes the impedance mismatch between the throw() protocol
# and the __exit__() protocol.
#
# This cannot use 'except BaseException as exc' (as in the
# async implementation) to maintain compatibility with
# Python 2, where old-style class exceptions are not caught
# by 'except BaseException'.
if sys.exc_info()[1] is value:
return False
raise
if exc is not value:
raise
exc.__traceback__ = traceback
return False
raise RuntimeError("generator didn't stop after throw()")


class _AsyncGeneratorContextManager(_GeneratorContextManagerBase,
AbstractAsyncContextManager,
AsyncContextDecorator):
"""Helper for @asynccontextmanager."""

def _recreate_cm(self):
# _AGCM instances are one-shot context managers, so the
# ACM must be recreated each time a decorated function is
# called
return self.__class__(self.func, self.args, self.kwds)
class _AsyncGeneratorContextManager(
_GeneratorContextManagerBase,
AbstractAsyncContextManager,
AsyncContextDecorator,
):
"""Helper for @asynccontextmanager decorator."""

async def __aenter__(self):
# do not keep args and kwds alive unnecessarily
# they are only needed for recreation, which is not possible anymore
del self.args, self.kwds, self.func
try:
return await self.gen.__anext__()
except StopAsyncIteration:
Expand All @@ -234,35 +239,48 @@ async def __aexit__(self, typ, value, traceback):
try:
await self.gen.__anext__()
except StopAsyncIteration:
return
return False
else:
raise RuntimeError("generator didn't stop")
else:
if value is None:
# Need to force instantiation so we can reliably
# tell if we get the same exception back
value = typ()
# See _GeneratorContextManager.__exit__ for comments on subtleties
# in this implementation
try:
await self.gen.athrow(typ, value, traceback)
raise RuntimeError("generator didn't stop after athrow()")
except StopAsyncIteration as exc:
# Suppress StopIteration *unless* it's the same exception that
# was passed to throw(). This prevents a StopIteration
# raised inside the "with" statement from being suppressed.
return exc is not value
except RuntimeError as exc:
# Don't re-raise the passed in exception. (issue27122)
if exc is value:
return False
# Avoid suppressing if a StopIteration exception
# was passed to throw() and later wrapped into a RuntimeError
# Avoid suppressing if a Stop(Async)Iteration exception
# was passed to athrow() and later wrapped into a RuntimeError
# (see PEP 479 for sync generators; async generators also
# have this behavior). But do this only if the exception wrapped
# by the RuntimeError is actually Stop(Async)Iteration (see
# issue29692).
if isinstance(value, (StopIteration, StopAsyncIteration)):
if exc.__cause__ is value:
return False
if (
isinstance(value, (StopIteration, StopAsyncIteration))
and exc.__cause__ is value
):
return False
raise
except BaseException as exc:
# only re-raise if it's *not* the exception that was
# passed to throw(), because __exit__() must not raise
# an exception unless __exit__() itself failed. But throw()
# has to raise the exception to signal propagation, so this
# fixes the impedance mismatch between the throw() protocol
# and the __exit__() protocol.
if exc is not value:
raise
return False
raise RuntimeError("generator didn't stop after athrow()")


def contextmanager(func):
Expand Down Expand Up @@ -505,9 +523,14 @@ def enter_context(self, cm):
"""
# We look up the special methods on the type to match the with
# statement.
_cm_type = type(cm)
_exit = _cm_type.__exit__
result = _cm_type.__enter__(cm)
cls = type(cm)
try:
_enter = cls.__enter__
_exit = cls.__exit__
except AttributeError:
raise TypeError(f"'{cls.__module__}.{cls.__qualname__}' object does "
f"not support the context manager protocol") from None
result = _enter(cm)
self._push_cm_exit(cm, _exit)
return result

Expand Down Expand Up @@ -565,10 +588,10 @@ def _fix_exception_context(new_exc, old_exc):
# Context may not be correct, so find the end of the chain
while 1:
exc_context = new_exc.__context__
if exc_context is old_exc:
if exc_context is None or exc_context is old_exc:
# Context is already set correctly (see issue 20317)
return
if exc_context is None or exc_context is frame_exc:
if exc_context is frame_exc:
break
new_exc = exc_context
# Change the end of the chain to point to the exception
Expand Down Expand Up @@ -641,9 +664,15 @@ async def enter_async_context(self, cm):
If successful, also pushes its __aexit__ method as a callback and
returns the result of the __aenter__ method.
"""
_cm_type = type(cm)
_exit = _cm_type.__aexit__
result = await _cm_type.__aenter__(cm)
cls = type(cm)
try:
_enter = cls.__aenter__
_exit = cls.__aexit__
except AttributeError:
raise TypeError(f"'{cls.__module__}.{cls.__qualname__}' object does "
f"not support the asynchronous context manager protocol"
) from None
result = await _enter(cm)
self._push_async_cm_exit(cm, _exit)
return result

Expand Down Expand Up @@ -708,10 +737,10 @@ def _fix_exception_context(new_exc, old_exc):
# Context may not be correct, so find the end of the chain
while 1:
exc_context = new_exc.__context__
if exc_context is old_exc:
if exc_context is None or exc_context is old_exc:
# Context is already set correctly (see issue 20317)
return
if exc_context is None or exc_context is frame_exc:
if exc_context is frame_exc:
break
new_exc = exc_context
# Change the end of the chain to point to the exception
Expand Down Expand Up @@ -779,6 +808,19 @@ async def __aexit__(self, *excinfo):
pass


class chdir(AbstractContextManager):
"""Non thread-safe context manager to change the current working directory."""

def __init__(self, path):
self.path = path
self._old_cwd = []

def __enter__(self):
self._old_cwd.append(os.getcwd())
os.chdir(self.path)

def __exit__(self, *excinfo):
os.chdir(self._old_cwd.pop())
# Preserve backwards compatibility
class ContextStack(ExitStack):
"""Backwards compatibility alias for ExitStack"""
Expand Down

0 comments on commit 5b46d24

Please sign in to comment.