Cast into custom type

H

Henning Bredel

Hi,

I created a plugin mechanism for my application orientating
at the mechanism described by Martin Alchy in

http://martyalchin.com/2008/jan/10/simple-plugin-framework/

Now I'd like to call methods like `initialize(parent)' when
the user chooses to use a plugin. As described in the blog
mentioned above, I only have access to the general type called
`PluginMount' (holding all the actual plugin instances).

I tried to define "abstract" methods in PluginMount type
raising a `NotImplementedError' but it seems, there is no
late binding (similar to Java), so the right method would be
called. Only the message

TypeError: unbound method initialize() must be called
with GeoCache instance as first argument (got PluginMount
instance instead)

`GeoCache' would be the plugin type. What is strange, is the
fact, that when asking what instances are hold by PluginMount

[<class 'geocacheplugin.GeoCache'>]

is listed. So it seems, that no late binding is applied when
calling the `initialize(self, parent)' method.

I'm quite new using Python, so this might be a quite basic
question caused by some misunderstandings in general. Feel
free to point me to appropriate site to solve my problem.

Thanks in advance.

Henning
 
D

Diez B. Roggisch

Henning said:
Hi,

I created a plugin mechanism for my application orientating
at the mechanism described by Martin Alchy in

http://martyalchin.com/2008/jan/10/simple-plugin-framework/

Now I'd like to call methods like `initialize(parent)' when
the user chooses to use a plugin. As described in the blog
mentioned above, I only have access to the general type called
`PluginMount' (holding all the actual plugin instances).

I tried to define "abstract" methods in PluginMount type
raising a `NotImplementedError' but it seems, there is no
late binding (similar to Java), so the right method would be
called. Only the message

TypeError: unbound method initialize() must be called
with GeoCache instance as first argument (got PluginMount
instance instead)

`GeoCache' would be the plugin type. What is strange, is the
fact, that when asking what instances are hold by PluginMount

[<class 'geocacheplugin.GeoCache'>]

is listed. So it seems, that no late binding is applied when
calling the `initialize(self, parent)' method.

I'm quite new using Python, so this might be a quite basic
question caused by some misunderstandings in general. Feel
free to point me to appropriate site to solve my problem.

It seems that GeoCache is not a subclass of PluginMount.

Diez
 
G

Gabriel Genellina

I created a plugin mechanism for my application orientating
at the mechanism described by Martin Alchy in

http://martyalchin.com/2008/jan/10/simple-plugin-framework/

Now I'd like to call methods like `initialize(parent)' when
the user chooses to use a plugin. As described in the blog
mentioned above, I only have access to the general type called
`PluginMount' (holding all the actual plugin instances).

According to the article, PluginMount holds a list of all plugin *classes*
defined, not instances.
I tried to define "abstract" methods in PluginMount type
raising a `NotImplementedError' but it seems, there is no
late binding (similar to Java), so the right method would be
called. Only the message

TypeError: unbound method initialize() must be called
with GeoCache instance as first argument (got PluginMount
instance instead)

Python uses "late binding", even more than Java. A reference like
obj.foo(...) searches for "foo" along obj's attributes at runtime.
Please post some code showing your issue, and don't forget to tell us what
is your expected result, and what you actually get.
 
S

Steven D'Aprano

Hi,

I created a plugin mechanism for my application orientating at the
mechanism described by Martin Alchy in

http://martyalchin.com/2008/jan/10/simple-plugin-framework/

Regarding your subject line, Python doesn't have casts. A very small
number of numeric types are automatically coerced as needed, for
everything else you need an explicit conversion between objects of one
type to different objects of another type.

Now I'd like to call methods like `initialize(parent)' when the user
chooses to use a plugin. As described in the blog mentioned above, I
only have access to the general type called `PluginMount' (holding all
the actual plugin instances).

I tried to define "abstract" methods in PluginMount type raising a
`NotImplementedError' but it seems, there is no late binding (similar to
Java), so the right method would be called.

You need to give some actual examples of what you are trying to do, and
what you are expecting to happen. How is initialized() being called?

Only the message

TypeError: unbound method initialize() must be called with GeoCache
instance as first argument (got PluginMount instance instead)

Sounds like you are calling initialize on the class instead of on an
instance. I'm *guessing* that you are doing something like this:

plugin_manager = PluginMount()
# ...
# create plugins, which are registered in the plugin_manager
# ...
# Now initialize them:
for cls in plugin_manager.plugins:
cls.initialize(plugin_Manager)


If my guess is correct, you can fix this two ways:

(1) Change the for-loop to:

for cls in plugin_manager.plugins:
cls().initialize(plugin_Manager)

although it isn't clear to me what you should do with the plugin
instances after you've created them.

(2) Change the initialize method to a classmethod:

@classmethod
def initialize(cls, parent):
pass


`GeoCache' would be the plugin type. What is strange, is the fact, that
when asking what instances are hold by PluginMount

[<class 'geocacheplugin.GeoCache'>]

is listed. So it seems, that no late binding is applied when calling the
`initialize(self, parent)' method.

What do you mean by late binding, and what makes you think it applies (or
should apply) to calling the initialize method?

I'm quite new using Python, so this might be a quite basic question
caused by some misunderstandings in general. Feel free to point me to
appropriate site to solve my problem.

We have to understand your problem first.
 
H

Henning Bredel

Diez, Gabriel, Steven,

thanks for your answers. I'll mainly give response in this posting, so
I hope not repeating myself.

You need to give some actual examples of what you are trying to do, and
what you are expecting to happen. How is initialized() being called?

Example: Assume a framework which offers common functionality for a plugin
or a module a user can choose at the beginning. The framework does not
know the concrete type of the plugin so it is possible to extend it by
implementing a well known interface or abstract class.

The framework reads the plugin directory, loads each module and creates
buttons for each plugin with a callback method for initializing. To use
common functionality of the framework, initialization method takes it as
the parent parameter.

I think this listing makes the most sense to you:

# initialize all plugins
self._plugin_modules = _load_plugins() # imp loading here
LOGGER.debug(ActionProvider.plugins) # print what was loaded
for plugin in ActionProvider.plugins: # create button for each
app_button = gtk.Button(plugin.title)
LOGGER.debug('Title of plugin: %s' % plugin.title)
app_button.connect("clicked",
plugin.initialize(plugin, self),
None)
self.set_canvas(app_button)
app_button.show()
Sounds like you are calling initialize on the class instead of on an
instance. I'm *guessing* that you are doing something like this:

Oh, I didn't expext PluginMount to hold only classes :/.

When calling `LOGGER.debug('Title of plugin: %s' % plugin.title)' the
global (not class attribute) `title' attribute is printed out though.
Probably some misinterpretation, too ..?!
(1) Change the for-loop to:

for cls in plugin_manager.plugins:
cls().initialize(plugin_Manager)

Good guess .. but calling it in that way another error says

app_button.connect("clicked", plugin().initialize(self), None)
TypeError: __init__() takes exactly 2 arguments (1 given)

But also

app_button.connect("clicked", plugin().initialize(plugin, self), None)
TypeError: __init__() takes exactly 2 arguments (1 given)

That is strange, because neither initialize() nor __init__ of the
plugin is called .. only a logging statement shall print a msg.

I hope it got a bit clearer what I am intended to do.

Thanks for your help.

Henning
 
G

Gabriel Genellina

Example: Assume a framework which offers common functionality for a
plugin
or a module a user can choose at the beginning. The framework does not
know the concrete type of the plugin so it is possible to extend it by
implementing a well known interface or abstract class.

The framework reads the plugin directory, loads each module and creates
buttons for each plugin with a callback method for initializing. To use
common functionality of the framework, initialization method takes it as
the parent parameter.

Then forget about the code you read in that blog post, doesn't apply to
your use case.
I think this listing makes the most sense to you:

# initialize all plugins
self._plugin_modules = _load_plugins() # imp loading here
LOGGER.debug(ActionProvider.plugins) # print what was loaded
for plugin in ActionProvider.plugins: # create button for each
app_button = gtk.Button(plugin.title)
LOGGER.debug('Title of plugin: %s' % plugin.title)
app_button.connect("clicked",
plugin.initialize(plugin, self),
None)
self.set_canvas(app_button)
app_button.show()

Try something like this:

--- begin plugin.py ---
class Plugin(object):
"Every plugin class should have a docstring"
def __init__(self, manager):
pass
--- end plugin.py ---

--- begin pluginmgr.py --
import os.path
from glob import glob
from plugin import Plugin

class PluginManager:
def __init__(self, plugin_directory):
self.plugin_directory = plugin_directory
self.plugins = []

def load_all(self):
for fn in glob(os.path.join(self.plugin_directory, '*.py')):
namespace = {}
execfile(fn, namespace)
for name, obj in namespace.items():
if (isinstance(obj, type) and
issubclass(obj, Plugin) and
obj is not Plugin):
# obj is a Plugin subclass
cls = obj
print cls.__name__, fn
print cls.__doc__
print
plugin = cls(self) # call the constructor
self.plugins.append(plugin)

if __name__ == '__main__':
mgr = PluginManager(r'd:\\temp\\plugins')
mgr.load_all()
print mgr.plugins
--- end pluginmgr.py ---

--- begin one.py in the plugins directory ---
from plugin import Plugin

class MyPlugin(Plugin):
"""The most wonderful plugin in the world.
This plugin does this, and that,
and also that, too.
It's very nice, I assure you."""

class Second(Plugin):
"""My second plugin. I don't know what
is this for, but it works."""
--- end one.py ---

Plugin is the base class; all plugins must inherit from it. PluginMgr
scans the plugin directory, executes all modules it finds there (be
careful...), and looks for Plugin subclasses. Then creates an instance of
each Plugin subclass.
 
D

Dennis Lee Bieber

app_button.connect("clicked", plugin().initialize(plugin, self), None)
TypeError: __init__() takes exactly 2 arguments (1 given)

That is strange, because neither initialize() nor __init__ of the
plugin is called .. only a logging statement shall print a msg.
"plugin()" creates an anonymous instance, which means __init__() is
called... THEN initialize(,) would be called on that instance.
 
H

Henning Bredel

Gabriel,

thanks for your reply. See my comments below.

En Tue, 03 Nov 2009 09:07:01 -0300, Henning Bredel
Then forget about the code you read in that blog post, doesn't apply to
your use case.

Well, but it shows how to mount plugins into the application without
creating instances instantly. I only use one plugin/module at a time,
so I'd like to avoid holding instances which aren't used.

If the solution from the blogpost are meant completely different,
I'd appreciate if you could point me on some concrete arguments why I
shouldn't realize plugin mechanism in that way.

BTW: I made it work now (was only a(nother) misinterpretation of how
a callable should look like.
Try something like this:
[...]

class PluginManager:
def __init__(self, plugin_directory):
self.plugin_directory = plugin_directory self.plugins = []

def load_all(self):
for fn in glob(os.path.join(self.plugin_directory, '*.py')):
namespace = {}
execfile(fn, namespace)
for name, obj in namespace.items():
if (isinstance(obj, type) and
issubclass(obj, Plugin) and
obj is not Plugin):
# obj is a Plugin subclass
cls = obj
print cls.__name__, fn
print cls.__doc__
print
plugin = cls(self) # call the constructor
self.plugins.append(plugin)

[...]

Yes, I get your point. But you instantiate all available plugins. I'd like
to avoid that. Will a callable like `plugin().initialize' avoid
that, or is an instance created immediately when passing this callable?
Plugin is the base class; all plugins must inherit from it. PluginMgr
scans the plugin directory, executes all modules it finds there (be
careful...), and looks for Plugin subclasses. Then creates an instance
of each Plugin subclass.

Well, as far I understand it, I'd say that the ActionProvider class from
the blogpost is the (nearly) the same as your Plugin base class. All new
plugins has to implement the abstract ActionProvider class. The module
loading/recognizing is done by my main application. So what would be the
main difference here?

Thanks for your advice

Henning
 
G

Gabriel Genellina

Well, but it shows how to mount plugins into the application without
creating instances instantly. I only use one plugin/module at a time,
so I'd like to avoid holding instances which aren't used.

Ah, I see. Well, but you don't have to do anything special to collect
plugin subclasses
-- somebaseclass.__subclasses__() returns a list with all direct
subclasses, without requiring a custom metaclass. This recipe [1] shows
how to recursively collect all of them.
(Perhaps some people prefer the metaclass approach, but not me)
Yes, I get your point. But you instantiate all available plugins. I'd
like
to avoid that. Will a callable like `plugin().initialize' avoid
that, or is an instance created immediately when passing this callable?

plugin().initialize() creates a plugin instance, calls its initialize
method, and then discards the instance.
You probably want to create a plugin instance, store it somewhere as the
"current plugin", call its initialize method, and use the "current plugin"
to do some work.
(Or, are the missing () after plugin().initialize intentional? Yes, this
is a bound method, and it knows the plugin instance from which you got it.
The instance is created at the time you ask for the bound method. You may
call it later at any time. I'd probably use a bound method like that only
if "initialize" is the only method you ever call on the plugin instance.
In other cases, I'd store a plugin instance somewhere as described above.
Well, as far I understand it, I'd say that the ActionProvider class from
the blogpost is the (nearly) the same as your Plugin base class. All new
plugins has to implement the abstract ActionProvider class. The module
loading/recognizing is done by my main application. So what would be the
main difference here?

Instead of relying on a custom metaclass, I excplicitely scan all module
objects, testing for subclasses. That's all my code does, no rocket
science involved :)

[1] http://code.activestate.com/recipes/576949/
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top