Metadata-Version: 2.1
Name: wrappingpaper
Version: 0.0.2
Summary: UNKNOWN
Home-page: https://github.com/beasteers/wrappingpaper
Author: Bea Steers
Author-email: bea.steers@gmail.com
License: MIT License
Description: # wrappingpaper
        
        A collection of Python decorators and utilities to abstract away common/tedious Python patterns.
        
        For example:
        ```python
        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](#logging)
            - catch errors thrown in a function and redirect to logger
         - [context managers](#context-managers)
           - context managers that double as function wrappers
         - [iterable utils](#iterables)
         - [object property patterns](#properties)
         - [function default manipulation](#function-signature)
         - [misc](#misc)
        
        
        ## Install
        
        ```bash
        pip install wrappingpaper
        ```
        
        ## Usage
        ```python
        import wrappingpaper as wp
        ```
        
        ### Logging
        
        ```python
        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:
        
        ```python
        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
        
        ```python
        
        @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:
        
        ```python
        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
        
        ```python
        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
        
        ```python
        # 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
        
        ```python
        
        # 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
        
        ```python
        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...')
        
        ```
Platform: UNKNOWN
Description-Content-Type: text/markdown
