Re: Re: Pyitect - Plugin architectural system for Python 3.0+ (feedback?)

2015-07-16 Thread ryexander
On Monday, June 22, 2015 at 1:18:08 PM UTC-6, Ian wrote:
> On Mon, Jun 22, 2015 at 2:32 AM, Ben Powers  wrote:
> > on Tue, Jun 16,  2015 at 17:49 Ian Kelly  wrote
> >
> >>On Mon, Jun 8, 2015 at 10:42 PM, Ben Powers  wrote:
> >>> #file.py
> >>> from PyitectConsumes import foo
> >>>
> >>> class Bar(object):
> >>> def __init__():
> >>> foo("it's a good day to be a plugin")>
> >
> >>I don't understand this example. Is "PyitectConsumes" the name of a
> >>component or part of Pyitect? Or is "foo" the name of the component?>
> >
> >
> > `PyitectComsumes` is a special module injected into sys.modules when the
> > plugin module is imported and removed after the import is complete. it's
> > name-space contains all the components declared as required in the plugin's
> > .json
> 
> So this import can only be performed when the module is loaded, not
> later when a component's functionality is being invoked?
> 
> >>> Plugins are loaded on demand when a component is loaded via
> >>>
> >>> System.load("")>
> >
> >>What's the difference between this and the "PyitectConsumes" import?>
> >
> >
> > The PyitectConsumes import is for use inside plugins where dependencies are
> > declared prior to import.
> >
> > System.load is for use in the application to load components. Plugins are
> > not intended to know about the System they are being used in.
> >
> >
> >>> Loaded pluginss do NOT store their module object in sys.modules>
> >
> >>What about imports of plugin-local modules that are performed inside
> >>the plugins? Do those also get cleaned up from sys.modules?>
> >
> >
> > Actually no... thats an oversight on my part. really I should temporally
> > clone sys.modules inject the PyitectConsumes special module and then set it
> > back to the original sys.modules dict after the import has been performed,
> > that would secure the process more and prevent cluttering of sys.modules.
> 
> It seems to me that this could create new problems. For example,
> suppose a plugin being loaded imports urllib, and then later the
> application tries to import urllib. It's not in sys.modules, so the
> import framework loads it again, and now there are two copies of
> urllib floating around in the interpreter. I think you actually need
> to diff sys.modules and then remove anything that's added but only if
> its path falls within the path of the plugin directory.
> 
> The more that I think about this, I don't think that overloading the
> import machinery like this is the right way for plugins to gain access
> to components. If instead you just pass the module a context object
> with a get_component method, then you won't have to muck around with
> sys.modules as much, and the context object can remain available to
> the plugin for later use.

well I've done some reworking of the system trying to take the points you've 
brought up into account. your probably right this would be best done without 
mucking with the import machinery. passing a module a context object as you say 
would be a much preferred method. unless were using direct code execution on a 
module object's namespace like so

filepath = os.path.join(self.path, self.file)
module_name = os.path.splitext(self.file)[0]
# exec mode requieres the file to be raw python
package = False
if module_name == '__init__':
module_name = os.path.basename(self.path)
package = True
try:
plugin = types.ModuleType(module_name)
if package:
plugin.__package__ = module_name
plugin.__path__ = [self.path]
sys.modules[module_name] = plugin
else:
plugin.__package__ = None
sys.path.insert(0, self.path)
with open(filepath) as f:
code = compile(f.read(), filepath, 'exec')
exec(code, plugin.__dict__)
sys.path.remove(self.path)
if package:
del sys.modules[module_name]

there is no way to make this context object available during the module load as 
importlib offer no functionality to provide a context during import.

and if the context object WAS provided this way by injecting it into the module 
object's __dict__ before the plugin code was executed it would exist as a magic 
object with no formal definition you just have to trust that it exists.

Also short of some convoluted declaration of globals in the plugin modules 
namespace, there is no way to pass in the plugin system so that the plugin can 
acquire components on demand short of the author of the plugin system author 
doing it themself by explicitly passing in an instance of the system.

Ultimately I think it best to inject a special Module into sys.modules during 
the import and require the plugin author to pull anything they might need 
during load from that. if they need to save it for later there is always

import PyitectConsumes as foobar

then use foobar.componentName there after

I think it far more pythonic to fail for lack of dependency during import of 
the plugin than during execution

If the plugin author needs to do things on demand then th

Pyitect V2 support for 2.6+ and big chagnes

2015-08-25 Thread ryexander
A while back I posted my plugin framework module asking for feedback

I only got one person to respond but that just fine, they gave me plenty to 
think on. I'm back to showcase my improvements.

https://github.com/Ryex/pyitect

The biggest change in V2 is that Python versions 2.6 and up are supported with 
no loss of functionality. for some reason I blanked the existence of imp I know 
about it but forgot for around a year, it's weird. Regardless, Python 3.3 and 
lower uses imp and python 3.4 and up uses importlib.

The second biggest change is that the magic module PyitectConsumes is no longer 
used for import time component loading. one of the feedback's was that this was 
just too much magic going on with the import machinery.
Instead, pyitect has a empty sub-module pyitect.imports . this module is 
populated with components during import of a plugin module so insted of

from PyitectConsumes import foo

you use

from pyitect.imports import foo

the difference may seem semantic but the difference is that where as 
PyitectConsumes just magically existed during import pyitect.imports always 
exists but is empty unless a plugin module is getting imported


In pyitect that is a difference between import-time and run-time component 
fetching. for former makes components declared in the configuration file of the 
plugin available while the plugin's module is still being loaded. The latter is 
invoked on a plugin system instance via it's load method. Plugins MAY need to 
use run-time component acquisition in V1 this was possible but author of the 
system needed to make the plugin system instance available to the author. V2 
makes this much easier by letting the instance hangout in the pyitect module 
build_system, get_system, and destroy_system all work with a global instance of 
the pyitect.System class.

in V1 version postfixes was a system to provide different types of the same 
component. their use however was a bit arcane. In V2 the functionality of 
post-fixes is instead accomplished with component subtypes.

if a component is names with a doted notations ie. "compa.sub1.sub2"
each part represents a subtype of the part before it. so a "a.b.c" is also a 
"a.b" and an "a" type component. if a component type "a" is requested a "a.b" 
may be returned if an "a" is not available.

all this behavior can of course be modified. 

I've also written up sphinx docs this time 
http://pyitect.readthedocs.org/en/latest/

I would like more feedback if anyone has the time avalible to look at it.

Thanks ~
Benjamin "Ryex" Powers
-- 
https://mail.python.org/mailman/listinfo/python-list