diff --git a/changes/8215.feature b/changes/8215.feature new file mode 100644 index 00000000000..fd34ea398c1 --- /dev/null +++ b/changes/8215.feature @@ -0,0 +1,2 @@ +Add ``ckan.logic.schema.validator_args`` and ``ckan.logic.validate`` decorators +to toolkit. diff --git a/ckan/logic/__init__.py b/ckan/logic/__init__.py index 5aa91d7244c..0faa6747b9e 100644 --- a/ckan/logic/__init__.py +++ b/ckan/logic/__init__.py @@ -665,8 +665,23 @@ def get_or_bust( def validate(schema_func: Callable[[], Schema], can_skip_validator: bool = False) -> Callable[[Action], Action]: - ''' A decorator that validates an action function against a given schema - ''' + """A decorator that validates an action function against a given schema. + + Example:: + + def schema_func(): + return { + "a": [get_validator("int_validator")], + "__extras": [get_validator("ignore")] + } + + @validate_action_data(schema_function) + def my_action(context, data_dict): + return data_dict + + data = {"a": "1", "b": "2"} + assert my_action({}, data) == {"a": 1} + """ def action_decorator(action: Action) -> Action: @functools.wraps(action) def wrapper(context: Context, data_dict: DataDict): diff --git a/ckan/logic/schema.py b/ckan/logic/schema.py index acf61448d96..7ea45dfea26 100644 --- a/ckan/logic/schema.py +++ b/ckan/logic/schema.py @@ -13,8 +13,19 @@ def validator_args(fn: ComplexSchemaFunc) -> PlainSchemaFunc: - u'''collect validator names from argument names - and pass them to wrapped function''' + """Collect validator names from argument names and pass them to wrapped + function. + + Example:: + + @validator_args + def schema_function(not_empty, ignore): + return not_empty, ignore + + ne, ig = schema_function() + assert ne is get_validator("not_empty") + assert ig is get_validator("ignore") + """ args = inspect.signature(fn).parameters diff --git a/ckan/plugins/toolkit.py b/ckan/plugins/toolkit.py index 66215ce9c19..b93f3753948 100644 --- a/ckan/plugins/toolkit.py +++ b/ckan/plugins/toolkit.py @@ -33,6 +33,8 @@ auth_allow_anonymous_access, auth_disallow_anonymous_access, fresh_context, + validate, + schema, ) import ckan.plugins.blanket as blanket @@ -82,30 +84,73 @@ from ckan.model.base import BaseModel __all__ = [ - "ckan", "base", "render", "abort", - "get_action", "check_access", - "get_validator", "get_converter", - "chained_auth_function", "chained_action", - "ObjectNotFound", "NotAuthorized", "ValidationError", "UnknownValidator", - "get_or_bust", - "side_effect_free", "auth_sysadmins_check", - "auth_allow_anonymous_access", "auth_disallow_anonymous_access", - "blanket", "signals", "enqueue_job", - "Invalid", "navl_validate", "missing", "StopOnError", - "h", "literal", "chained_helper", "redirect_to", "url_for", - "CkanVersionException", "HelperError", - "config", "_", "ungettext", "g", "c", "request", - "asbool", "asint", "aslist", "login_user", "logout_user", "current_user", - "DefaultDatasetForm", "DefaultGroupForm", "DefaultOrganizationForm", - "error_shout", - "mail_recipient", "mail_user", - "render_snippet", "add_template_directory", "add_public_directory", + "BaseModel", + "CkanVersionException", + "DefaultDatasetForm", + "DefaultGroupForm", + "DefaultOrganizationForm", + "HelperError", + "Invalid", + "NotAuthorized", + "ObjectNotFound", + "StopOnError", + "UnknownValidator", + "ValidationError", + "_", + "abort", + "add_public_directory", "add_resource", - "check_ckan_version", "requires_ckan_version", "get_endpoint", - "fresh_context", "BaseModel", + "add_template_directory", + "asbool", + "asint", + "aslist", + "auth_allow_anonymous_access", + "auth_disallow_anonymous_access", + "auth_sysadmins_check", + "base", + "blanket", + "c", + "chained_action", + "chained_auth_function", + "chained_helper", + "check_access", + "check_ckan_version", + "ckan", + "config", + "current_user", + "enqueue_job", + "error_shout", + "fresh_context", + "g", + "get_action", + "get_converter", + "get_endpoint", + "get_or_bust", + "get_validator", + "h", + "literal", + "login_user", + "logout_user", + "mail_recipient", + "mail_user", + "missing", + "navl_validate", + "redirect_to", + "render", + "render_snippet", + "request", + "requires_ckan_version", + "side_effect_free", + "signals", + "ungettext", + "url_for", + "validate_action_data", + "validator_args", ] get_converter = get_validator +validate_action_data = validate +validator_args = schema.validator_args # Wrapper for the render_snippet function as it uses keywords rather than