You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
As per discussion in #51 I started thinking about how to address the problem of nested plugin dependencies and clobbering of the global registry state. This is just a very quickly concocted initial design draft so feel free to critique the heck out of it.
The main issue as I understand it is summarized:
the PluginManager contains a global state of plugin hooks.
registering new plugins (and their dependencies) permanently mutates this global hook registry.
sure plugins can be unregistered but there is no way to know what dependencies (other plugins) should also be unregistered.
when dynamically registering and then subsequently unregistering plugins, dependencies which were also registered will not be removed without careful tracking by the user - this results in stale hooks left in the registry which may cause problems for the other plugins not expecting them to be there when operating in a different plugins context.
there is no way to track when a plugin sub-dependency is required by more then one parent plugin
The problem as discovered with pytest was avoided through the limitation in pytest-dev/pytest#3084. Currently sub-conftest.py files can't introduce new plugins dynamically without causing this registry clobbering via plugin dependencies.
An idea for a solution is to add a sub-system whereby each registered plugin is expected to explicitly declare any 1st depth level (plugin) dependencies in order for the PluginManager to keep track of the overall plugin dependency graph thereby allowing for modelling distinct plugins contexts.
Let's set some premises and terminology:
a plugins context is a snap-shot of the dependency tree at some point in time where each plugin in the tree has its hooks already registered (thereby affecting the runtime execution of the host project); execution under different contexts results in different behaviour of the host project
it's not feasible to expect each plugin to know all its (transitive) dependencies only its immediate sub-dependencies
a context can be modelled as the entire n-ary dependency tree of all currently registered plugin nodes where the root sub-tree is top most single depth sub-tree (eg. pytest + the pytest11 entry point children + top level conftest.py children)
the dependency tree may naturally contain cycles (some plugin may have more then one dependent parent plugin) and will not be a DAG
a plugin can be registered specifying 1 optional piece of info: a sub_plugins: [str] list of expected dependency plugin names allowing for tracking each single depth sub-tree layer of the dependency graph
a context can be verified at each step using the existing PluginManager.check_pending() API to ensure dependencies which are not listed are not registered and vice-versa, dependencies which are declared are at some point are in fact registered prior to a call to check_pending()
by keeping track of each single depth sub-tree (a plugin and its immediate 1st level dependencies) we can store a dependency graph of all plugins and use it to conduct traversals for:
listing all downstream dependencies of a particular plugin
unregistering subsets of plugins easily
tracking differences between hook calls under different contexts
API and implementation:
introduce a dependencies or sub_plugins arg to PluginManager.register()
introduce a PluginManager._host_deps a sequence listing the top-most first level deps of the host project (describes the first deps layer)
store an additional 2 internal dictionaries of plugin2deps and dep2parents which respectively allow tracking deps per node (each layer) and parents per node
add an internal tree traversal function (topological sort) which can be used to iterate all downstream deps of a plugin for either the purposes of removal or context comparison
Edge cases:
top level nodes (deps in the first layer) must be distinguished differently since a host project should not be required to know all plugins that may be used; a special API call or flag might be needed when registering root dependencies as they will need to be appended dynamically to the _host_deps set
removing (de-regristration) of any node requires that zero dependents (parent node plugins) point to it
The text was updated successfully, but these errors were encountered:
this looks like a nice initial architecture idea - but at first glance also something we couldn't even hope to use with the current conftest/plugin/loaded plugins mechanisms in pytest
i suspect we need to iterate a few times to get something we can experiment with, however for me its a low hanging fruit i can do with easily another 5 years down the line
As per discussion in #51 I started thinking about how to address the problem of nested plugin dependencies and clobbering of the global registry state. This is just a very quickly concocted initial design draft so feel free to critique the heck out of it.
The main issue as I understand it is summarized:
PluginManager
contains a global state of plugin hooks.The problem as discovered with
pytest
was avoided through the limitation in pytest-dev/pytest#3084. Currently sub-conftest.py
files can't introduce new plugins dynamically without causing this registry clobbering via plugin dependencies.An idea for a solution is to add a sub-system whereby each registered plugin is expected to explicitly declare any 1st depth level (plugin) dependencies in order for the
PluginManager
to keep track of the overall plugin dependency graph thereby allowing for modelling distinct plugins contexts.Let's set some premises and terminology:
pytest
+ thepytest11
entry point children + top levelconftest.py
children)sub_plugins: [str]
list of expected dependency plugin names allowing for tracking each single depth sub-tree layer of the dependency graphPluginManager.check_pending()
API to ensure dependencies which are not listed are not registered and vice-versa, dependencies which are declared are at some point are in fact registered prior to a call tocheck_pending()
API and implementation:
dependencies
orsub_plugins
arg toPluginManager.register()
PluginManager._host_deps
a sequence listing the top-most first level deps of the host project (describes the first deps layer)plugin2deps
anddep2parents
which respectively allow tracking deps per node (each layer) and parents per nodeEdge cases:
_host_deps
setThe text was updated successfully, but these errors were encountered: