No project description provided
Project description
wrappingpaper
A collection of Python decorators and utilities to abstract away common/tedious Python patterns.
For example:
import wrappingpaper as wp
@wp.contextdecorator
def doing_something(a, b):
print(a)
yield
print(b)
# por que no los dos?
# you can do this
with doing_something(4, 5):
print(1)
# as well as this
@doing_something(4, 5)
def something():
print(1)
something()
Includes
- logging / error handling
- catch errors thrown in a function and redirect to logger
- context managers
- context managers that double as function wrappers
- iterable utils
- object property patterns
- function default manipulation
- misc
Install
pip install wrappingpaper
Usage
import wrappingpaper as wp
Logging
import logging
log = logging.getLogger(__name__)
# handle and log error
@wp.log_error_as_warning(log, default=dict)
def get_stats(x=None):
if x is True:
raise ValueError() # some error happens
return {'a': 5, 'b': 6}
assert get_stats() == {'a': 5, 'b': 6}
assert get_stats(True) == {}
Roughly equivalent to:
def get_stats(x=None):
try:
if x is True:
raise ValueError() # some error happens
return {'a': 5, 'b': 6}
except ValueError as e:
log.warning('Exception in get_stats: %s', e)
return {}
Context Managers
@wp.contextdecorator
def doing_something(a, b):
print(a)
yield
print(b)
# por que no los dos?
# you can do this
with doing_something(4, 5):
print(1)
# as well as this
@doing_something(4, 5)
def something():
print(1)
something()
Roughly equivalent to:
import functools
from contextlib import contextmanager
@contextmanager
def doing_something(a, b):
print(a)
yield
print(b)
def doing_something2(a, b):
def outer(func):
@functools.wraps(func)
def inner(*a, **kw):
with doing_something(a, b):
return func(*a, **kw)
return inner
return outer
# used like:
with doing_something(4, 5):
print(1)
@doing_something2(4, 5)
def something():
print(1)
something()
Properties
import time
class SomeClass:
@wp.cachedproperty
def instance_prop(self):
'''This is run once per object instance.'''
return time.time()
@wp.onceproperty
def class_prop(self):
'''This is run once. It is cached in the property
object itself.'''
return time.time()
@wp.overridable_property
def overridable(self):
return time.time()
def __init__(self, overridable=None):
if overridable: # override the property value
# stores at self._overridable
self.overridable = overridable
# otherwise it just uses the property function like usual
a = SomeClass()
b = SomeClass()
assert a.instance_prop != b.instance_prop # prop runs once per object
assert a.class_prop == b.class_prop # prop runs only once
assert a.overridable != a.overridable # gets called twice, shouldn't be the same
a.overridable = 5
assert a.overridable == 5 # now the value is overridden
assert SomeClass(5).overridable == 5 # overriding inside class
Function Signature
# dynamic function defaults
@wp.configfunction
def asdf(a=5, b=6, c=7):
return a + b + c
assert asdf() == 5+6+7 # normal behavior
asdf.update(a=1)
assert asdf() == 1+6+7 # updated default
assert asdf(3) == 3+6+7 # automatically resolves kwargs and posargs
asdf.clear()
assert asdf() == 5+6+7 # back to normal behavior
# filter out kwargs not in the signature (if **kw, it's a no-op).
@wp.filterkw
def asdf(a=5, b=6, c=7):
return a + b + c
assert asdf(d=1234) == 5+6+7
Iterables
# make sure that a for loop doesn't go too fast.
# limit the time one iteration takes.
# limiting the number of iterations to 10.
# by default it loops infinitely.
for dt, time_asleep in wp.limit(wp.throttled(1), 10):
print('Iteration took {}s. Had to sleep for {}s.'.format(dt, time_asleep))
print('-'*10)
# check the first n items in an iterable, without removing them.
it = iter(range(6))
items, it = wp.pre_check_iter(it, 3)
assert items == [0, 1, 2]
assert list(it) == [0, 1, 2, 3, 4, 5, 6]
# repeat and chain iterables infinitely
import random
def get_numbers():
return [random.random() for _ in range(10)]
numbers = wp.run_iter_forever(get_numbers)
# repeat get_numbers() and chain iterable outputs together
all_numbers = list(wp.limit(numbers, 100))
assert all(isinstance(x, float) for x in all_numbers)
def get_numbers():
if random.random() > 0.8: # make random breaks
return # returns empty
return [random.random() for _ in range(10)]
numbers = wp.run_iter_forever(get_numbers, none_if_empty=True)
# this SHOULD contain sporadic None's at a multiple of 10
all_numbers = list(wp.limit(numbers, 5000))
assert None in all_numbers
Misc
import random
# retry a function if an exception is raised
@wp.retry_on_failure(10)
def asdf():
x = random.random()
if x < 0.5:
raise ValueError
return x
# will either return a number that is definitely > 0.5
# or every number in the first 10 tries were below 0.5
try:
assert asdf() > 0.5
except ValueError:
print("Couldn't get a number :/")
# ignore error
with wp.ignore():
a, b = 5, 0
c = a / b # throws divide by zero
a = 10 # never run
assert a == 5
# A wrappable alternative to `while True:`
for _ in wp.infinite():
print('this is gonna be a while...')
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
wrappingpaper-0.0.2.tar.gz
(10.0 kB
view details)
File details
Details for the file wrappingpaper-0.0.2.tar.gz
.
File metadata
- Download URL: wrappingpaper-0.0.2.tar.gz
- Upload date:
- Size: 10.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.1.1 pkginfo/1.5.0.1 requests/2.23.0 setuptools/46.1.3.post20200330 requests-toolbelt/0.9.1 tqdm/4.45.0 CPython/3.7.7
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | f2d8df01f379653e0b900a2ae5b53d0448ea72d2e9a72626d7ee1e00ad445616 |
|
MD5 | 1b6871cce7d01fa7b2b994b30fe28a06 |
|
BLAKE2b-256 | 9aa0f455e951c08d17b1cf8815958316772e4a09c707f7017b8e8e6a3790094d |