|
| 1 | +""" |
| 2 | +Decorators receive a function to be executed when it is called. |
| 3 | +However, some other action can be done before or after its execution. |
| 4 | +The idea is to encapsulate some behaviour that should happen on top of |
| 5 | +some operation and can be reused everywhere it is needed. |
| 6 | +""" |
| 7 | +from functools import wraps |
| 8 | +from enum import Enum |
| 9 | +from datetime import datetime |
| 10 | + |
| 11 | +class Level(Enum): |
| 12 | + NONE = 0 |
| 13 | + INFO = 1 |
| 14 | + DEBUG = 2 |
| 15 | + |
| 16 | +LOG_FILE_NAME = "output.log" |
| 17 | + |
| 18 | +# If you’ve called @name without arguments, then the |
| 19 | +# decorated function will be passed in as _func. If you’ve |
| 20 | +# called it with arguments, then _func will be None, and |
| 21 | +# some of the keyword arguments may have been changed from |
| 22 | +# their default values. The asterisk in the argument list means |
| 23 | +# that you can’t call the remaining arguments as positional arguments. |
| 24 | +def logme(_func=None, *, level: Level = Level.NONE): |
| 25 | + def decorated_function(func): |
| 26 | + @wraps(func) # this ensures docstring, function name, arguments list, etc. are all copied |
| 27 | + # to the wrapped function - instead of being replaced with wrapper's info |
| 28 | + def wrapper(*args, **kwargs): |
| 29 | + if show_info(): |
| 30 | + with open(LOG_FILE_NAME, "a+") as log_file: |
| 31 | + log_file.writelines("[{}] Executing function: {}\n".format(get_current_timestamp(), func.__name__)) |
| 32 | + |
| 33 | + result = func(*args, **kwargs) |
| 34 | + |
| 35 | + if show_debug(): |
| 36 | + with open(LOG_FILE_NAME, "a+") as log_file: |
| 37 | + log_file.writelines("[{}] Result of function {} is: {}\n".format(get_current_timestamp(), func.__name__, result)) |
| 38 | + |
| 39 | + return result |
| 40 | + |
| 41 | + def get_current_timestamp(): |
| 42 | + return datetime.now().strftime('%Y%m%d-%H:%M:%S') |
| 43 | + |
| 44 | + def show_info(): |
| 45 | + if level == Level.NONE: |
| 46 | + return False |
| 47 | + |
| 48 | + # DEBUG also shows INFO |
| 49 | + return level._value_ <= Level.DEBUG._value_ |
| 50 | + |
| 51 | + def show_debug(): |
| 52 | + # INFO does not show DEBUG |
| 53 | + return level != Level.NONE and level == Level.DEBUG |
| 54 | + |
| 55 | + return wrapper |
| 56 | + |
| 57 | + # In this case, you called the decorator with arguments. |
| 58 | + # Return a decorator function that takes a function as an |
| 59 | + # argument and returns a wrapper function. |
| 60 | + if _func is None: |
| 61 | + return decorated_function |
| 62 | + # In this case, you called the decorator without arguments. |
| 63 | + # Apply the decorator to the function immediately. |
| 64 | + else: |
| 65 | + return decorated_function(_func) |
| 66 | + |
0 commit comments