Budowanie minimalnej architektury wtyczek w Pythonie

Mam aplikację napisaną w Pythonie, która jest używana przez dość techniczną publiczność (naukowców).

Szukam dobrego sposobu na rozszerzenie aplikacji przez użytkowników, czyli architektury skryptów/wtyczek.

Szukam czegoś niezwykle lekkiego . Większość skryptów, lub wtyczek, nie będą opracowywane i dystrybuowane przez osoby trzecie i zainstalowane, ale będą coś bita przez użytkownika w ciągu kilku minut, aby zautomatyzować powtarzanie zadania, dodawanie obsługi formatu pliku itp. Tak więc wtyczki powinny mieć absolutne minimum kodu boilerplate i nie wymagają "instalacji" innej niż kopiowanie do folderu (więc coś takiego jak punkty wejścia setuptools lub Architektura wtyczek Zope wydaje się zbyt duża.)

Czy istnieją już takie systemy, lub jakieś projekty, które realizują podobny schemat, na który powinienem spojrzeć w poszukiwaniu pomysłów / inspiracji?

Author: dF., 2009-05-31

17 answers

Mój jest, w zasadzie, katalog o nazwie "plugins", które główna aplikacja może ankiety, a następnie używać imp.load_module aby pobierać pliki, poszukaj dobrze znanego punktu wejścia, prawdopodobnie z paramami konfiguracyjnymi na poziomie modułów, i idź stamtąd. Używam rzeczy do monitorowania plików dla pewnej dynamiki, w której wtyczki są aktywne, ale to miło mieć.

Oczywiście każdy wymóg, który pojawia się mówiąc "nie potrzebuję [dużej, skomplikowanej rzeczy] X; chcę tylko czegoś lekkiego", biegnie ryzyko ponownego wdrożenia x jednego odkrytego wymogu na raz. Ale to nie znaczy, że i tak nie można się bawić robiąc to:)

 140
Author: TJG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-13 08:26:49

module_example.py:

def plugin_main(*args, **kwargs):
    print args, kwargs

loader.py:

def load_plugin(name):
    mod = __import__("module_%s" % name)
    return mod

def call_plugin(name, *args, **kwargs):
    plugin = load_plugin(name)
    plugin.plugin_main(*args, **kwargs)

call_plugin("example", 1234)

Jest z pewnością "minimalny", nie ma absolutnie żadnego sprawdzania błędów, prawdopodobnie niezliczonych problemów z bezpieczeństwem, nie jest zbyt elastyczny - ale powinien pokazać, jak prosty może być system wtyczek w Pythonie..

Prawdopodobnie chcesz zajrzeć do Imp module zbyt, chociaż można zrobić wiele z tylko __import__, os.listdir i jakaś manipulacja sznurkiem.

 46
Author: dbr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-07 09:57:18

Spójrz na w tym przeglądzie istniejących frameworków / bibliotek wtyczek , jest to dobry punkt wyjścia. Bardzo lubię yapsy , ale to zależy od Twojego przypadku użycia.

 31
Author: PhilS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-31 13:54:12

Chociaż to pytanie jest naprawdę interesujące, myślę, że jest to dość trudne do odpowiedzi, Bez więcej szczegółów. Co to za aplikacja? Ma GUI? Czy to narzędzie wiersza poleceń? Zestaw scenariuszy? Program z unikalnym punktem wejścia itp...

Biorąc pod uwagę niewiele informacji, które posiadam, odpowiem w bardzo ogólny sposób.

Co oznacza dodawanie wtyczek?

  • prawdopodobnie będziesz musiał dodać plik konfiguracyjny, który wyświetli listę ścieżek / katalogów załadować.
  • innym sposobem byłoby powiedzieć "wszystkie pliki w tym katalogu plugin/ zostaną załadowane", ale ma to niewygodne wymaganie od użytkowników poruszania się po plikach.
  • ostatnią, pośrednią opcją byłoby wymaganie, aby wszystkie wtyczki znajdowały się w tym samym folderze/ wtyczce, a następnie aktywowanie/dezaktywowanie ich za pomocą ścieżek względnych w pliku konfiguracyjnym.

W praktyce czystego kodu / projektowania będziesz musiał jasno określić, jakie zachowania / konkretne działania chcesz rozszerzyć. Zidentyfikuj wspólny punkt wejścia / zestaw funkcji, które zawsze będą nadpisywane, i określ grupy w ramach tych działań. Gdy to zrobisz, powinno być łatwo rozszerzyć swoją aplikację,

Przykład użycia hooków , zainspirowany MediaWiki (PHP, ale czy język naprawdę ma znaczenie?):

import hooks

# In your core code, on key points, you allow user to run actions:
def compute(...):
    try:
        hooks.runHook(hooks.registered.beforeCompute)
    except hooks.hookException:
        print('Error while executing plugin')

    # [compute main code] ...

    try:
        hooks.runHook(hooks.registered.afterCompute)
    except hooks.hookException:
        print('Error while executing plugin')

# The idea is to insert possibilities for users to extend the behavior 
# where it matters.
# If you need to, pass context parameters to runHook. Remember that
# runHook can be defined as a runHook(*args, **kwargs) function, not
# requiring you to define a common interface for *all* hooks. Quite flexible :)

# --------------------

# And in the plugin code:
# [...] plugin magic
def doStuff():
    # ....
# and register the functionalities in hooks

# doStuff will be called at the end of each core.compute() call
hooks.registered.afterCompute.append(doStuff)
Kolejny przykład, zainspirowany mercurialem. W tym przypadku rozszerzenia dodają tylko polecenia do pliku wykonywalnego hg , rozszerzając zachowanie.
def doStuff(ui, repo, *args, **kwargs):
    # when called, a extension function always receives:
    # * an ui object (user interface, prints, warnings, etc)
    # * a repository object (main object from which most operations are doable)
    # * command-line arguments that were not used by the core program

    doMoreMagicStuff()
    obj = maybeCreateSomeObjects()

# each extension defines a commands dictionary in the main extension file
commands = { 'newcommand': doStuff }

Dla obu podejścia, możesz potrzebować common initializei finalize dla Twojego rozszerzenia. Możesz użyć wspólnego interfejsu, który wszystkie rozszerzenia będą musiały zaimplementować (pasuje lepiej z drugim podejściem; mercurial używa reposetup(ui, repo), który jest wywoływany dla wszystkich rozszerzeń), lub użyć podejścia typu hook, z hookami./ align = "left" /

Ale znowu, jeśli chcesz więcej przydatnych odpowiedzi, będziesz musiał zawęzić swoje pytanie;)

 23
Author: Nicolas Dumazet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-31 15:54:28

Marty Allchin ' s simple plugin framework jest podstawą używam dla własnych potrzeb. Naprawdę polecam, aby spojrzeć na to, myślę, że to naprawdę dobry początek, jeśli chcesz czegoś prostego i łatwo hakowalnego. Możesz go znaleźć również jako urywki Django .

 11
Author: edomaur,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-14 12:14:17

Jestem emerytowanym biologiem, który zajmował się cyfrowymi mikrografami i musiał napisać pakiet przetwarzania i analizy obrazu (nie jest to technicznie biblioteka), aby uruchomić na maszynie SGi. Napisałem kod w C i użyłem Tcl jako języka skryptowego. GUI, taki jak był, został wykonany przy użyciu Tk. Polecenia, które pojawiły się w Tcl miały postać " extensionName commandName arg0 arg1 ... param0 param1 ...", czyli proste słowa i liczby rozdzielone spacjami. Kiedy TCL zobaczył "extensionName", Kontrola została przekazana do pakietu C. To z kolei uruchomiło polecenie przez lexer / parser (zrobione w lex/yacc), a następnie wywołało procedury C w razie potrzeby.

Polecenia do obsługi pakietu mogą być uruchamiane pojedynczo przez okno w GUI, ale zadania wsadowe były wykonywane przez edycję plików tekstowych, które były poprawnymi skryptami Tcl; można wybrać szablon, który wykonuje operacje na poziomie plików, które chcesz wykonać, a następnie edytować kopię, aby zawierała rzeczywisty katalog i plik nazwy oraz polecenia pakietu. Zadziałało jak czar. Dopóki ...

1) świat zwrócił się do komputerów PC i 2) Skrypty stały się dłuższe niż około 500 linii, gdy niepewne możliwości organizacyjne TCL zaczęły stać się prawdziwą niedogodnością. Czas mijał ...

Przeszedłem na emeryturę, Python został wymyślony i wyglądał na idealnego następcę Tcl. Nigdy nie robiłem tego portu, ponieważ nigdy nie stawiałem czoła wyzwaniom związanym z kompilowaniem (dość dużych) programów C na PC, rozszerzaniem Pythona z pakietem C i robiąc Gui w Pythonie / Gt?/ Tk?/??. Jednak stary pomysł posiadania edytowalnych skryptów szablonów wydaje się nadal wykonalny. Ponadto nie powinno być zbyt dużym obciążeniem wprowadzanie poleceń pakietu w natywnej formie Pythona, np.:

Nazwa pakietu.polecenie (arg0, arg1, ..., param0, param1,..)

Kilka dodatkowych kropek, parenów i przecinków, ale to nie są showstoppery.

Pamiętam, że ktoś zrobił wersje Lexa i yacc w Pythonie (spróbuj: http://www.dabeaz.com/ply / ), więc jeśli są one nadal potrzebne, są w pobliżu.

Sens tego bełkotu jest taki, że wydawało mi się, że Python jest pożądanym "lekkim" frontem używanym przez naukowców. Jestem ciekaw, dlaczego uważasz, że tak nie jest, i mówię poważnie.


Dodano później: aplikacja gedit przewiduje dodawanie wtyczek, a ich strona ma o najczystsze Wyjaśnienie prostej procedury wtyczki znalazłem w kilka minut rozglądając się. Try:

Https://wiki.gnome.org/Apps/Gedit/PythonPluginHowToOld

Nadal chciałbym lepiej zrozumieć twoje pytanie. Nie jestem pewien, czy 1) chcesz, aby naukowcy mogli korzystać z aplikacji (Python) po prostu na różne sposoby lub 2) chcesz, aby naukowcy mogli dodać nowe możliwości do aplikacji. Wybór # 1 to sytuacja, w której mieliśmy do czynienia z obrazami i która doprowadziła nas do użycia generycznych skryptów, które zmodyfikowaliśmy do odpowiadaj potrzebom chwili. Czy jest to wybór # 2, który prowadzi do idei wtyczek, czy też jest to jakiś aspekt aplikacji, który sprawia, że wydawanie poleceń do niego niewykonalne?

 11
Author: behindthefall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-16 23:21:53

Kiedy szukałem dekoratorów Pythona, znalazłem prosty, ale przydatny fragment kodu. Może nie pasuje do Twoich potrzeb, ale jest bardzo inspirująca.

Scipy Advanced Python # Plugin Registration System

class TextProcessor(object):
    PLUGINS = []

    def process(self, text, plugins=()):
        if plugins is ():
            for plugin in self.PLUGINS:
                text = plugin().process(text)
        else:
            for plugin in plugins:
                text = plugin().process(text)
        return text

    @classmethod
    def plugin(cls, plugin):
        cls.PLUGINS.append(plugin)
        return plugin


@TextProcessor.plugin
class CleanMarkdownBolds(object):
    def process(self, text):
        return text.replace('**', '')

Użycie:

processor = TextProcessor()
processed = processor.process(text="**foo bar**", plugins=(CleanMarkdownBolds, ))
processed = processor.process(text="**foo bar**")
 10
Author: guneysus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-06 03:58:22

Cieszyłem się miłą dyskusją na temat różnych architektur wtyczek podaną przez Dr Andre Roberge na Pycon 2009. Daje dobry przegląd różnych sposobów wdrażania wtyczek, zaczynając od czegoś naprawdę prostego.

Jest dostępny jako podcast (Druga część po wyjaśnieniu łatania małp) wraz z serią sześciu wpisów na blogu.

Zalecam szybkie wysłuchanie go przed podjęciem decyzji.

 7
Author: Jon Mills,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-14 07:33:02

Przybyłem tutaj szukając minimalnej architektury wtyczek i znalazłem wiele rzeczy, które wydawały mi się przesadą. Zaimplementowałem więc Super proste wtyczki Pythona . Aby go użyć, tworzysz jeden lub więcej katalogów i upuszczasz specjalny plik __init__.py do każdego z nich. Importowanie tych katalogów spowoduje, że wszystkie inne pliki Pythona zostaną załadowane jako moduły podrzędne, a ich nazwy zostaną umieszczone na liście __all__. Następnie należy zweryfikować/zainicjować / zarejestrować te moduły. Jest przykład w pliku README.

 4
Author: samwyse,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-24 02:23:28

W rzeczywistości setuptools działa z "katalogiem wtyczek" , jak Poniższy przykład zaczerpnięty z dokumentacji projektu: http://peak.telecommunity.com/DevCenter/PkgResources#locating-plugins

Przykładowe użycie:

plugin_dirs = ['foo/plugins'] + sys.path
env = Environment(plugin_dirs)
distributions, errors = working_set.find_plugins(env)
map(working_set.add, distributions)  # add plugins+libs to sys.path
print("Couldn't load plugins due to: %s" % errors)

Na dłuższą metę, setuptools jest znacznie bezpieczniejszym wyborem, ponieważ może ładować wtyczki bez konfliktów lub brakujących wymagań.

Kolejną zaletą jest to, że same wtyczki mogą być rozszerzane za pomocą tego samego mechanizmu, bez oryginalne aplikacje muszą o to dbać.

 4
Author: ankostis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-21 16:00:48

Jako inne podejście do systemu wtyczek, możesz sprawdzić Extend Me project.

Na przykład zdefiniujmy klasę prostą i jej rozszerzenie

# Define base class for extensions (mount point)
class MyCoolClass(Extensible):
    my_attr_1 = 25
    def my_method1(self, arg1):
        print('Hello, %s' % arg1)

# Define extension, which implements some aditional logic
# or modifies existing logic of base class (MyCoolClass)
# Also any extension class maby be placed in any module You like,
# It just needs to be imported at start of app
class MyCoolClassExtension1(MyCoolClass):
    def my_method1(self, arg1):
        super(MyCoolClassExtension1, self).my_method1(arg1.upper())

    def my_method2(self, arg1):
        print("Good by, %s" % arg1)

I spróbuj go użyć:

>>> my_cool_obj = MyCoolClass()
>>> print(my_cool_obj.my_attr_1)
25
>>> my_cool_obj.my_method1('World')
Hello, WORLD
>>> my_cool_obj.my_method2('World')
Good by, World

I pokazać, co kryje się za sceną:

>>> my_cool_obj.__class__.__bases__
[MyCoolClassExtension1, MyCoolClass]

biblioteka extend_me manipuluje procesem tworzenia klasy za pomocą metaklas, tak więc w powyższym przykładzie, podczas tworzenia nowej instancji MyCoolClass otrzymujemy instancję nowej klasy, która jest podklasą zarówno MyCoolClassExtension, jak i MyCoolClass mając funkcjonalność obu z nich, dzięki dziedziczeniu wielokrotnemu

Dla lepszej kontroli nad tworzeniem klas istnieje kilka metaklas zdefiniowanych w tej lib:

  • ExtensibleType - pozwala na prostą rozszerzalność poprzez podklasowanie

  • ExtensibleByHashType - podobne do ExtensibleType, ale posiadają zdolność do budowania wyspecjalizowanych wersji klasy, pozwalających na globalne rozszerzenie klasy podstawowej i rozszerzenia specjalistycznych wersji Klasa

Ta lib jest używana w OpenERP Proxy Project i wydaje się działać wystarczająco dobrze!

Prawdziwy przykład użycia znajdziesz w rozszerzeniu OpenERP Proxy 'field_datetime' :

from ..orm.record import Record
import datetime

class RecordDateTime(Record):
    """ Provides auto conversion of datetime fields from
        string got from server to comparable datetime objects
    """

    def _get_field(self, ftype, name):
        res = super(RecordDateTime, self)._get_field(ftype, name)
        if res and ftype == 'date':
            return datetime.datetime.strptime(res, '%Y-%m-%d').date()
        elif res and ftype == 'datetime':
            return datetime.datetime.strptime(res, '%Y-%m-%d %H:%M:%S')
        return res

Record Oto extesible object. RecordDateTime jest rozszerzeniem.

Aby włączyć rozszerzenie, wystarczy zaimportować moduł zawierający klasę extension i (w przypadku powyżej) wszystkie obiekty Record utworzone po nim będą miały klasę extension w klasach bazowych, a tym samym wszystkie jego funkcjonalność.

Główną zaletą tej biblioteki jest to, że kod, który obsługuje obiekty rozszerzalne, nie musi wiedzieć o rozszerzeniach, A rozszerzenia mogą zmienić wszystko w obiektach rozszerzalnych.

 3
Author: FireMage,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-13 19:14:14

Rozszerzając odpowiedź @ edomaur mogę zasugerować przyjrzenie się simple_plugins (bezwstydna wtyczka), która jest prostym frameworkiem wtyczek inspirowanym pracą Marty ' ego Alchina.

Krótki przykład użycia oparty na README projektu:

# All plugin info
>>> BaseHttpResponse.plugins.keys()
['valid_ids', 'instances_sorted_by_id', 'id_to_class', 'instances',
 'classes', 'class_to_id', 'id_to_instance']

# Plugin info can be accessed using either dict...
>>> BaseHttpResponse.plugins['valid_ids']
set([304, 400, 404, 200, 301])

# ... or object notation
>>> BaseHttpResponse.plugins.valid_ids
set([304, 400, 404, 200, 301])

>>> BaseHttpResponse.plugins.classes
set([<class '__main__.NotFound'>, <class '__main__.OK'>,
     <class '__main__.NotModified'>, <class '__main__.BadRequest'>,
     <class '__main__.MovedPermanently'>])

>>> BaseHttpResponse.plugins.id_to_class[200]
<class '__main__.OK'>

>>> BaseHttpResponse.plugins.id_to_instance[200]
<OK: 200>

>>> BaseHttpResponse.plugins.instances_sorted_by_id
[<OK: 200>, <MovedPermanently: 301>, <NotModified: 304>, <BadRequest: 400>, <NotFound: 404>]

# Coerce the passed value into the right instance
>>> BaseHttpResponse.coerce(200)
<OK: 200>
 2
Author: Petar Marić,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-11 16:19:47

Setuptools ma EntryPoint :

Punkty wejścia to prosty sposób na "reklamowanie" Pythona obiekty (takie jak funkcje lub klasy) do wykorzystania przez inne dystrybucje. Rozszerzalne aplikacje i struktury mogą wyszukiwać punkty wejścia z określoną nazwą lub grupą, albo z określonej dystrybucji lub ze wszystkich aktywnych dystrybucji na sys.ścieżki, a następnie sprawdzić lub załadować reklamowane obiekty do woli.

AFAIK ten pakiet jest zawsze dostępne, jeśli używasz pip lub virtualenv.

 2
Author: guettli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-27 22:52:35

Spędziłem dużo czasu próbując znaleźć mały system wtyczek dla Pythona, który pasowałby do moich potrzeb. Ale wtedy po prostu pomyślałem, jeśli istnieje już dziedziczenie, które jest naturalne i elastyczne, dlaczego nie użyć go.

Jedynym problemem z używaniem dziedziczenia dla wtyczek jest to, że nie wiesz, jakie są najbardziej specyficzne(najniższe na drzewie dziedziczenia) klasy wtyczek.

Ale można to rozwiązać za pomocą metaclass, który śledzi dziedziczenie klasy bazowej i ewentualnie może Klasa build, która dziedziczy z większości konkretnych wtyczek ('root extended' na rysunku poniżej)

Tutaj wpisz opis obrazka

Więc przyszedłem z rozwiązaniem kodując taką metaklasę:

class PluginBaseMeta(type):
    def __new__(mcls, name, bases, namespace):
        cls = super(PluginBaseMeta, mcls).__new__(mcls, name, bases, namespace)
        if not hasattr(cls, '__pluginextensions__'):  # parent class
            cls.__pluginextensions__ = {cls}  # set reflects lowest plugins
            cls.__pluginroot__ = cls
            cls.__pluginiscachevalid__ = False
        else:  # subclass
            assert not set(namespace) & {'__pluginextensions__',
                                         '__pluginroot__'}     # only in parent
            exts = cls.__pluginextensions__
            exts.difference_update(set(bases))  # remove parents
            exts.add(cls)  # and add current
            cls.__pluginroot__.__pluginiscachevalid__ = False
        return cls

    @property
    def PluginExtended(cls):
        # After PluginExtended creation we'll have only 1 item in set
        # so this is used for caching, mainly not to create same PluginExtended
        if cls.__pluginroot__.__pluginiscachevalid__:
            return next(iter(cls.__pluginextensions__))  # only 1 item in set
        else:
            name = cls.__pluginroot__.__name__ + 'PluginExtended'
            extended = type(name, tuple(cls.__pluginextensions__), {})
            cls.__pluginroot__.__pluginiscachevalid__ = True
return extended

Więc kiedy masz bazę roota, wykonaną z metaclass i drzewo wtyczek, które dziedziczą z niego, możesz automatycznie uzyskać klasę, która dziedziczy od najbardziej konkretnych wtyczek, po prostu podklasując:

class RootExtended(RootBase.PluginExtended):
    ... your code here ...

Baza kodu jest dość mała (~30 linii czystego kodu) i tak elastyczna jak dziedziczenie pozwala.

Jeśli jesteś zainteresowany, zaangażuj się @ https://github.com/thodnev/pluginlib

 1
Author: thodnev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-04 14:04:45

Spędziłem czas czytając ten wątek, podczas gdy szukałem frameworku wtyczek w Pythonie teraz i wtedy. Mam używane niektóre, ale były braki z nimi. Oto, co wymyśliłem dla twojej kontroli w 2017, darmowy interfejs, luźno sprzężony system zarządzania wtyczkami: Load me later . Oto tutoriale Jak z niego korzystać.

 1
Author: chfw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-31 12:10:46

Możesz użyć pluginlib.

Wtyczki są łatwe do utworzenia i mogą być ładowane z innych pakietów, ścieżek plików lub punktów wejścia.

Utwórz klasę nadrzędną wtyczki, definiując wszelkie wymagane metody:

import pluginlib

@pluginlib.Parent('parser')
class Parser(object):

    @pluginlib.abstractmethod
    def parse(self, string):
        pass

Tworzenie pluginu poprzez dziedziczenie klasy nadrzędnej:

import json

class JSON(Parser):
    _alias_ = 'json'

    def parse(self, string):
        return json.loads(string)

Załaduj wtyczki:

loader = pluginlib.PluginLoader(modules=['sample_plugins'])
plugins = loader.plugins
parser = plugins.parser.json()
print(parser.parse('{"json": "test"}'))
 1
Author: aviso,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-17 15:00:30

Możesz również spojrzeć na Prace Ziemne .

Chodzi o budowanie aplikacji wokół komponentów wielokrotnego użytku, zwanych wzorcami i wtyczkami. Pluginy są klasami pochodzącymi z GwBasePattern. Oto podstawowy przykład:

from groundwork import App
from groundwork.patterns import GwBasePattern

class MyPlugin(GwBasePattern):
    def __init__(self, app, **kwargs):
        self.name = "My Plugin"
        super().__init__(app, **kwargs)

    def activate(self): 
        pass

    def deactivate(self):
        pass

my_app = App(plugins=[MyPlugin])       # register plugin
my_app.plugins.activate(["My Plugin"]) # activate it

Istnieją również bardziej zaawansowane wzorce do obsługi np. interfejsów wiersza poleceń, sygnalizacji lub obiektów współdzielonych.

Groundwork znajduje swoje wtyczki, programowo wiążąc je z aplikacją, Jak pokazano powyżej, lub automatycznie za pośrednictwem setuptools. Python pakiety zawierające wtyczki muszą zadeklarować je za pomocą specjalnego punktu wejścia groundwork.plugin.

Oto dokumenty .

Disclaimer: jestem jednym z autorów Groundwork.

 0
Author: ub_marco,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-25 18:38:18