Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions keyring/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,10 @@
import os
import typing
import warnings

from jaraco.context import ExceptionTrap
from jaraco.functools import once
from functools import lru_cache as once

from . import credentials, errors, util
from .compat import properties
from .compat import ExceptionTrap, properties
from .compat.py312 import metadata

log = logging.getLogger(__name__)
Expand Down
4 changes: 1 addition & 3 deletions keyring/backends/SecretService.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import logging
from contextlib import closing

from jaraco.context import ExceptionTrap

from .. import backend
from ..backend import KeyringBackend
from ..compat import properties
from ..compat import ExceptionTrap, properties
from ..credentials import SimpleCredential
from ..errors import (
InitError,
Expand Down
4 changes: 1 addition & 3 deletions keyring/backends/Windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@

import logging

from jaraco.context import ExceptionTrap

from ..backend import KeyringBackend
from ..compat import properties
from ..compat import ExceptionTrap, properties
from ..credentials import SimpleCredential
from ..errors import PasswordDeleteError

Expand Down
9 changes: 3 additions & 6 deletions keyring/compat/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
__all__ = ['properties']
from . import properties
from .context import ExceptionTrap, suppress


try:
from jaraco.classes import properties
except ImportError: # pragma: no cover
from . import properties # type: ignore[no-redef]
__all__ = ['ExceptionTrap', 'suppress', 'properties']
130 changes: 130 additions & 0 deletions keyring/compat/context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# from jaraco.context 6.1.0
from __future__ import annotations

import contextlib
import functools
import operator


class ExceptionTrap:
"""
A context manager that will catch certain exceptions and provide an
indication they occurred.

>>> with ExceptionTrap() as trap:
... raise Exception()
>>> bool(trap)
True

>>> with ExceptionTrap() as trap:
... pass
>>> bool(trap)
False

>>> with ExceptionTrap(ValueError) as trap:
... raise ValueError("1 + 1 is not 3")
>>> bool(trap)
True
>>> trap.value
ValueError('1 + 1 is not 3')
>>> trap.tb
<traceback object at ...>

>>> with ExceptionTrap(ValueError) as trap:
... raise Exception()
Traceback (most recent call last):
...
Exception

>>> bool(trap)
False
"""

exc_info = None, None, None

def __init__(self, exceptions=(Exception,)):
self.exceptions = exceptions

def __enter__(self):
return self

@property
def type(self):
return self.exc_info[0]

@property
def value(self):
return self.exc_info[1]

@property
def tb(self):
return self.exc_info[2]

def __exit__(self, *exc_info):
type = exc_info[0]
matches = type and issubclass(type, self.exceptions)
if matches:
self.exc_info = exc_info
return matches

def __bool__(self):
return bool(self.type)

def raises(self, func, *, _test=bool):
"""
Wrap func and replace the result with the truth
value of the trap (True if an exception occurred).

First, give the decorator an alias to support Python 3.8
Syntax.

>>> raises = ExceptionTrap(ValueError).raises

Now decorate a function that always fails.

>>> @raises
... def fail():
... raise ValueError('failed')
>>> fail()
True
"""

@functools.wraps(func)
def wrapper(*args, **kwargs):
with ExceptionTrap(self.exceptions) as trap:
func(*args, **kwargs)
return _test(trap)

return wrapper

def passes(self, func):
"""
Wrap func and replace the result with the truth
value of the trap (True if no exception).

First, give the decorator an alias to support Python 3.8
Syntax.

>>> passes = ExceptionTrap(ValueError).passes

Now decorate a function that always fails.

>>> @passes
... def fail():
... raise ValueError('failed')

>>> fail()
False
"""
return self.raises(func, _test=operator.not_)


class suppress(contextlib.suppress, contextlib.ContextDecorator):
"""
A version of contextlib.suppress with decorator support.

>>> @suppress(KeyError)
... def key_error():
... {}['']
>>> key_error()
"""
3 changes: 2 additions & 1 deletion keyring/devpi_client.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import functools

import pluggy
from jaraco.context import suppress

import keyring.errors

from .compat import suppress

hookimpl = pluggy.HookimplMarker("devpiclient")


Expand Down
2 changes: 1 addition & 1 deletion keyring/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class ExceptionRaisedContext:

def __init__(self, ExpectedException=Exception):
warnings.warn(
"ExceptionRaisedContext is deprecated; use `jaraco.context.ExceptionTrap`",
"ExceptionRaisedContext is deprecated; use `keyring.compat.ExceptionTrap`",
DeprecationWarning,
stacklevel=2,
)
Expand Down
15 changes: 8 additions & 7 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
requires = [
"setuptools>=77",
"setuptools_scm[toml]>=3.4.1",
# jaraco/skeleton#174
"coherent.licensed",
]
build-backend = "setuptools.build_meta"
Expand All @@ -26,20 +25,19 @@ classifiers = [
requires-python = ">=3.9"
license = "MIT"
dependencies = [
'pywin32-ctypes>=0.2.0; sys_platform=="win32"',
'SecretStorage>=3.2; sys_platform=="linux"',
'jeepney>=0.4.2; sys_platform=="linux"',
'importlib_metadata >= 4.11.4; python_version < "3.12"',
"jaraco.classes",
"jaraco.functools",
"jaraco.context",
]
dynamic = ["version"]

[project.urls]
Source = "https://github.com/jaraco/keyring"

[project.optional-dependencies]
backend = [
'pywin32-ctypes>=0.2.0; sys_platform=="win32"',
'SecretStorage>=3.2; sys_platform=="linux"',
]

test = [
# upstream
"pytest >= 6, != 8.1.*",
Expand Down Expand Up @@ -79,6 +77,9 @@ type = [
# upstream
"pytest-mypy >= 1.0.1",

## workaround for python/mypy#20454
"mypy < 1.19; python_implementation == 'PyPy'",

# local
"pygobject-stubs",
"shtab", # Optional install for completion
Expand Down
1 change: 1 addition & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ commands =
pytest {posargs}
usedevelop = True
extras =
backend
test
check
cover
Expand Down