No project description provided

## Project description

# wrappingpaper

A collection of Python decorators and utilities to abstract away common/tedious Python patterns.

For example:

```
@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.overrideable_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.1.tar.gz
(10.0 kB
view hashes)