Cast into custom type

Discussion in 'Python' started by Henning Bredel, Nov 3, 2009.

  1. 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
     
    Henning Bredel, Nov 3, 2009
    #1
    1. Advertising

  2. Henning Bredel schrieb:
    > 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
     
    Diez B. Roggisch, Nov 3, 2009
    #2
    1. Advertising

  3. En Tue, 03 Nov 2009 06:41:37 -0300, Henning Bredel <>
    escribió:

    > 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.

    --
    Gabriel Genellina
     
    Gabriel Genellina, Nov 3, 2009
    #3
  4. On Tue, 03 Nov 2009 09:41:37 +0000, Henning Bredel wrote:

    > 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.



    --
    Steven
     
    Steven D'Aprano, Nov 3, 2009
    #4
  5. Diez, Gabriel, Steven,

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

    On Tue, 03 Nov 2009 10:18:29 +0000, Steven D'Aprano wrote:

    > On Tue, 03 Nov 2009 09:41:37 +0000, Henning Bredel wrote:
    >
    >> 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?


    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()

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


    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
     
    Henning Bredel, Nov 3, 2009
    #5
  6. En Tue, 03 Nov 2009 09:07:01 -0300, Henning Bredel <>
    escribió:
    > On Tue, 03 Nov 2009 10:18:29 +0000, Steven D'Aprano wrote:


    >> 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.


    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.

    --
    Gabriel Genellina
     
    Gabriel Genellina, Nov 4, 2009
    #6
  7. On 03 Nov 2009 12:07:01 GMT, Henning Bredel <>
    declaimed the following in gmane.comp.python.general:


    > 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.
    --
    Wulfraed Dennis Lee Bieber KD6MOG
    HTTP://wlfraed.home.netcom.com/
     
    Dennis Lee Bieber, Nov 4, 2009
    #7
  8. Gabriel,

    thanks for your reply. See my comments below.

    On Tue, 03 Nov 2009 21:31:27 -0300, Gabriel Genellina wrote:

    > En Tue, 03 Nov 2009 09:07:01 -0300, Henning Bredel
    > <> escribió:
    >> On Tue, 03 Nov 2009 10:18:29 +0000, Steven D'Aprano wrote:

    >
    > 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
     
    Henning Bredel, Nov 4, 2009
    #8
  9. En Wed, 04 Nov 2009 10:52:51 -0300, Henning Bredel <>
    escribió:

    > On Tue, 03 Nov 2009 21:31:27 -0300, Gabriel Genellina wrote:
    >>
    >> 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.


    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/

    --
    Gabriel Genellina
     
    Gabriel Genellina, Nov 5, 2009
    #9
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Salim Afþar
    Replies:
    1
    Views:
    1,707
  2. Elmo Watson
    Replies:
    3
    Views:
    6,071
    Scott M.
    Dec 25, 2003
  3. Mike Newton
    Replies:
    0
    Views:
    2,702
    Mike Newton
    Jul 27, 2004
  4. tshad
    Replies:
    6
    Views:
    3,488
    tshad
    Dec 15, 2004
  5. Imran Aziz
    Replies:
    4
    Views:
    8,795
    ljenner01
    Jan 19, 2011
Loading...

Share This Page