147 lines
3.5 KiB
Python
147 lines
3.5 KiB
Python
from requests.exceptions import ConnectionError, RequestException
|
|
|
|
|
|
class Attrs:
|
|
|
|
@classmethod
|
|
def call(cls, f, **kwargs):
|
|
return f(cls(**kwargs))
|
|
|
|
def __init__(self, **kwargs):
|
|
self.attrs = kwargs
|
|
|
|
def __getattr__(self, attr):
|
|
return self.attrs.get(attr)
|
|
|
|
|
|
def get_short_error_message(e: Exception):
|
|
'''Generate a reasonable short message why the HTTP request failed'''
|
|
|
|
if isinstance(e, RequestException) and e.response is not None:
|
|
# e.g. "401 Unauthorized"
|
|
return '{} {}'.format(e.response.status_code, e.response.reason)
|
|
elif isinstance(e, ConnectionError):
|
|
# e.g. "ConnectionError" or "ConnectTimeout"
|
|
return e.__class__.__name__
|
|
else:
|
|
return str(e)
|
|
|
|
|
|
def identity(value, *_, **__):
|
|
"""
|
|
Trivial identity function: return the value passed in its first argument.
|
|
|
|
Examples:
|
|
|
|
>>> identity(42)
|
|
42
|
|
|
|
>>> list(
|
|
... filter(
|
|
... identity,
|
|
... [None, False, True, 0, 1, list(), set(), dict()],
|
|
... ),
|
|
... )
|
|
[True, 1]
|
|
"""
|
|
|
|
return value
|
|
|
|
|
|
def const(value, *_, **__):
|
|
"""
|
|
Given a value, returns a function that simply returns that value.
|
|
|
|
Example:
|
|
>>> f = const(42)
|
|
>>> f()
|
|
42
|
|
>>> f()
|
|
42
|
|
"""
|
|
|
|
return lambda *_, **__: value
|
|
|
|
|
|
def catching(computation, catcher=identity, exception=Exception):
|
|
"""
|
|
Catch exceptions.
|
|
|
|
Call the provided computation with no arguments. If it throws an exception
|
|
of the provided exception class (or any exception, if no class is provided),
|
|
return the result of calling the catcher function with the exception as the
|
|
sole argument. If no catcher function is specified, return the exception.
|
|
|
|
Examples:
|
|
|
|
Catch a KeyError and return the exception itself:
|
|
>>> catching(lambda: {'foo': 'bar'}['meh'])
|
|
KeyError('meh',)
|
|
|
|
Catch a KeyError and return a default value:
|
|
>>> catching(
|
|
... computation=lambda: {'foo': 'bar'}['meh'],
|
|
... catcher=const('nope'),
|
|
... )
|
|
'nope'
|
|
"""
|
|
|
|
try:
|
|
return computation()
|
|
except exception as e:
|
|
return catcher(e)
|
|
|
|
|
|
def defaulting(computation, default=None, exception=Exception):
|
|
"""
|
|
Like `catching`, but just return a default value if an exception is caught.
|
|
|
|
If no default value is supplied, default to None.
|
|
|
|
Examples:
|
|
|
|
Catch a KeyError and return a default value, like the `get` method:
|
|
>>> defaulting(lambda: {'foo': 'bar'}['meh'], 'nope')
|
|
'nope'
|
|
|
|
Turn a ZeroDivisionError into None:
|
|
>>> defaulting(lambda: 1/0) == None
|
|
True
|
|
"""
|
|
|
|
return catching(
|
|
computation=computation,
|
|
catcher=const(default),
|
|
exception=exception,
|
|
)
|
|
|
|
|
|
def these(what, where=None):
|
|
"""
|
|
Combinator for yielding multiple values with property access.
|
|
|
|
Yields from the values generated by an attribute of the given object, or
|
|
the values generated by the given object itself if no attribute key is
|
|
specified.
|
|
|
|
Examples:
|
|
|
|
No attribute key specified; yields from the given object:
|
|
>>> these(['foo', 'bar'])
|
|
['foo', 'bar']
|
|
|
|
An attribute key is specified; yields from the values generated by the
|
|
specified attribute's value:
|
|
object:
|
|
>>> these({'foo': ['bar', 'baz']}, 'foo')
|
|
['bar', 'baz']
|
|
|
|
An invalid attribute key is specified; yields nothing:
|
|
>>> these({'foo': ['bar', 'baz']}, 'meh')
|
|
[]
|
|
"""
|
|
|
|
if not where:
|
|
return what
|
|
return what[where] if where in what else []
|