https://pantsbuild.org/ logo
#general
Title
# general
h

hundreds-breakfast-49010

11/07/2019, 9:38 PM
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

aloof-angle-91616

11/07/2019, 9:41 PM
yes i think
h

hundreds-father-404

11/07/2019, 9:41 PM
It is possible. Let me see if it's the Python cookbook
a

aloof-angle-91616

11/07/2019, 9:41 PM
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

hundreds-father-404

11/07/2019, 9:41 PM
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

aloof-angle-91616

11/07/2019, 9:42 PM
this is for rendering i think
h

hundreds-breakfast-49010

11/07/2019, 9:42 PM
if I just pass an argument to the
console_rule
decorator, then I think that blows away the original function
a

aloof-angle-91616

11/07/2019, 9:42 PM
yes, you have to modify the decorator to add that check
h

hundreds-breakfast-49010

11/07/2019, 9:42 PM
@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

hundreds-father-404

11/07/2019, 9:43 PM
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

hundreds-breakfast-49010

11/07/2019, 9:43 PM
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

hundreds-father-404

11/07/2019, 9:45 PM
recipe 9.6. I think there's an online copy. I'll tl;dr it:
a

aloof-angle-91616

11/07/2019, 9:48 PM
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

hundreds-father-404

11/07/2019, 9:48 PM
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

aloof-angle-91616

11/07/2019, 9:51 PM
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

hundreds-father-404

11/07/2019, 9:51 PM
Python Cookbook is an exceptionally helpful book, particularly because of the chapter on decorators
h

hundreds-breakfast-49010

11/07/2019, 9:52 PM
is there a link to an online version of the book?
@aloof-angle-91616 that's a good idea
h

hundreds-father-404

11/07/2019, 9:53 PM
e

early-needle-54791

11/07/2019, 10:40 PM
You can have a variadic arg capture after a kwarg, wahhhht
a

aloof-angle-91616

11/08/2019, 2:51 AM
@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
5 Views