From 801162517ac3e23b069f21665abd2eeeaad645c1 Mon Sep 17 00:00:00 2001 From: Toshio Kuratomi Date: Tue, 17 Sep 2024 01:29:04 -0700 Subject: [PATCH] Allow config_schemas to be Sequences (lists and tuples) of config fields. * Add an _is_config_sequence() function to validate this and use that in the definition of actor metadata. Note that most of the other validation functions used here can be retrofitted to use this implementation as well. --- leapp/actors/__init__.py | 42 ++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/leapp/actors/__init__.py b/leapp/actors/__init__.py index 4f539316..65f0f069 100644 --- a/leapp/actors/__init__.py +++ b/leapp/actors/__init__.py @@ -1,6 +1,8 @@ +import functools import logging import os import sys +from collections.abc import Sequence from leapp.actors.config import Config, retrieve_config from leapp.compat import string_types @@ -402,25 +404,30 @@ def _lint_warn(actor, name, type_name): logging.getLogger("leapp.linter").warning("Actor %s field %s should be a tuple of %s", actor, name, type_name) -def _is_model_tuple(actor, name, value): - if isinstance(value, type) and issubclass(value, Model): - _lint_warn(actor, name, "Models") +def _is_foo_sequence(cls, cls_name, actor, name, value): + if isinstance(value, type) and issubclass(value, cls): + _lint_warn(actor, name, cls_name) value = (value,) - _is_type(tuple)(actor, name, value) - if not all([True] + [isinstance(item, type) and issubclass(item, Model) for item in value]): + _is_type(Sequence)(actor, name, value) + if not all([True] + [isinstance(item, type) and issubclass(item, cls) for item in value]): raise WrongAttributeTypeError( - 'Actor {} attribute {} should contain only Models'.format(actor, name)) + 'Actor {} attribute {} should contain only {}'.format(actor, name, cls_name)) return value -def _is_dialog_tuple(actor, name, value): - if isinstance(value, Dialog): - _lint_warn(actor, name, "Dialogs") +_is_config_sequence = functools.partial(_is_foo_sequence, Config, "Config") + + +# TODO: model, tag, and api_tuple can be migrated to use _is_foo_sequence. + +def _is_model_tuple(actor, name, value): + if isinstance(value, type) and issubclass(value, Model): + _lint_warn(actor, name, "Models") value = (value,) _is_type(tuple)(actor, name, value) - if not all([True] + [isinstance(item, Dialog) for item in value]): + if not all([True] + [isinstance(item, type) and issubclass(item, Model) for item in value]): raise WrongAttributeTypeError( - 'Actor {} attribute {} should contain only Dialogs'.format(actor, name)) + 'Actor {} attribute {} should contain only Models'.format(actor, name)) return value @@ -446,6 +453,17 @@ def _is_api_tuple(actor, name, value): return value +def _is_dialog_tuple(actor, name, value): + if isinstance(value, Dialog): + _lint_warn(actor, name, "Dialogs") + value = (value,) + _is_type(tuple)(actor, name, value) + if not all([True] + [isinstance(item, Dialog) for item in value]): + raise WrongAttributeTypeError( + 'Actor {} attribute {} should contain only Dialogs'.format(actor, name)) + return value + + def _get_attribute(actor, name, validator, required=False, default_value=None, additional_info='', resolve=None): if resolve: value = resolve(actor, name) @@ -482,7 +500,7 @@ def get_actor_metadata(actor): _get_attribute(actor, 'dialogs', _is_dialog_tuple, required=False, default_value=()), _get_attribute(actor, 'description', _is_type(string_types), required=False, default_value=actor.__doc__ or 'There has been no description provided for this actor.'), - _get_attribute(actor, 'config_schemas', _is_type(Config), required=False, + _get_attribute(actor, 'config_schemas', _is_config_sequence, required=False, default_value=actor.__doc__ or 'Description of the configuration used by this actor.'), _get_attribute(actor, 'apis', _is_api_tuple, required=False, default_value=()) ])