Jun 5, 2019

python context managers using contextlib

"""
# Using contextlib you don't have to explicitly write __enter__, __exit__
# yield instead of return
"""
import contextlib
import sys
import time

@contextlib.contextmanager
def context_manager_def_test():
    print('context_manager_def_test: ENTER')
    try:
        yield 'You are in with-block'
        print('context_manager_def_test: NORMAL EXIT')
    except Exception:
        print('context_manager_def_test: EXCEPTION EXIT', sys.exc_info())
        raise

print('*'*75)

with context_manager_def_test() as cm:
    print('Inside ContextManagerTest')
    print(cm)

print('*'*75)
time.sleep(1)

with context_manager_def_test() as cm:
    print('Inside ContextManagerTest')
    print(cm)
    raise ValueError('something is wrong')

print('*'*75)


"""
***************************************************************************
context_manager_def_test: ENTER
Inside ContextManagerTest
You are in with-block
context_manager_def_test: NORMAL EXIT
***************************************************************************
context_manager_def_test: ENTER
Inside ContextManagerTest
You are in with-block
context_manager_def_test: EXCEPTION EXIT (<class 'ValueError'>, ValueError('something is wrong'), <traceback object at 0x1023ed0c8>)
Traceback (most recent call last):
  File "/Users/prabhathkota/Workspace/prabhath/personal/Python_Scripts/context_managers/contextlib_example.py", line 31, in <module>
    raise ValueError('something is wrong')
ValueError: something is wrong
***************************************************************************
"""

Python context manager with exceptions

###################
# __enter__
# __enter__ is called before executing with-statement body
# __exit__
# __exit__ called after with-statement body
# File opening is context managers
###################


class ContextManagerTest:
    def __init__(self):
        print('Inside __init__')

    def __enter__(self):
        print('Inside __enter__')
        return 'returning ... Inside with block'
        # return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is None:
            print('Inside __exit__ without exception')
        else:
            print('Inside __exit__ with exception ({} - {} - {})' .format(exc_type, exc_val, exc_tb))


with ContextManagerTest() as cm:
    print('Inside ContextManagerTest')
    print(cm)
    raise ValueError('something is wrong')


"""
Traceback (most recent call last):
Inside __enter__
  File "...../Python_Scripts/context_managers/context_manager_with_exception.py", line 30, in <module>
Inside ContextManagerTest
    raise ValueError('something is wrong')
returning ... Inside with block
ValueError: something is wrong
Inside __exit__ with exception (<class 'ValueError'> - something is wrong - <traceback object at 0x1034ba608>)

"""

Python context manager

###################
# __enter__
# __enter__ is called before executing with-statement body
# __exit__
# __exit__ called after with-statement body
# File opening is context managers
###################


class ContextManagerTest:
    def __init__(self):
        print('Inside __init__')

    def __enter__(self):
        print('Inside __enter__')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is None:
            print('Inside __exit__ without exception')
        else:
            print('Inside __exit__ with exception ({} - {} - {})'.format(exc_type, exc_val, exc_tb))
        return


with ContextManagerTest() as cm:
    print('Inside ContextManagerTest')
    print(cm)


"""
Inside __init__
Inside __enter__
Inside ContextManagerTest
<__main__.ContextManagerTest object at 0x10a920160>
Inside __exit__ without exception
"""

Jun 2, 2019

Python decorator to find out execution time taken by a function

import functools
import time

def timer(f): # without functools.wraps
    def timer_wrap(*args, **kwargs):
        """timer_wrap documentation """
        print('inside timer_wrap decorator')
        start_time = time.time()
        f(*args, **kwargs)
        end_time = time.time()
        print('Total Time Taken by function %s is : %4f secs' % (f.__name__, end_time - start_time))
        # f.__name__ gives function name
    return timer_wrap

def timer_wrap_with_functools(f): # with functools.wraps
    @functools.wraps(f)
    def timer_wrap(*args, **kwargs):
        """timer_wrap_with_functools documentation """
        print('inside timer_wrap_with_functools decorator')
        start_time = time.time()
        f(*args, **kwargs)
        end_time = time.time()
        print('Total Time Taken by function %s is : %4f secs' % (f.__name__, end_time - start_time))
    return timer_wrap


@timer
def test_timer_func(num_times):
    """test_timer_func documentation """
    total_sum = 0
    for _ in range(num_times):
        total_sum += sum([i ** 2 for i in range(1000)])
    print('Total Sum: %f ' % total_sum)

@timer_wrap_with_functools
def test_timer_func_functools(num_times):
    """test_timer_func_functools documentation """
    total_sum = 0
    for _ in range(num_times):
        total_sum += sum([i ** 2 for i in range(1000)])
    print('Total Sum: %f ' % total_sum)


if __name__ == '__main__':
    print('------------------------------------')
    test_timer_func(200)
    print(test_timer_func.__name__)  #gives wrapper name
    print(test_timer_func.__doc__)   #gives wrapper name
    print('------------------------------------')
    test_timer_func_functools(200)
    print(test_timer_func_functools.__name__) #gives function name
    print(test_timer_func_functools.__doc__)  #gives function name
    print('------------------------------------')


# Output:
------------------------------------
inside timer_wrap decorator
Total Sum: 66566700000.000000
Total Time Taken by function test_timer_func is : 0.205079 secs
timer_wrap
timer_wrap documentation
------------------------------------
inside timer_wrap_with_functools decorator
Total Sum: 66566700000.000000
Total Time Taken by function test_timer_func_functools is : 0.188637 secs
test_timer_func_functools
test_timer_func_functools documentation
------------------------------------




Python Decorator functools.wrap

######################################################
# Decorators
# use of functools.wraps
# The @functools.wraps decorator uses the function functools.update_wrapper() to update special attributes
# like __name__ and __doc__ that are used in the introspection.
######################################################
import functools


def decorator1(f):
    print('inside decorator1')

    def wrap(*args, **kwargs):
        f(*args, **kwargs)
    return wrap


@decorator1
def test_decorator1_func():
    """ test_decorator1_func documentation """
    print('inside test_decorator1_func')


def decorator2(f):
    print('inside decorator2')

    def wrap(*args, **kwargs):
        f(*args, **kwargs)
    wrap.__name__ = f.__name__
    wrap.__doc__ = f.__doc__
    return wrap


@decorator2
def test_decorator2_func():
    """ test_decorator2_func documentation """
    print('inside test_decorator2_func')


def decorator3(f):
    print('inside decorator3')

    @functools.wraps(f)
    def wrap(*args, **kwargs):
        f(*args, **kwargs)
    return wrap


@decorator3
def test_decorator3_func():
    """ test_decorator3_func documentation """
    print('inside test_decorator3_func')


if __name__ == '__main__':
    print('------------------------------------')
    # print(help(test_decorator1_func))
    print(test_decorator1_func.__name__)     # wrap
    print(test_decorator1_func.__doc__)      # None
    print(test_decorator1_func.__closure__)  # (<cell at 0x1064d0b28: function object at 0x1064ebe18>,)
    print('------------------------------------')
    # Using functools
    # print(help(test_decorator2_func))
    print(test_decorator2_func.__name__)     # test_decorator2_func
    print(test_decorator2_func.__doc__)      # test_decorator2_func
    print(test_decorator2_func.__closure__)  # (<cell at 0x1064d0b28: function object at 0x10654bc80>,)
    print('------------------------------------')
    # Using functools - this will achieve same as test_decorator2_func
    # print(help(test_decorator3_func))
    print(test_decorator3_func.__name__)     # test_decorator3_func
    print(test_decorator3_func.__doc__)      # test_decorator3_func documentation
    print(test_decorator3_func.__closure__)  # (<cell at 0x10344df18: function object at 0x1034e0d08>,)
    print('------------------------------------')