Skip to content

Commit b6ac5a1

Browse files
echedey-lskandersolarAdamRJensen
authored
[MAINT]: Decorator to warn about changed parameter names & allow for deprecation period (#2237)
* Add renamed_kwarg_warning decorator * Example at solarposition.hour_angle() * another day, another test * Flake8 strikes again * Fix no warning test Co-Authored-By: Kevin Anderson <[email protected]> * Add test to remember removing the deprecation decorator * test-driven-development for the win: unchanged remain properties Co-Authored-By: Kevin Anderson <[email protected]> * Update docs * Update fail_on_pvlib_version test comment * Rename&deprecate solarposition.sun_rise_set_transit_spa * flake8 * flake8 no more * date -> time in solarposition.sun_rise_set_transit_spa * Change to ..versionchanged, same format. * Apply suggestions from Adam Co-authored-by: Adam R. Jensen <[email protected]> * Apply Adam review x2 Co-Authored-By: Adam R. Jensen <[email protected]> * Update solarposition.py * Update solarposition.py * Revert solarposition and test_solarposition changes * Had to use main instead of master xd * 😭 --------- Co-authored-by: Kevin Anderson <[email protected]> Co-authored-by: Adam R. Jensen <[email protected]>
1 parent d9acdba commit b6ac5a1

File tree

2 files changed

+124
-0
lines changed

2 files changed

+124
-0
lines changed

Diff for: pvlib/_deprecation.py

+73
Original file line numberDiff line numberDiff line change
@@ -316,3 +316,76 @@ def wrapper(*args, **kwargs):
316316
return finalize(wrapper, new_doc)
317317

318318
return deprecate
319+
320+
321+
def renamed_kwarg_warning(since, old_param_name, new_param_name, removal=""):
322+
"""
323+
Decorator to mark a possible keyword argument as deprecated and replaced
324+
with other name.
325+
326+
Raises a warning when the deprecated argument is used, and replaces the
327+
call with the new argument name. Does not modify the function signature.
328+
329+
.. warning::
330+
Ensure ``removal`` date with a ``fail_on_pvlib_version`` decorator in
331+
the test suite.
332+
333+
.. note::
334+
Not compatible with positional-only arguments.
335+
336+
.. note::
337+
Documentation for the function may updated to reflect the new parameter
338+
name; it is suggested to add a |.. versionchanged::| directive.
339+
340+
Parameters
341+
----------
342+
since : str
343+
The release at which this API became deprecated.
344+
old_param_name : str
345+
The name of the deprecated parameter.
346+
new_param_name : str
347+
The name of the new parameter.
348+
removal : str, optional
349+
The expected removal version, in order to compose the Warning message.
350+
351+
Examples
352+
--------
353+
>>> @renamed_kwarg_warning("1.4.0", "old_name", "new_name", "1.6.0")
354+
>>> def some_function(new_name=None):
355+
>>> pass
356+
>>> some_function(old_name=1)
357+
Parameter 'old_name' has been renamed since 1.4.0. and
358+
will be removed in 1.6.0. Please use 'new_name' instead.
359+
360+
>>> @renamed_kwarg_warning("1.4.0", "old_name", "new_name")
361+
>>> def some_function(new_name=None):
362+
>>> pass
363+
>>> some_function(old_name=1)
364+
Parameter 'old_name' has been renamed since 1.4.0. and
365+
will be removed soon. Please use 'new_name' instead.
366+
"""
367+
368+
def deprecate(func, old=old_param_name, new=new_param_name, since=since):
369+
def wrapper(*args, **kwargs):
370+
if old in kwargs:
371+
if new in kwargs:
372+
raise ValueError(
373+
f"{func.__name__} received both '{old}' and '{new}', "
374+
"which are mutually exclusive since they refer to the "
375+
f"same parameter. Please remove deprecated '{old}'."
376+
)
377+
warnings.warn(
378+
f"Parameter '{old}' has been renamed since {since}. "
379+
f"and will be removed "
380+
+ (f"in {removal}" if removal else "soon")
381+
+ f". Please use '{new}' instead.",
382+
_projectWarning,
383+
stacklevel=2,
384+
)
385+
kwargs[new] = kwargs.pop(old)
386+
return func(*args, **kwargs)
387+
388+
wrapper = functools.wraps(func)(wrapper)
389+
return wrapper
390+
391+
return deprecate

Diff for: pvlib/tests/test__deprecation.py

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"""
2+
Test the _deprecation module.
3+
"""
4+
5+
import pytest
6+
7+
from pvlib import _deprecation
8+
9+
import warnings
10+
11+
12+
@pytest.fixture
13+
def renamed_kwarg_func():
14+
"""Returns a function decorated by renamed_kwarg_warning.
15+
This function is called 'func' and has a docstring equal to 'docstring'.
16+
"""
17+
18+
@_deprecation.renamed_kwarg_warning(
19+
"0.1.0", "old_kwarg", "new_kwarg", "0.2.0"
20+
)
21+
def func(new_kwarg):
22+
"""docstring"""
23+
return new_kwarg
24+
25+
return func
26+
27+
28+
def test_renamed_kwarg_warning(renamed_kwarg_func):
29+
# assert decorated function name and docstring are unchanged
30+
assert renamed_kwarg_func.__name__ == "func"
31+
assert renamed_kwarg_func.__doc__ == "docstring"
32+
33+
# assert no warning is raised when using the new kwarg
34+
with warnings.catch_warnings():
35+
warnings.simplefilter("error")
36+
assert renamed_kwarg_func(new_kwarg=1) == 1 # as keyword argument
37+
assert renamed_kwarg_func(1) == 1 # as positional argument
38+
39+
# assert a warning is raised when using the old kwarg
40+
with pytest.warns(Warning, match="Parameter 'old_kwarg' has been renamed"):
41+
assert renamed_kwarg_func(old_kwarg=1) == 1
42+
43+
# assert an error is raised when using both the old and new kwarg
44+
with pytest.raises(ValueError, match="they refer to the same parameter."):
45+
renamed_kwarg_func(old_kwarg=1, new_kwarg=2)
46+
47+
# assert when not providing any of them
48+
with pytest.raises(
49+
TypeError, match="missing 1 required positional argument"
50+
):
51+
renamed_kwarg_func()

0 commit comments

Comments
 (0)