diff --git a/pygmt/helpers/__init__.py b/pygmt/helpers/__init__.py index 4b9c4051e80..b52f2c5022a 100644 --- a/pygmt/helpers/__init__.py +++ b/pygmt/helpers/__init__.py @@ -2,6 +2,7 @@ Functions, classes, decorators, and context managers to help wrap GMT modules. """ from pygmt.helpers.decorators import ( + check_data_input_order, deprecate_parameter, fmt_docstring, kwargs_to_strings, diff --git a/pygmt/helpers/decorators.py b/pygmt/helpers/decorators.py index bf270d3455d..ce6fd0abd6b 100644 --- a/pygmt/helpers/decorators.py +++ b/pygmt/helpers/decorators.py @@ -811,3 +811,63 @@ def new_module(*args, **kwargs): return new_module return deprecator + + +def check_data_input_order(deprecate_version, remove_version): + """ + Decorator to raise a FutureWarning if the order of data input parameters + changes and positional arguments are passed. + + The decorator is temporary and should be removed in v0.7.0. + + Parameters + ---------- + deprecate_version : str + The PyGMT version when the order of data input parameters is changed. + remove_version : str + The PyGMT version when the deprecation warning should be removed. + + Examples + -------- + >>> @check_data_input_order("v0.0.0", "v9.9.9") + ... def module(data=None, x=None, y=None, z=None, **kwargs): + ... "A module that prints the arguments it received" + ... print(f"data={data}, x={x}, y={y}, z={z}") + >>> module(data="table.txt") + data=table.txt, x=None, y=None, z=None + >>> module(x=0, y=1, z=2) + data=None, x=0, y=1, z=2 + >>> with warnings.catch_warnings(record=True) as w: + ... module(0, 1, 2) + ... assert len(w) == 1 + ... assert issubclass(w[0].category, FutureWarning) + ... + data=0, x=1, y=2, z=None + """ + + def data_input_order_checker(module_func): + """ + The decorator that creates the new function to check if positional + arguments are passed. + """ + + @functools.wraps(module_func) + def new_module(*args, **kwargs): + """ + New module instance that raises a warning if positional arguments + are passed. + """ + if len(args) > 0: # positional arguments are used + msg = ( + "The function parameters has been re-ordered as 'data, x, y, [z]' " + f"since {deprecate_version} but you're passing positional arguments. " + "You can silence the warning by passing keyword arguments " + "like 'x=x, y=y, z=z'. Otherwise, the warning will be removed " + f"in {remove_version}." + ) + warnings.warn(msg, category=FutureWarning, stacklevel=2) + return module_func(*args, **kwargs) + + return new_module + + return data_input_order_checker