Skip to content

Instantly share code, notes, and snippets.

@FMeinicke
Created July 4, 2024 09:32
Show Gist options
  • Select an option

  • Save FMeinicke/f1ed71fc1e93436849eb52b9d03d9df0 to your computer and use it in GitHub Desktop.

Select an option

Save FMeinicke/f1ed71fc1e93436849eb52b9d03d9df0 to your computer and use it in GitHub Desktop.
Compilation of different methods to create function decorators with and without parameters
from __future__ import annotations
from functools import wraps
from typing import overload
def func_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"func_decorator: Calling {func.__name__} with args: {args}, kwargs: {kwargs}")
return func(*args, **kwargs)
return wrapper
def func_decorator_with_args(*dargs, **dkwargs):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(
f"func_decorator_with_args: Calling {func.__name__} with args: {args}, kwargs: {kwargs}, "
f"decorator dargs: {dargs}, decorator dkwargs: {dkwargs}"
)
return func(*args, **kwargs)
return wrapper
print(f"decorator dargs: {dargs}, decorator dkwargs: {dkwargs}")
return decorator
def test1():
@func_decorator
def func(*args, **kwargs):
print(f"func args: {args}, kwargs: {kwargs}")
@func_decorator_with_args("darg1", "darg2", dkwarg1="dkwarg1", dkwarg2="dkwarg2")
def func_with_args(*args, **kwargs):
print(f"func_with_args args: {args}, kwargs: {kwargs}")
print("---------------")
func(1, 2, 3, kwarg1="kwarg1", kwarg2="kwarg2")
func_with_args(1, 2, 3, kwarg1="kwarg1", kwarg2="kwarg2")
@overload
def func_decorator_with_and_without_args(func): ...
@overload
def func_decorator_with_and_without_args(*, dkwarg="default_kwarg"): ...
def func_decorator_with_and_without_args(*dargs, dkwarg="default_kwarg"):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(
f"func_decorator_with_and_without_args: Calling {func.__name__} with args: {args}, kwargs: {kwargs}, "
f"decorator dkwarg: {dkwarg}"
)
return func(*args, **kwargs)
return wrapper
print(f"decorator dargs: {dargs}, decorator dkwarg: {dkwarg}")
if len(dargs) > 1:
raise ValueError("Only one positional argument is allowed (the function to decorate)")
# see if we're being called as @decorator or @decorator()
if len(dargs) == 0:
# we're called with parentheses as @decorator(kwargs...)
return decorator
# we're called as @decorator without parentheses
func = dargs[0]
if not callable(func):
raise TypeError("The function to decorate must be a callable")
return decorator(func)
def test2():
@func_decorator_with_and_without_args
def func(*args, **kwargs):
print(f"func args: {args}, kwargs: {kwargs}")
@func_decorator_with_and_without_args(dkwarg="dkwarg")
def func_with_args(*args, **kwargs):
print(f"func_with_args args: {args}, kwargs: {kwargs}")
try:
@func_decorator_with_and_without_args()
def func_with_empty_parens(*args, **kwargs):
print(f"func_with_empty_parens args: {args}, kwargs: {kwargs}")
except ValueError as e:
print(f"func_with_empty_parens: {e}")
try:
@func_decorator_with_and_without_args("darg")
def func_with_single_arg_invalid(*args, **kwargs):
print(f"func_with_single_arg_invalid args: {args}, kwargs: {kwargs}")
except TypeError as e:
print(f"func_with_single_arg_invalid: {e}")
try:
@func_decorator_with_and_without_args("darg1", "darg2")
def func_with_args_invalid(*args, **kwargs):
print(f"func_with_args_invalid args: {args}, kwargs: {kwargs}")
except ValueError as e:
print(f"func_with_args_invalid: {e}")
try:
@func_decorator_with_and_without_args(dkwarg="dkwarg", dkwarg2="dkwarg2")
def func_with_multiple_kwargs_invalid(*args, **kwargs):
print(f"func_with_multiple_kwargs_invalid args: {args}, kwargs: {kwargs}")
except TypeError as e:
print(f"func_with_multiple_kwargs_invalid: {e}")
print("---------------")
func(1, 2, 3, kwarg1="kwarg1", kwarg2="kwarg2")
func_with_args(1, 2, 3, kwarg1="kwarg1", kwarg2="kwarg2")
test1()
print("---------------")
test2()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment