ARC Welder plugin system Redisigned

Started by Ryex, December 17, 2013, 06:48:50 pm

Previous topic - Next topic

Ryex

December 17, 2013, 06:48:50 pm Last Edit: December 17, 2013, 06:56:42 pm by Ryex
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 Plan

ARC 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
    Code: ini

    [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
    Spoiler: ShowHide

    *

    matches any plugin

    some_name:*

    matches any plugin with the name "some_name"

    some_name:some_author:*

    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
    Code: python

    #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

    Code: python

    #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 no longer keep up with posts in the forum very well. If you have a question or comment, about my work, or in general I welcome PM's. if you make a post in one of my threads and I don't reply with in a day or two feel free to PM me and point it out to me.<br /><br />DropBox, the best free file syncing service there is.<br />

Blizzard

I think that this looks alright. It's simple and can easily be overviewed.
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.