A decorator is a function (or a callable object) that accepts a function as an argument and returns a new function. The result of decoration is a function that has been wrapped. Generally, the additional features of the wrapping surround the original functionality, either by transforming actual argument values or by transforming the result value. These are the two readily available join points in a function.
When we use a decorator, we want to be sure that the resulting decorated function has the original function's name and docstring. These details can be handled for us by a decorator to build our decorators. Using functools.wraps to write new decorators simplifies the work we need to do because the bookkeeping is handled for us.
Additionally, the type hints for decorators can be confusing because the parameter and return are both essentially...