I'll be honest my original plan (while it has undergone some revision) was just too clunky. too much boiler plate required to get things working.
I'm writing this down here mostly so It doesn't get lost and I can make sure I have a solid picture of what I'm doing.
The goal of modular development is to enable extensive code reuse and so that when your working on separate components only the interface matters, not the implantation. to that ends we can define a plugin as any piece of code that provide an interface for a specific functionality.
The PlanARC Welder is broken into 2 distinct sets of components
- The Core, provides a structure to extend the functionality and a place to start building from
- Plugins, Provide all real functionality
ideally there should be no difference (in interface, use, and load sequence) between a Plugin distributed with the main distribution of ARC and a plugin made by a 3ed party.
Until now I'v been treating a plugin the same way I treat a python module this is a problem when it comes load time because it get very hard to dynamically manage load sequence and dependencise.
so here is how I'm going to do things
The Structure
when Welder goes to load it will load it core and setup the name space. Then it will go searching for all plugins it can load. It will do this by searching a predefined set of paths for packages. A package is simply a folder with a "simple_plugin.welder_package" file inside. this file is a simple text ini configuration file with the following structure
[plugin]
name=simple_plugin
author=Ryex
version=0.0.1
files=plugin1.py,plugin2.py
[plugin1:consumes]
<component_name1>=<version_string>
<component_name3>=<version_string>
[plugin1:provides]
<component_name4>=<version_string>
<component_name5>=<version_string>
<component_name6>=<version_string>
[plugin2:consumes]
<component_name2>=<version_string>
[plugin2:provides]
<component_name7>=<namespace_name>;<version_string>
<component_name8>=<namespace_name>;<version_string>
<component_name9>=<namespace_name>;<version_string>
Component name
a component name is simply a string name of the component to be registered with the kernel
Version string
a version string can take any of the following forms
matches any plugin
matches any plugin with the name "some_name"
matches any plugin with the name "some_name" by "some_author"
some_name:some_author:1.x.x
matches any plugin with the name "some_name" by "some_author" specifying the major version 1 series
some_name:some_author:1.2.x
matches any plugin with the name "some_name" by "some_author" specifying the major version 1 and minor version 2 series
some_name:some_author:1.2.3
matches any plugin with the name "some_name" by "some_author" specifying the major version 1, minor version 2 and patch version 3
the version string when provided in the consumes section specifies what version of the component needs to be provided to the plugin's name space on load. if the version is unspecified with a "*" it will use the last loaded complaint version. a separate user configuration file can be used to prefer a specific version on load for these unspecified versions. if a version series is specified unseeing the "name:author:A.B.C" form then the highest version that meats the requirements will be used. again the user's configuration file can be uses to specify a version to use.
if the user's specify version is too low compared to the version requirements of the plugin it is ignored for that plugin.
when the version string is provided in the provides section it specifies what version of the component the plugin provides here all parts are optional and default to the package's values
Namespace name
the namespace names is the name by witch the components is accessed in the loaded file. for example if the following file was loaded
#plugin2.py
from . import some_local_module
global component7
component7 = some_local_module.class4
component_name7 would be accessed through "component7"
The Result
Welder will loop through and find all packages and index all the plugins then sort them based on their dependencies to get a load order.
Then it will loop through and load the files providing the components the file consumes by name through a namespace object provided as a global
thus a plugin that has dependencies would look something like this
#plugin2.py
global imports
from . import some_local_module
global component7, componet8
component7 = some_local_module.class4
class componet8(imports.component2):
def __init__(self):
# do stuf
pass
I think that this looks alright. It's simple and can easily be overviewed.