-
-
Notifications
You must be signed in to change notification settings - Fork 18.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DOC: Make doc decorator a class and replace Appender by doc #33160
Conversation
ErrorThe error in commit 6e4b036 was:
FixWe could fix this by converting doc decorator from function to class. The change was made in commit 21fa045. |
Do you mind providing some more details of what are you doing here? Looks reasonable, but not sure I understand what's the problem, and how this change fixes it. |
Hi @datapythonista. I didn't investigate this error very thoroughly, but my guess will be this: Our original decorator returns a function, not a class. We will get this kind of error when we are using @doc("This is a sample docstring")
class A:
pass
class B(A):
pass The fix here is to convert function decorator to class/callable decorator. |
I see, didn't think of that. I don't think you need to inherit from the class to be a problem, even if you don't inherit I think our decorator is transforming it to a function and won't work. But I see two different changes here:
You're implementing both, but I think they are independent, aren't they? I think if we remove I'm happy with the changes, but just to make sure I'm understanding this correctly. Thanks! |
I agree with you that the For my understanding, if we have to use a function, we would have something like: def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
wrapper.__doc__ = "This is a sample docstring"
return wrapper That is the fewer function to be as a decorator I can think of. I am not sure if we can remove either |
Decorators are a bit tricky. If you want to do something like: @decorator
def foo():
pass which is equivalent to: def foo():
pass
foo = decorator(foo) Then, we could simply use: def decorator(func):
func.__doc__ = "This is a sample docstring"
return func Since we call the decorator, then we actually have: @decorator("PARAM")
def foo():
pass Which is equivalent to: def foo():
pass
foo = decorator("PARAM")(foo) And then you need the We currently have |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I checked it again, and I have a small preference for having the decorator as a function (it'll be slightly simpler), but happy to move forward either way, up to you.
If we finally use the decorator, probably you can rename the params args
and kwargs
to the names you're giving in the class.
pandas/util/_decorators.py
Outdated
|
||
def __call__(self, func: F) -> F: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does F
(FuncType
) also represent classes, or just functions? func
is now a function or a class (probably worth changing the name).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point! I agree here. It has been replaced to callable.
@datapythonista Yes, you are right. I didn't think about that. Also, I agree that the use function looks better here. One more thing wants to confirm: |
pandas/util/_decorators.py
Outdated
def wrapper(*args, **kwargs) -> Callable: | ||
return func(*args, **kwargs) | ||
|
||
def decorator(call: Callable) -> Callable: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
call
doesn't seem very descriptive to me, may be decorated_obj
, func_or_class
or something like that?
Is a class of type Callable
, even if it doesn't implement __call__
? @simonjayhawkins may be you can help, the parameter can be any function or any class, is Callable
the appropriate type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIUC then this seems OK since we are passing a class type not instance, and a class type is callable in order to instantiate.
from typing import Callable
class Foo:
pass
def func(call:Callable):
pass
func(Foo)
reveal_type(Foo)
gives
type-test.py:11: note: Revealed type is 'def () -> type-test.Foo'
and no errors. The class type seems to be treated as a function that returns an instance by mypy.
pandas/util/_decorators.py
Outdated
@@ -245,13 +245,13 @@ def wrapper(*args, **kwargs) -> Callable[..., Any]: | |||
return decorate | |||
|
|||
|
|||
def doc(*args: Union[str, Callable], **kwargs: str) -> Callable[[F], F]: | |||
def doc(*args: Union[str, Callable], **kwargs: str) -> Callable[[Callable], Callable]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the names of the parameters, I think a descriptive name is better in this case. I think args
and kwargs
are great names when they are generic, you can pass whatever, and they are passed to another function mostly.
But in this case, they have very specific usage, and they are very different between them.
I think something like *docs
or *docs_or_objs
and **params
could be ok. May be you think of something better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But in this case, they have very specific usage, and they are very different between them.
I like this point! Agree that we should be specific in this case.
For the names, I think docs
and params
are good. I also want to add some other candidates here:
- use
supplements
to replaceargs
- use
fillers
to replacekwargs
I am not a native English speaker, so I am not sure if those words also make sense to you and other developers. Please let me know which one you like and I will be more than happy to make the change.
@HH-MWB this PR is removing some of the typevars to ensure that decorated functions/classes have their typing signatures preserved. I recall discussing this before. do we have tests? |
Hi @simonjayhawkins. Yes, I would be happy to add some tests and/or switch back to original type, but I guess I am a little bit confusing here. I was changing type annotation from Would you mind provide some more information about |
@simonjayhawkins can you have a look at @HH-MWB question? @HH-MWB, once you know about the typing, can you address the latest comments, merge master, and leave the CI green, so we can merge this please? Thanks! |
see https://mypy.readthedocs.io/en/stable/generics.html#declaring-decorators for an explaination on |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me, if @simonjayhawkins is happy with the typing.
Not sure about the names, may be I'd use:
supplements
->docstrings
substitutes
->params
call
->decorated
orfunc_or_class
But it's just a personal opinion. @simonjayhawkins may be you can also give your opinion on the naming?
Thanks @HH-MWB
I think this names makes sense to me, especially use |
naming is hard.
i don't think it makes sense to change the names if not using a class
looking again at the 21fa045, i think the class approach may be clearer is some of the logic had been moved from __call__ to __init__. The __call__ would then be simpler, maybe look similar to the current Appender/Substitution, and the naming maybe be more natural. We also have an underlying bug to fix. i.e. remove the extra wrapper returning a function. So my preference would be to do just the fix (maybe raise a issue for clarity and posterity) here. i.e. no renaming. |
I personally think implementing the docorator with a function instead of a class is simpler. But just my opinion, happy with any of them. For the naming, I think the generic Of course not a big deal, I'm ok calling them @HH-MWB CI is red, if you don't mind having a look. Thanks! |
Hi, I replaced It would be great if you considered this use of If it helps here is more detail about the situation:
Hope this helps with improving the |
Yes, you are right. I had the same issue in my PR. You need to decrement this value by 2 to make the tests pass after removing the wrapper function. |
Thanks @smartvinnetou for providing this information. I guess this new implementation would fix the stacklevel issue. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm, thanks @HH-MWB
Just to make sure I understand the stachlevel issue. Some functions were before wrapped more than once with Appender
and Substitution
and now they are just wrapped once, and that's changing the numbers of levels the warning is stacked. Is that correct?
Do you mind generating the warning that you're changing in core/arrays/datetimelike.py
and add the output in a comment in this PR, so we can have a look, and make sure we're not breaking anything with this PR please?
Hi @datapythonista. I am not 100% sure about this,but my guess is: the old Our old decorator has that extra wrapper function which increased the stacklevel by 2. @smartvinnetou has a merged PR using the old decorator, and the stacklevel has been increased by 2 to pass the CI/CD checks. Now we have to switch it back. |
The error message might be this:
We can also goto the failed commit 6e4b036 for more detail information. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm, thanks @HH-MWB, nice PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm @simonjayhawkins
Thanks @HH-MWB for sticking with this. can you merge upstream/master one more time and ping on green |
Thanks @HH-MWB |
black pandas
git diff upstream/master -u -- "*.py" | flake8 --diff