Python Decorators Assignment

For this assignment you will write two Python decorators.

See the example code we talked about in class and this primer for help:

https://realpython.com/blog/python/primer-on-python-decorators/

Given Code

The file decorators.py already contains a decorator and a function. If you run the code as it is, you sould see the following output:

The following positional arguments were passed:
  hi
The following optional arguments were passed:
  yell=True
HI
The function returned HI

The following positional arguments were passed:
  bye
The following optional arguments were passed:
  yell=True
BYE
The function returned BYE

The following positional arguments were passed:
  sup
The following optional arguments were passed:
  yell=True
SUP
The function returned SUP

The print_message() function is decorated by @print_function_call_details. The decorator’s wrapper function prints the arguments that were passed to the decorated function, then calls the function and stores the return value, then it prints out the return value, and then returns the return value to the caller. Run and read the existing code so that you understand what is going on.

Sleep Decorator

Write a new decorator named add_sleep which works as a rate limiter by sleeping for 1 second in the wrapper function between calling the original function and returning the function’s return value. You can do this by importing the sleep function from the time module and calling sleep(1). Add your decorator to print_message() so that it is now decorated with two decorators like this:

@add_sleep
@print_function_call_details
def print_message(message, yell=False):
    ...

Now when you run the program you should see the same output as before, but with a 1 second delay after the output is printed for each call to print_message().

Timing Decorator

Now write another decorator named print_timing. The wrapper function in this decorator must find out how long it took the wrapped function to execute, and print the timing information before returning the wrapped function’s return value. Get the current time using the time() function from the time module. This function returns the time as the number of seconds since January 1, 1970 (UNIX epoch time).

Wrap print_message() with this additional decorator, like this:

@print_timing
@add_sleep
@print_function_call_details
def print_message(message, yell=False):
    ...

Now when you run the program you should see the amount of time it took print_message() to execute each time. Note that the function is wrapped by the decorators in reverse order: @print_function_call_details is applied first, then @add_sleep, then @print_timing, so the timing will take the sleep time into account. Since @add_sleep causes a sleep of 1 second, each function call should take just over 1 second to execute, resulting in output like this:

The following positional arguments were passed:
  hi
The following optional arguments were passed:
  yell=True
HI
The function returned HI
The function executed in 1.0072300434112549 seconds

The following positional arguments were passed:
  sup
The following optional arguments were passed:
  yell=True
SUP
The function returned SUP
The function executed in 1.0048308372497559 seconds

The following positional arguments were passed:
  bye
The following optional arguments were passed:
  yell=True
BYE
The function returned BYE
The function executed in 1.0031001567840576 seconds

Submission

Git-keeper will run tests using your decorators to make sure that they behave correctly. Your @print_timing decorator must output the timing information in the exact same format as shown in the example output above to pass the tests.

As always, follow PEP 8.