From 8822de05d78e6affb168e1bfa59ea4f953b0a6e3 Mon Sep 17 00:00:00 2001 From: kmantel <1592123+kmantel@users.noreply.github.com> Date: Wed, 20 Nov 2024 20:33:05 -0500 Subject: [PATCH] Component: make deprecated arg error via illegal args; deprecate 'size' (#3123) --- psyneulink/core/components/component.py | 19 ++++++++++++++++++ tests/components/test_component.py | 26 +++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index fa00fbf1be..8beb4fc7c6 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -931,6 +931,9 @@ class Component(MDFSerializable, metaclass=ComponentsMeta): componentType = None standard_constructor_args = {EXECUTE_UNTIL_FINISHED, FUNCTION_PARAMS, MAX_EXECUTIONS_BEFORE_FINISHED, RESET_STATEFUL_FUNCTION_WHEN, INPUT_SHAPES} + deprecated_constructor_args = { + 'size': 'input_shapes', + } # helper attributes for MDF model spec _model_spec_id_parameters = 'parameters' @@ -2150,8 +2153,11 @@ def alias_conflicts(alias, passed_name): conflicting_aliases = [] unused_constructor_args = {} + deprecated_args = {} for p in self.parameters: if p.name in illegal_passed_args: + # p must have a constructor_argument, because otherwise + # p.name would not be in illegal_passed_args assert p.constructor_argument is not None unused_constructor_args[p.name] = p.constructor_argument @@ -2164,6 +2170,12 @@ def alias_conflicts(alias, passed_name): if alias_conflicts(p, passed_name): conflicting_aliases.append((p.source.name, passed_name, p.name)) + for arg in illegal_passed_args: + try: + deprecated_args[arg] = self.deprecated_constructor_args[arg] + except KeyError: + continue + # raise constructor arg errors if len(unused_constructor_args) > 0: raise create_illegal_argument_error([ @@ -2171,6 +2183,13 @@ def alias_conflicts(alias, passed_name): for arg, constr_arg in unused_constructor_args.items() ]) + # raise deprecated argument errors + if len(deprecated_args) > 0: + raise create_illegal_argument_error([ + f"'{arg}' is deprecated. Use '{new_arg}' instead" + for arg, new_arg in deprecated_args.items() + ]) + # raise generic illegal argument error unknown_args = illegal_passed_args.difference(unused_constructor_args) if len(unknown_args) > 0: diff --git a/tests/components/test_component.py b/tests/components/test_component.py index 08237bec3e..668e738c72 100644 --- a/tests/components/test_component.py +++ b/tests/components/test_component.py @@ -130,6 +130,13 @@ def test_execute_manual_context(self, component_type): class TestConstructorArguments: class NewTestMech(pnl.Mechanism_Base): + deprecated_constructor_args = { + **pnl.Mechanism_Base.deprecated_constructor_args, + **{ + 'deprecated_param': 'new_param', + } + } + class Parameters(pnl.Mechanism_Base.Parameters): cca_param = pnl.Parameter('A', constructor_argument='cca_constr') param_with_alias = pnl.Parameter(None, constructor_argument='pwa_constr_arg', aliases=['pwa_alias']) @@ -226,6 +233,25 @@ def test_invalid_argument(self, cls_, argument_name, param_value, params_dict_en constr_arg = getattr(cls_.parameters, argument_name).constructor_argument assert f"'{argument_name}': must use '{constr_arg}' instead" in str(err.value) + @pytest.mark.parametrize( + 'cls_, argument_name, new_name', + [ + (NewTestMech, 'deprecated_param', 'new_param'), + (NewTestMech, 'size', 'input_shapes'), + (pnl.TransferMechanism, 'size', 'input_shapes'), + ] + ) + @pytest.mark.parametrize('params_dict_entry', [NotImplemented, 'params']) + def test_invalid_argument_deprecated(self, cls_, argument_name, new_name, params_dict_entry): + with pytest.raises( + pnl.ComponentError, + match=( + rf".*Illegal argument in constructor \(type: {cls_.__name__}\):" + f"\n\t'{argument_name}' is deprecated. Use '{new_name}' instead" + ) + ): + cls_(**nest_dictionary({argument_name: new_name}, params_dict_entry)) + @pytest.mark.parametrize( 'cls_, param_name, param_value, alias_name, alias_value', [