Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Templates not overriden due to plugin ordering issues in 2.9 #5965

@amercader

Description

@amercader

Tl;DR:

  • PluginImplementations return plugins in the order that their class (extending SingletonPlugin) is first evaluated, not when the plugin is loaded
  • This causes issues like the template folders always being loaded in a particular position
  • Let's ignore how pyutilib returns the plugins and always enforce the order in the ini file

This is related to #5731 but a different issue. The initial symptom was that some templates from plugin1 that I was overriding from plugin2 never got applied regardless of the ordering of plugin2 in ckan.plugins. Inspecting the config['computed_template_paths'] you could tell that the templates from plugin1 always were loaded first than the plugin2, regardless of the order of the plugins in the ini file.

As with all plugin ordering issues, this order eventually boils down to the order in which plugins are returned by the PluginImplementations iterator (in this particular case in the one called in environment.py to call the update_config() hook where the template dirs are registered).

pyutilib uses an internal _id property to order the output list of plugins that implement a particular interface when using PluginImplementations. Our assumption has always been that this order was determined by the order in which plugins were loaded by CKAN, and in most cases tsat is true. But the actual time this internal _id property is set is when the plugin class is initialized, ie when we define a plugin class that implements SingletonPlugin, eg:

import ckan.plugins as p


class MyPlugin1(p.SingletonPlugin):

    p.implements(ITemplateHelpers)

    def get_helpers(self):
        return {'plugin1_helper': plugin1_helper}


def plugin1_helper():
    pass

That is fine if plugins don't import modules from other plugins, but if they do, when evaluating the imports they can initialize the other plugin class, and so that one will be always loaded first.

For instance:

import ckan.plugins as p

from ckanext.plugin1.plugin import plugin1_helper


class MyPlugin2(p.SingletonPlugin):
    
    #...

When loading plugin2, the imports will get processed and MyPlugin1 will get evaluated, receiving a lower _id than plugin1 and so always being returned first than plugin1 regardless of the ordering in the ini file.
This particular case might seem like an edge case but note this is a simplified example, the imports could be in another module of plugin2, not necessarily plugin.py. The same ordering issue is happening eg when using text_view, which loads resourceproxy causing it to always come up first.

Considering how plugin ordering is and how difficult these issues are to track down, my suggestion to address this and future issues is that we enforce on the CKAN side that the order of plugins returned by PluginImplementations matches the ones defined in ckan.plugins (as in reordering the iterator returned by pyutilib based on the value of ckan.plugins):

# on ckan/plugins/core.py

from pyutilib.component.core import ExtensionPoint


class PluginImplementations(ExtensionPoint):

    def __iter__(self):

        iterator = super(PluginImplementations, self).__iter__()

        plugins_found = list(iterator)

        plugins_in_config = config.get('ckan.plugins', '').split()

        # TODO reorder plugins_found based on plugins_in_config :)

        return plugins_found

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions