python question: is there a way to define a decora...
# general
h
python question: is there a way to define a decorator that can either be called with an argument or without one?
this is in the context of named rules, cf. https://github.com/pantsbuild/pants/issues/7907
💯 1
🔥 1
I want to try re-defining the
@rule
and
@console_rule
decorators to take an optional name argument
without breaking the current uses of those decorators where we dont' call them with arguments
and I'm not sure whether this is possible syntactically in python
a
yes i think
h
It is possible. Let me see if it's the Python cookbook
a
you can check whether the first argument is a function or class or whatever you're supposed to be decorating, and if not, assume that it's being called with arguments
h
can you grab the name from the wrapped function's name, though? Like
create_pex
. Or you want it in plain English like
Create Pex
?
a
this is for rendering i think
h
if I just pass an argument to the
console_rule
decorator, then I think that blows away the original function
a
yes, you have to modify the decorator to add that check
h
@hundreds-father-404 I think based on that ticket we want to have more control over what the name looks like than just getting the name of the function
h
if I just pass an argument to the
console_rule
decorator, then I think that blows away the original function
Python strongly recommends using
functools.wraps
, in part for this reason. We don't use it much
h
but right now I'm just experimenting, I don't know exactly what the API should look like
https://docs.python.org/3.6/library/functools.html?highlight=functools#functools.update_wrapper <- so given this example, where would arguments to the decorator live?
h
recipe 9.6. I think there's an online copy. I'll tl;dr it:
a
Copy code
dmcclanahan@tw-mbp-dmcclanahan: ~/workspace/codesing 13:43:57 $? 2
X> python
Python 3.7.4 (default, Oct 12 2019, 18:55:28)
[Clang 11.0.0 (clang-1100.0.33.8)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> def decorator_maybe_args(*args, **kwargs):
...   if not kwargs and len(args) == 1 and hasattr(args[0], '__call__'):
...     print('called without any args!!!')
...     return args[0]
...   else:
...     print(f'called with args!! args={args}, kwargs={kwargs}')
...     def inner_decorator(func):
...       print('we are in the inner decorator now!!!')
...       return func
...     return inner_decorator
...
>>> @decorator_maybe_args
... def f():
...   return 3
...
called without any args!!!
>>> f()
3
>>> @decorator_maybe_args('asdf', some_arg='wow')
... def g():
...   return 4
...
called with args!! args=('asdf',), kwargs={'some_arg': 'wow'}
we are in the inner decorator now!!!
>>> g()
4
>>>
h
Copy code
def logged(func=None, *, level=logging.DEBUG):
 if func is None:
   return functools.partial(logged, level=level)
  @wraps(func)
  def wrapper(*args, **kwargs):
     log.log(level, "test")
    return func(*args, **kwargs)
  return wrapper
🔥 2
a
ah wow
that's great
keyword-only args are also bae
and @hundreds-breakfast-49010 i'd definitely recommend using keyword-only args here to remove ambiguity
h
Python Cookbook is an exceptionally helpful book, particularly because of the chapter on decorators
h
is there a link to an online version of the book?
@aloof-angle-91616 that's a good idea
h
e
You can have a variadic arg capture after a kwarg, wahhhht
a
@early-needle-54791 that's that new python 3.7 jazz
it's not a variadic arg, it's "keyword-only" (unless you're referring to the new new python 3.8 jazz aka positional-only arguments)
you can also then have
*args
and
**kwargs
captures after those though
bonkers
bananas