Add new deprecate_parameter helper#7256
Conversation
The plan is for deprecate_parameter to be a complete drop-in for the former remove_arg and deprecate_kwarg decorators.
|
Cc you @rfezzani, as you were heavily involved with the implementation I am building on. Please let me know if I am stepping on your toes or unintentionally removing any of the nice perks of the previous iteration. |
|
Still working on #7257. |
The old way of detecting the appropriate stacklevel for deprecation warnings was broken. The new approach should be simpler and raises a RuntimeError if the stacklevel can't be determined reliably.
Co-authored-by: Marianne Corvellec <marianne.corvellec@ens-lyon.org>
|
Thanks @mkcor. I've incorporated all your suggestions. I'll mark this as ready for review, since this completes the new decorator |
I find this name clearer and easier to understand than the more abstract "stack rank" which doesn't make the connection to decorators and wrappers obvious.
|
|
||
| .. versionadded:: 0.10 | ||
| arg1 : int, optional | ||
| Second unchanged parameter. |
There was a problem hiding this comment.
Hmm... Since arg1 is added in version 0.10, I understand that no change happens to it (at version 0.12, in the context of this mock function), but I guess its description shouldn't be exactly parallel to arg0's, which was 'always' there, i.e., wasn't added in version 0.10.
| Second unchanged parameter. | |
| Parameter added (not replacing any other parameter). |
There was a problem hiding this comment.
I am not following. In this test function arg1 is a parameter that is unaffected by the decorators. At least to the extend that it was there before. Adding the decorators only affects it in the sense that it's no longer reachable as a positional parameter because that would require passing deprecated parameters before, e.g.:
_func_deprecated_params(0, 1, 2, 3)3 is passed to arg1 but 1, 2 are triggering deprecation warnings.
This is a quirk of deprecating positional arguments. Positional args can only be reached by triggering a deprecation warning if there are deprecated positional args in front. But that's also very useful because at the end of the deprecation we can switch the remaining parameters to keyword-only args!
There was a problem hiding this comment.
Oops, sorry, because of the blank line before
.. versionadded:: 0.10my eyes associated it with what's below (arg1 : int, optional instead of above (new1 : int, optional).
But that's also very useful because at the end of the deprecation we can switch the remaining parameters to keyword-only args!
Nice! So, at the end of the deprecation cycle, we'll have the following function signature
def _func_replace_params(
arg0, new0=None, new1=None, *, arg1=None
):? Or even
def _func_replace_params(
arg0, *, new0=None, new1=None, arg1=None
):? Maybe I'll find the answer as I finish reviewing... 🙏
There was a problem hiding this comment.
Correct. After the deprecation cycle we can be sure that users always got a warning if they used any parameter following old0 as a positional one. So we can move over to def _func_replace_params(arg0, *, new0=None, new1=None, arg1=None) 🎉.
Thinking about this, there is some case that is somewhat tricky to deprecate with a grace period where both APIs work at the same time. When changing
def foo(a, b, c): # old API
# ???
def foo(a, c): # new APIfoo(1, 2) throws a warning because b is deprecated. But if users still want to use c while avoiding the warning and being future proof, they have to use foo(1, c=2). So b needs a default parameter in the meantime, which also makes c need a default parameter, at least temporarily. Do you follow? I find this hard to explain. 😅
There was a problem hiding this comment.
Right. Yes, I'm following... Isn't this case covered by line 487 of skimage/_shared/tests/test_utils.py, when you
assert _func_deprecated_params(1, arg1=3) == (1, DEPRECATED, DEPRECATED, 3)?
There was a problem hiding this comment.
Oh, sorry, maybe I'm getting only now what you really(?) meant: The utilities you're providing with this PR will let us handle the deprecation (any deprecation) on a 'formal' ('external') level but, in cases such as what you're describing, we'll have to adapt the actual code (inside the function) to handle all cases during the deprecation period: if function call is foo(1, 2, 3) elif foo(1, 2) elif foo(1, c=2) else... error? So, yes, it will be more work that just decorating the function with the new deprecate_parameter helper. Is that what you meant?
There was a problem hiding this comment.
Yes, your second guess is correct. Though, I argue that in cases I'm describing, it's very difficult to do a proper deprecation path because a new and old positional API can't be valid at the same time.
|
@lagru I really like 55f4bf6 because, even if 'stack rank' actually makes sense, it's so much easier/faster to grasp the meaning of 'count wrappers!' Along these lines... I've noticed you used 'replace' (replace parameters, replace docstring, replace warning, ...) where I would rather expect 'update' -- I find 'replace' a bit non-standard in a computing context. I feel bad for asking you for a change affecting so many lines of code, since you've put in a lot of effort in this very comprehensive PR! 😬 |
|
PS: Personally, statistics comes to mind first when I read 'replace' in a computing context. |
Co-authored-by: Marianne Corvellec <marianne.corvellec@ens-lyon.org>
|
Re #7256 (comment): This is semantics and may depend on which specific cases you are referring to but I feel like it should be "replace parameter". It may just be my mental model but I find "update parameter" misleading. In my mind we are replacing the deprecated parameter with a successor. It's not a modified version of the former which "update" would suggest. I am not sure if you are getting at the |
|
About |
|
Failure is unrelated and addressed in #7275. |
|
Ok, I'm fine with the term 'replace.'
Yes... Naively speaking, I would find it more consistent if these were named |
|
Weird, I can't explain the test failure in the pre-release job yet but it seems unrelated to this PR. I can reproduce the unexpected warning if I install |
Very good question. I would tend to be pragmatic (lazy?) and keep |
Yes, this warning (treated as error) shows up in the other recent PRs as well. |
Co-authored-by: Marianne Corvellec <marianne.corvellec@ens-lyon.org>
Co-authored-by: Marianne Corvellec <marianne.corvellec@ens-lyon.org>
|
🎉 Thank you for the reviews @mkcor! Excited to hack away at skimage2 now. |
Description
The plan is for
deprecate_parameterto be a complete drop-in for the formerremove_arganddeprecate_kwargdecorators (which I'll remove in a follow-up PR). It's mean to address several concerns I have with our current solution:remove_argdoesn't account for this,deprecate_kwargignores*args.remove_arg, the deprecated parameter must be left in the signature during the deprecation cycle, fordeprecate_kwargit's meant to be removed. The new approach forces the deprecated parameter to a default valueDEPRECATEDthat makes sure that the parameter is no longer in use and its status is immediately visible in the signature. Some linters such as PyCharm even highlight using the deprecated parameter as a potential error.deprecate_kwargare passed, only a warning is shown. In my opinion this should be an error, as it's totally ambiguous which passed value takes precedence.This is work to make deprecations for skimage2 less work. See also
filters.gaussian#7225plot_matchesin favor ofplot_matched_features#7255Checklist
./doc/examplesfor new featuresRelease note
Summarize the introduced changes in the code block below in one or a few sentences. The
summary will be included in the next release notes automatically: