pyEnv - Pluginbasierte Python Shell (Code, Stil, Umsetzung?)

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Crazed
User
Beiträge: 171
Registriert: Sonntag 18. Mai 2008, 11:44

pyEnv - Pluginbasierte Python Shell (Code, Stil, Umsetzung?)

Beitragvon Crazed » Montag 25. August 2008, 18:17

Hey, Ich habe mal wieder nen bisschen rumgebastelt. Ich habe ein relatives simples Plugin System zustande gebracht:

Code: Alles auswählen

#!/usr/bin/python

import types
import os

class pyEnv(object):
    def __init__(self):
        self.commands = {}
        for plugin in self.plugins:
            plugin = __import__(os.path.join('plugins', plugin))
            for key, value in plugin.__dict__.iteritems():
                if type(value) == types.FunctionType:
                    self.register_command(key, value)
               
    def register_command(self, command, callback):
        if not self.commands.has_key(command):
            self.commands[command] = callback
           
    def parse(self, string):
        tokens = string.split()
        command = tokens[0].lower()
       
        if self.commands.has_key(command):
            self.commands[command](tokens[1:])
           
    @property
    def plugins(self):
        plugins = []
        for file in os.listdir(os.path.join('plugins')):
            if not file.startswith('__') and file.endswith('.py'):
                plugins.append(file.split('.py')[0])
       
        return plugins
   
pyenv = pyEnv()

while True:
    command = str(raw_input('>>> '))
   
    if command:
        pyenv.parse(command)


Ich wollte mal wissen was ihr vom Code und er Umsetzung haltet.

Um selber Plugins zu schreiben einfach einen Unterordner "plugins" erstellen, module erstellen und dort funktionen definieren. Die funktionen kann man nachher über ihre Namen in der Konsole aufrufen.

Code: Alles auswählen

def say(args):
    print ' '.join(args)

...

>>> say Hallo wie gehts?
Hallo wie gehts?
>>>




MfG,
CracKPod
Zuletzt geändert von Crazed am Mittwoch 27. August 2008, 16:34, insgesamt 1-mal geändert.
Mad-Marty
User
Beiträge: 317
Registriert: Mittwoch 18. Januar 2006, 19:46

Beitragvon Mad-Marty » Dienstag 26. August 2008, 09:10

Naja von der Idee her funktionierts sicher.

Für das Verwendungsziel im Beispiel ist es auch gut geeignet (interaktive shell).

Aber für ein produktiv verwendbares system taugt es meiner meinung nach nicht. Ich bevorzuge OO Plugins die entweder per klassenname oder per module-funktion (z.b. init_plugin) gestartet werden.

Du registrierst einfach jede Funktion eines Moduls als plugin ...

Zusätzlich musst du dir noch gedanken über Starten und Stoppen von Plugins, die Threadbasiert sein sollten, machen.
Benutzeravatar
Leonidas
Administrator
Beiträge: 16023
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Beitragvon Leonidas » Dienstag 26. August 2008, 09:32

setuptools Entrypoints nutzen?
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Crazed
User
Beiträge: 171
Registriert: Sonntag 18. Mai 2008, 11:44

Beitragvon Crazed » Dienstag 26. August 2008, 13:44

Mad-Marty hat geschrieben:Naja von der Idee her funktionierts sicher.

Für das Verwendungsziel im Beispiel ist es auch gut geeignet (interaktive shell).

Aber für ein produktiv verwendbares system taugt es meiner meinung nach nicht. Ich bevorzuge OO Plugins die entweder per klassenname oder per module-funktion (z.b. init_plugin) gestartet werden.

Du registrierst einfach jede Funktion eines Moduls als plugin ...

Zusätzlich musst du dir noch gedanken über Starten und Stoppen von Plugins, die Threadbasiert sein sollten, machen.


Könntest du mir eventuell nen kleines Beispiel geben bezüglich "per klassenname oder per module-funktion"?

MfG,
CracKPod
Mad-Marty
User
Beiträge: 317
Registriert: Mittwoch 18. Januar 2006, 19:46

Beitragvon Mad-Marty » Mittwoch 27. August 2008, 09:45

im prinzip ganz einfach.

Zuerst musst du dir deine Regeln für das plugin system ausdenken.
Tue das bitte VOR dem schreiben von irgendwelchem code (zumindest grob).

z.B. das jedes plugin-file eine init_plugin() funktion und eine stop_plugin() haben muss die für starten & stoppen zuständig ist.

Oder du legst fest das jedes plugin file eine PluginHandler klasse hat die das plugin als Einsprungspunkt nutzt.

Oder das jedes Plugin file eine Klasse mit selben namen haben muss und eine .start() methode.

etc etc

Du siehst, man kann die Einsprungsmethodik praktisch völlig frei machen.
Für den Anfang wäre glaube ich die methode mit init und stop funktion auf modulebene ganz gut zum üben.

Das Klassendesign erfordert dann doch etwas mehr ehrfahrung und ist für den ersten versuch nicht so gut geignet denke ich.

Auserdem empfehle ich später das durch Klassenvererbung oder Metaklassen zu verbessern.

Code: Alles auswählen

class InvalidPlugin(Exception):
    pass

mymodule = __import__(...)
init_func = getattr(mymodule, "init_plugin", None)
if init_func is None:
    raise InvalidPlugin("Plugin has no init_plugin function")
else:
    init_func()
Crazed
User
Beiträge: 171
Registriert: Sonntag 18. Mai 2008, 11:44

Beitragvon Crazed » Mittwoch 27. August 2008, 16:29

Oke,
Das interessiert mich jetzt. Ich will vor allen Dingen meine Shell verbessern, mein Plugin ebenso und einen schönen und sauberen Code haben.

Also nochmal, und ich stelle diese Fragen nicht nur an dich, sondern an Alle die hier mal zufällig drüberfliegen.

Also:

Ich halte das Klassenbasierte Prinzip für eine sehr gute Idee wenn ich ehrlich bin.
Mit "Klassenvererbung oder Metaklassen" denke ich meinst du sowas hier:

Code: Alles auswählen

class MyPlugin(Plugin)
, oder?

Ich würde mir dann sowas denken:

Code: Alles auswählen

class Plugin(object):
    def __plugin_init__(self):
        return self

class MyPlugin(Plugin):
    def __init__(self):
        Plugin.__plugin_init__(self)

    def methode(self, args, pyenv):
        print ' '.join(args)
        for key, value in pyenv.commands.iteritems():
            print '%s: %s' % (key, value)

EDIT: Achtung: Code ungetestet!

Und pyEnv (Die shell) greift dann nachher auf das __dict__ attribut der Objektinstanz die beim Aufruf von __plugin_init__ zurückgegeben worden ist auf deren Methoden zu. Eine Referenz der Instanz der Klasse hat man ja dann auch direkt -> Das heißt man brauch das Plugin nachher noch nicht mal mehr zu instanzieren.

Was haltet ihr davon?

MfG,
CracKPod

PS:
Falls euch bessere Bezeichner einfallen und eine gute Idee um die Plugins und deren Outputs irgendwie in einen Thread laufen zu lassen, sagt mir bitte bescheid!

Wer ist online?

Mitglieder in diesem Forum: Bing [Bot]