Skip to content

Instantly share code, notes, and snippets.

@kchawla-pi
Last active December 28, 2021 13:14
Show Gist options
  • Save kchawla-pi/6c7d733858a7e30e27c28df40ef46a4b to your computer and use it in GitHub Desktop.
Save kchawla-pi/6c7d733858a7e30e27c28df40ef46a4b to your computer and use it in GitHub Desktop.
Code snippets used in article "Code. Changing it without breaking it, using a Decorator."
import warnings
def view_connectome(adjacency_matrix,
node_coords,
edge_threshold=None,
edge_cmap=cm.cyan_orange,
symmetric_cmap=True,
linewidth=6.,
node_size=3.,
# placing old params here to preserve old keyworded args.
coords=None, threshold=None, cmap=None, marker_size=None,
):
# code for the deprecation
param_deprecation_msg_template = ('The param {} is being deprecated and will be
replaced by the param {} in future. Please use {}.')
if marker_size:
node_size = marker_size
warnings.warn(param_deprecation_msg_template.format('marker_size',
'node_size',
'node_size',
)
if threshold is not None: # numpy arrays don't have unambiguous truthiness.
edge_threshold = threshold
warnings.warn(param_deprecation_msg_template.format('threshold',
'edge_threshold',
'edge_threshold',
)
if cmap is not None: # numpy arrays don't have unambiguous truthiness.
edge_cmap = cmap
warnings.warn(param_deprecation_msg_template.format('cmap',
'edge_cmap',
'edge_cmap',
)
if coords is not None: # numpy arrays don't have unambiguous truthiness.
node_coords = coords
warnings.warn(param_deprecation_msg_template.format('coords',
'node_coords',
'node_coords',
)
# original functionality of the function,
connectome_info = _get_connectome(adjacency_matrix,
node_coords,
edge_threshold=threshold,
cmap=edge_cmap,
symmetric_cmap=symmetric_cmap,
)
connectome_info["line_width"] = linewidth
connectome_info["marker_size"] = node_size
return _make_connectome_html(connectome_info)
def replace_parameters(replacement_params,
end_version='future',
lib_name='Nilearn',
):
"""
Decorator to deprecate & replace specified parameters
in the decorated functions and methods
without changing function definition or signature.
Parameters
----------
replacement_params : Dict[string, string]
Dict where the key-value pairs represent the old parameters
and their corresponding new parameters.
Example: {old_param1: new_param1, old_param2: new_param2,...}
end_version : str (optional) {'future' (default) | 'next' | <version>}
Version when using the deprecated parameters will raise an error.
For informational purpose in the warning text.
lib_name: str (optional) (Default: 'Nilearn')
Name of the library to which the decoratee belongs.
For informational purpose in the warning text.
"""
def _replace_params(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
_warn_deprecated_params(replacement_params, end_version, lib_name,
kwargs
)
kwargs = _transfer_deprecated_param_vals(replacement_params,
kwargs
)
return func(*args, **kwargs)
return wrapper
return _replace_params
def _warn_deprecated_params(replacement_params, end_version, lib_name, kwargs):
""" For the decorator replace_parameters(),
raises warnings about deprecated parameters.
Parameters
----------
replacement_params: Dict[str, str]
Dictionary of old_parameters as keys with replacement parameters
as their corresponding values.
end_version: str
The version where use of the deprecated parameters will raise an error.
For informational purpose in the warning text.
lib_name: str
Name of the library. For informational purpose in the warning text.
kwargs: Dict[str, any]
Dictionary of all the keyword args passed on the decorated function.
"""
used_deprecated_params = set(kwargs).intersection(replacement_params)
for deprecated_param_ in used_deprecated_params:
replacement_param = replacement_params[deprecated_param_]
param_deprecation_msg = (
'The parameter "{}" will be removed in {} release of {}. '
'Please use the parameter "{}" instead.'.format(deprecated_param_,
end_version,
lib_name,
replacement_param,
)
)
warnings.filterwarnings('always', message=param_deprecation_msg)
warnings.warn(category=DeprecationWarning,
message=param_deprecation_msg,
stacklevel=3)
def _transfer_deprecated_param_vals(replacement_params, kwargs):
""" For the decorator replace_parameters(), reassigns new parameters
the values passed to their corresponding deprecated parameters.
Parameters
----------
replacement_params: Dict[str, str]
Dictionary of old_parameters as keys with replacement parameters
as their corresponding values.
kwargs: Dict[str, any]
Dictionary of all the keyword args passed on the decorated function.
Returns
-------
kwargs: Dict[str, any]
Dictionary of all the keyword args to be passed on
to the decorated function, with old parameter names
replaced by new parameters, with their values intact.
"""
for old_param, new_param in replacement_params.items():
old_param_val = kwargs.setdefault(old_param, None)
if old_param_val is not None:
kwargs[new_param] = old_param_val
kwargs.pop(old_param)
return kwargs
def _replacement_params_view_connectome():
""" Returns a dict containing deprecated & replacement parameters
as key-value pair for view_connectome().
Avoids cluttering the global namespace.
"""
return {
'coords': 'node_coords',
'threshold': 'edge_threshold',
'cmap': 'edge_cmap',
'marker_size': 'node_size',
}
@replace_parameters(replacement_params=_replacement_params_view_connectome(),
end_version='0.6.0',
lib_name='Nilearn',
)
def view_connectome(adjacency_matrix, node_coords, edge_threshold=None,
edge_cmap=cm.bwr, symmetric_cmap=True,
linewidth=6., node_size=3.,
):
connectome_info = _get_connectome(
adjacency_matrix, node_coords, threshold=edge_threshold, cmap=edge_cmap,
symmetric_cmap=symmetric_cmap, marker_size=node_size)
return _make_connectome_html(connectome_info)
from nilearn._utils.helpers import replace_parameters
def _mock_args_for_testing_replace_parameter():
"""
:return: Creates mock deprecated & replacement parameters for use with
testing functions related to replace_parameters().
"""
mock_kwargs_with_deprecated_params_used = {
'unchanged_param_0': 'unchanged_param_0_val',
'deprecated_param_0': 'deprecated_param_0_val',
'deprecated_param_1': 'deprecated_param_1_val',
'unchanged_param_1': 'unchanged_param_1_val',
}
replacement_params = {
'deprecated_param_0': 'replacement_param_0',
'deprecated_param_1': 'replacement_param_1',
}
return mock_kwargs_with_deprecated_params_used, replacement_params
def test_replace_parameters():
""" Integration tests that deprecates mock parameters in a mock function
and checks that the deprecated parameters transfer their values correctly
to replacement parameters and all deprecation warning are raised as
expected.
"""
mock_input, replacement_params = _mock_args_for_testing_replace_parameter()
expected_output = ('dp0', 'dp1', 'up0', 'up1')
expected_warnings = [
('The parameter "deprecated_param_0" will be removed in 0.6.1rc '
'release of other_lib. Please use the parameter "replacement_param_0"'
' instead.'
),
('The parameter "deprecated_param_1" will be removed in 0.6.1rc '
'release of other_lib. Please use the parameter "replacement_param_1"'
' instead.'
),
]
@replace_parameters(replacement_params, '0.6.1rc', 'other_lib', )
def mock_function(replacement_param_0, replacement_param_1,
unchanged_param_0, unchanged_param_1):
return (replacement_param_0, replacement_param_1, unchanged_param_0,
unchanged_param_1
)
with warnings.catch_warnings(record=True) as raised_warnings:
actual_output = mock_function(deprecated_param_0='dp0',
deprecated_param_1='dp1',
unchanged_param_0='up0',
unchanged_param_1='up1',
)
assert actual_output == expected_output
expected_warnings.sort()
raised_warnings.sort(key=lambda mem: str(mem.message))
for raised_warning_, expected_warning_ in zip(raised_warnings,
expected_warnings):
assert str(raised_warning_.message) == expected_warning_
def test_transfer_deprecated_param_vals():
""" Unit test to check that values assigned to deprecated parameters are
correctly reassigned to the replacement parameters.
"""
mock_input, replacement_params = _mock_args_for_testing_replace_parameter()
expected_output = {
'unchanged_param_0': 'unchanged_param_0_val',
'replacement_param_0': 'deprecated_param_0_val',
'replacement_param_1': 'deprecated_param_1_val',
'unchanged_param_1': 'unchanged_param_1_val',
}
actual_ouput = helpers._transfer_deprecated_param_vals(
replacement_params,
mock_input,
)
assert actual_ouput == expected_output
def test_future_warn_deprecated_params():
""" Unit test to check that the correct warning is displayed.
"""
mock_input, replacement_params = _mock_args_for_testing_replace_parameter()
expected_warnings = [
('The parameter "deprecated_param_0" will be removed in sometime '
'release of somelib. Please use the parameter "replacement_param_0" '
'instead.'
),
('The parameter "deprecated_param_1" will be removed in sometime '
'release of somelib. Please use the parameter "replacement_param_1" '
'instead.'
),
]
with warnings.catch_warnings(record=True) as raised_warnings:
helpers._warn_deprecated_params(
replacement_params,
end_version='sometime',
lib_name='somelib',
kwargs=mock_input,
)
expected_warnings.sort()
raised_warnings.sort(key=lambda mem: str(mem.message))
for raised_warning_, expected_warning_ in zip(raised_warnings,
expected_warnings
):
assert str(raised_warning_.message) == expected_warning_
def view_connectome(adjacency_matrix,
coords,
threshold=None,
cmap=cm.cyan_orange,
symmetric_cmap=True,
linewidth=6.,
marker_size=3.,
):
connectome_info = _get_connectome(
adjacency_matrix, coords, threshold=threshold, cmap=cmap,
symmetric_cmap=symmetric_cmap)
connectome_info["line_width"] = linewidth
connectome_info["marker_size"] = marker_size
return _make_connectome_html(connectome_info)
def view_connectome(adjacency_matrix,
node_coords,
edge_threshold=None,
edge_cmap=cm.cyan_orange,
symmetric_cmap=True,
linewidth=6.,
node_size=3.,
**kwargs,
):
# generate the deprecation warnings
kwargs = _param_deprecation_view_connectome(kwargs)
# handover the args from old params to new ones.
if kwargs['marker_size']:
node_size = marker_size
if kwargs['threshold'] is not None:
edge_threshold = threshold
if kwargs['cmap'] is not None:
edge_cmap = cmap
if kwargs['coords'] is not None:
node_coords = coords
connectome_info = _get_connectome(
adjacency_matrix, node_coords, threshold=edge_threshold, cmap=edge_cmap,
symmetric_cmap=symmetric_cmap)
connectome_info["line_width"] = linewidth
connectome_info["marker_size"] = node_size
return _make_connectome_html(connectome_info)
def _param_deprecation_view_connectome(kwargs):
kwargs.setdefault('marker_size', None)
kwargs.setdefault('threshold', None)
kwargs.setdefault('cmap', None)
kwargs.setdefault('coords', None)
param_deprecation_msg_template = ('The param {} is being deprecated and will be
replaced by the param {} in future. Please
use {}.')
if kwargs['marker_size']:
warnings.warn(param_deprecation_msg_template.format('marker_size',
'node_size',
'node_size',
)
if kwargs['threshold'] is not None: # numpy arrays don't have unambiguous truthiness.
warnings.warn(param_deprecation_msg_template.format('threshold',
'edge_threshold',
'edge_threshold',
)
if kwargs['cmap'] is not None: # numpy arrays don't have unambiguous truthiness.
warnings.warn(param_deprecation_msg_template.format('cmap',
'edge_cmap',
'edge_cmap',
)
if kwargs['coords'] is not None: # numpy arrays don't have unambiguous truthiness.
warnings.warn(param_deprecation_msg_template.format('coords',
'node_coords',
'node_coords',
)
return kwargs
def view_connectome(adjacency_matrix,
node_coords,
edge_threshold=None,
edge_cmap=cm.cyan_orange,
symmetric_cmap=True,
linewidth=6.,
node_size=3.,
**kwargs,
):
# generate the deprecation warnings
kwargs = _param_deprecation_view_connectome(kwargs)
# handover the args from old params to new ones.
if kwargs['marker_size']:
node_size = marker_size
if kwargs['threshold'] is not None:
edge_threshold = threshold
if kwargs['cmap'] is not None:
edge_cmap = cmap
if kwargs['coords'] is not None:
node_coords = coords
connectome_info = _get_connectome(
adjacency_matrix, node_coords, threshold=edge_threshold, cmap=edge_cmap,
symmetric_cmap=symmetric_cmap)
connectome_info["line_width"] = linewidth
connectome_info["marker_size"] = node_size
return _make_connectome_html(connectome_info)
def _param_deprecation_view_connectome(kwargs):
kwargs.setdefault('marker_size', None)
kwargs.setdefault('threshold', None)
kwargs.setdefault('cmap', None)
kwargs.setdefault('coords', None)
param_deprecation_msg_template = ('The param {} is being deprecated and will be
replaced by the param {} in future. Please
use {}.')
if kwargs['marker_size']:
warnings.warn(param_deprecation_msg_template.format('marker_size',
'node_size',
'node_size',
)
if kwargs['threshold'] is not None: # numpy arrays don't have unambiguous truthiness.
warnings.warn(param_deprecation_msg_template.format('threshold',
'edge_threshold',
'edge_threshold',
)
if kwargs['cmap'] is not None: # numpy arrays don't have unambiguous truthiness.
warnings.warn(param_deprecation_msg_template.format('cmap',
'edge_cmap',
'edge_cmap',
)
if kwargs['coords'] is not None: # numpy arrays don't have unambiguous truthiness.
warnings.warn(param_deprecation_msg_template.format('coords',
'node_coords',
'node_coords',
)
return kwargs
def _deprecate_params_view_connectome(func):
""" Decorator to deprecate specific parameters in view_connectome()
without modifying view_connectome().
"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
_warn_deprecated_params_view_connectome(kwargs)
kwargs = _transfer_deprecated_param_vals_view_connectome(kwargs)
return func(*args, **kwargs)
return wrapper
@_deprecate_params_view_connectome
def view_connectome(adjacency_matrix, node_coords, edge_threshold=None,
edge_cmap=cm.bwr, symmetric_cmap=True,
linewidth=6., node_size=3.,
**kwargs):
connectome_info = _get_connectome(
adjacency_matrix, node_coords, threshold=edge_threshold, cmap=edge_cmap,
symmetric_cmap=symmetric_cmap)
connectome_info["line_width"] = linewidth
connectome_info["marker_size"] = node_size
return _make_connectome_html(connectome_info)
def _warn_deprecated_params_view_connectome(kwargs):
""" For view_connectome(), raises warnings about deprecated parameters.
"""
all_deprecated_params = {'coords': 'node_coords',
'threshold': 'edge_threshold',
'cmap': 'edge_cmap',
'marker_size': 'node_size',
}
used_deprecated_params = set(kwargs).intersection(all_deprecated_params)
for deprecated_param_ in used_deprecated_params:
replacement_param = all_deprecated_params[deprecated_param_]
param_deprecation_msg = (
'The parameter "{}" will be removed in Nilearn version 0.6.0. '
'Please use the parameter "{}" instead.'.format(deprecated_param_,
replacement_param,
)
)
warnings.filterwarnings('always', message=param_deprecation_msg)
warnings.warn(category=DeprecationWarning,
message=param_deprecation_msg,
stacklevel=3)
def _transfer_deprecated_param_vals_view_connectome(kwargs):
""" For view_connectome(), reassigns new parameters the values passed
to their corresponding deprecated parameters.
"""
coords = kwargs.get('coords', None)
threshold = kwargs.get('threshold', None)
cmap = kwargs.get('cmap', None)
marker_size = kwargs.get('marker_size', None)
if coords is not None:
kwargs['node_coords'] = coords
if threshold:
kwargs['edge_threshold'] = threshold
if cmap:
kwargs['edge_cmap'] = cmap
if marker_size:
kwargs['node_size'] = marker_size
return kwargs
def test_params_deprecation_view_connectome():
deprecated_params = {'coords': 'node_coords',
'threshold': 'edge_threshold',
'cmap': 'edge_cmap',
'marker_size': 'node_size',
}
deprecation_msg = (
'The parameter "{}" will be removed in Nilearn version 0.6.0. '
'Please use the parameter "{}" instead.'
)
warning_msgs = {old_: deprecation_msg.format(old_, new_)
for old_, new_ in deprecated_params.items()
}
adj, coord = _make_connectome()
with warnings.catch_warnings(record=True) as raised_warnings:
html_connectome.view_connectome(adjacency_matrix=adj,
coords=coord,
edge_threshold='85.3%',
edge_cmap=cm.cyan_orange,
linewidth=8.5, node_size=4.2,
)
html_connectome.view_connectome(adjacency_matrix=adj,
node_coords=coord,
threshold='85.3%',
edge_cmap=cm.cyan_orange,
linewidth=8.5,
node_size=4.2,
)
html_connectome.view_connectome(adjacency_matrix=adj,
node_coords=coord,
edge_threshold='85.3%',
cmap=cm.cyan_orange,
linewidth=8.5,
node_size=4.2,
)
html_connectome.view_connectome(adjacency_matrix=adj,
node_coords=coord,
edge_threshold='85.3%',
edge_cmap=cm.cyan_orange,
linewidth=8.5,
marker_size=4.2,
)
html_connectome.view_connectome(adjacency_matrix=adj,
node_coords=coord,
edge_threshold='85.3%',
edge_cmap=cm.cyan_orange,
linewidth=8.5,
node_size=4.2,
)
html_connectome.view_connectome(adj,
coord,
'85.3%',
cm.cyan_orange,
8.5,
4.2,
)
old_params = ['coords', 'threshold', 'cmap', 'marker_size']
assert len(raised_warnings) == 4
for old_param_, raised_warning_ in zip(old_params, raised_warnings):
assert warning_msgs[old_param_] == str(raised_warning_.message)
assert raised_warning_.category is DeprecationWarning
def view_connectome(adjacency_matrix,
node_coords, # change param name here,
edge_threshold=None, # here,
edge_cmap=cm.cyan_orange, # here,
symmetric_cmap=True,
linewidth=6.,
node_size=3., # and here.
):
connectome_info = _get_connectome(
# rename the variables in this line, ...
adjacency_matrix, node_coords, threshold=edge_threshold, cmap=edge_cmap,
symmetric_cmap=symmetric_cmap)
connectome_info["line_width"] = linewidth
connectome_info["marker_size"] = node_size # ...and here.
return _make_connectome_html(connectome_info)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment