def _import_module_functions(module_path): '''Import a module and get the functions and return them in a dict''' module = importlib.import_module(module_path) return { k: v for k, v in authz.get_local_functions(module) }
def _import_module_functions( module_path: str) -> dict[str, Callable[..., Any]]: '''Import a module and get the functions and return them in a dict''' module = importlib.import_module(module_path) return { k: v for k, v in authz.get_local_functions(module) }
def get_functions(module_root): import ckan.authz as authz fns = {} for auth_module_name in [ "get", "create", "update", "delete", "patch", ]: module_path = "%s.%s" % (module_root, auth_module_name) module = importlib.import_module(module_path) members = authz.get_local_functions(module) for key, v in members: name = "%s: %s" % (auth_module_name, key) fns[name] = v return fns
def _get_public_members(module: types.ModuleType) -> dict[str, Any]: return _get_explicit_members(module) or dict(get_local_functions(module))
def get_action(action: str) -> Action: '''Return the named :py:mod:`ckan.logic.action` function. For example ``get_action('package_create')`` will normally return the :py:func:`ckan.logic.action.create.package_create()` function. For documentation of the available action functions, see :ref:`api-reference`. You should always use ``get_action()`` instead of importing an action function directly, because :py:class:`~ckan.plugins.interfaces.IActions` plugins can override action functions, causing ``get_action()`` to return a plugin-provided function instead of the default one. Usage:: import ckan.plugins.toolkit as toolkit # Call the package_create action function: toolkit.get_action('package_create')(context, data_dict) As the context parameter passed to an action function is commonly:: context = {'model': ckan.model, 'session': ckan.model.Session, 'user': user} an action function returned by ``get_action()`` will automatically add these parameters to the context if they are not defined. This is especially useful for plugins as they should not really be importing parts of ckan eg :py:mod:`ckan.model` and as such do not have access to ``model`` or ``model.Session``. If a ``context`` of ``None`` is passed to the action function then the default context dict will be created. .. note:: Many action functions modify the context dict. It can therefore not be reused for multiple calls of the same or different action functions. :param action: name of the action function to return, eg. ``'package_create'`` :type action: string :returns: the named action function :rtype: callable ''' if _actions: if action not in _actions: raise KeyError("Action '%s' not found" % action) return _actions[action] # Otherwise look in all the plugins to resolve all possible First # get the default ones in the ckan/logic/action directory Rather # than writing them out in full will use importlib.import_module # to load anything from ckan.logic.action that looks like it might # be an action for action_module_name in ['get', 'create', 'update', 'delete', 'patch']: module = importlib.import_module( '.' + action_module_name, 'ckan.logic.action') for k, v in authz.get_local_functions(module): _actions[k] = v # Whitelist all actions defined in logic/action/get.py as # being side-effect free. if action_module_name == 'get' and \ not hasattr(v, 'side_effect_free'): v.side_effect_free = True # Then overwrite them with any specific ones in the plugins: resolved_action_plugins: dict[str, str] = {} fetched_actions = {} chained_actions = defaultdict(list) for plugin in p.PluginImplementations(p.IActions): for name, action_function in plugin.get_actions().items(): if _is_chained_action(action_function): chained_actions[name].append(action_function) elif name in resolved_action_plugins: raise NameConflict( 'The action %r is already implemented in %r' % ( name, resolved_action_plugins[name] ) ) else: resolved_action_plugins[name] = plugin.name # Extensions are exempted from the auth audit for now # This needs to be resolved later # type_ignore_reason: custom attribute action_function.auth_audit_exempt = True # type: ignore fetched_actions[name] = action_function for name, func_list in chained_actions.items(): if name not in fetched_actions and name not in _actions: # nothing to override from plugins or core raise NotFound('The action %r is not found for chained action' % ( name)) for func in reversed(func_list): # try other plugins first, fall back to core prev_func = fetched_actions.get(name, _actions.get(name)) new_func = functools.partial(func, prev_func) # persisting attributes to the new partial function for attribute, value in func.__dict__.items(): setattr(new_func, attribute, value) fetched_actions[name] = new_func # Use the updated ones in preference to the originals. _actions.update(fetched_actions) # wrap the functions for action_name, _action in _actions.items(): def make_wrapped(_action: Action, action_name: str): def wrapped(context: Optional[Context], data_dict: DataDict, **kw: Any): if kw: log.critical('%s was passed extra keywords %r' % (_action.__name__, kw)) context = _prepopulate_context(context) # Auth Auditing - checks that the action function did call # check_access (unless there is no accompanying auth function). # We push the action name and id onto the __auth_audit stack # before calling the action, and check_access removes it. # (We need the id of the action in case the action is wrapped # inside an action of the same name, which happens in the # datastore) context.setdefault('__auth_audit', []) context['__auth_audit'].append((action_name, id(_action))) # check_access(action_name, context, data_dict=None) result = _action(context, data_dict, **kw) try: audit = context['__auth_audit'][-1] if audit[0] == action_name and audit[1] == id(_action): if action_name not in authz.auth_functions_list(): log.debug('No auth function for %s' % action_name) elif not getattr(_action, 'auth_audit_exempt', False): raise Exception( 'Action function {0} did not call its ' 'auth function' .format(action_name)) # remove from audit stack context['__auth_audit'].pop() except IndexError: pass return result return wrapped fn = make_wrapped(_action, action_name) # we need to mirror the docstring fn.__doc__ = _action.__doc__ # we need to retain the side effect free behaviour if getattr(_action, 'side_effect_free', False): # type_ignore_reason: custom attribute fn.side_effect_free = True # type: ignore _actions[action_name] = fn return _actions[action]