Python'da minimal bir eklenti mimarisi oluşturma


190

Oldukça teknik bir izleyici (bilim adamları) tarafından kullanılan Python'da yazılmış bir uygulamam var.

Ben bir uygulama / yani bir betik / eklenti mimarisi, kullanıcılar tarafından genişletilebilir yapmak için iyi bir yol arıyorum.

Son derece hafif bir şey arıyorum . Çoğu komut dosyası veya eklenti, üçüncü taraflarca geliştirilip dağıtılmayacak ve yüklenmeyecek, ancak yinelenen bir görevi otomatikleştirmek, dosya formatı için destek eklemek için birkaç dakika içinde bir kullanıcı tarafından çırpılmış bir şey olacak, Bu yüzden eklentiler mutlak minimum kazan plakası koduna sahip olmalı ve bir klasöre kopyalamaktan başka 'kurulum' gerektirmemelidir (bu nedenle setuptools giriş noktaları veya Zope eklenti mimarisi gibi bir şey çok fazla gibi görünür.)

Dışarıda bunun gibi herhangi bir sistem veya fikirler / ilham için bakmam gereken benzer bir plan uygulayan projeler var mı?

Yanıtlar:


150

Mine, temel olarak, ana uygulamanın yoklayabileceği ve daha sonra dosyaları almak, muhtemelen modül düzeyinde yapılandırma parametreleriyle iyi bilinen bir giriş noktası aramak ve oradan gitmek için kullanabileceği "eklentiler" adı verilen bir dizindir . Eklentilerin etkin olduğu belirli bir dinamizm için dosya izleme öğelerini kullanıyorum, ancak bu güzel bir şey.

Tabii ki, "[büyük, karmaşık bir şeye ihtiyacım yok] X; Ben sadece hafif bir şey istiyorum" diyerek ortaya çıkan herhangi bir gereksinim, keşfedilen bir gereksinimi X'i yeniden uygulama riskiyle karşı karşıyadır. Ama bu yine de bunu yapmaktan zevk alamayacağınız anlamına gelmez :)


26
Çok teşekkürler! Gönderinize
MiJyn

9
impModül lehine kaldırılmasına karar verildi importlibpiton 3.4 başlayarak
b0fh

1
Birçok kullanım durumunda importlib.import_module yerine kullanılabilir imp.load_module.
Chris Arndt

58

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)

Kesinlikle "minimal", kesinlikle hiçbir hata kontrolü, muhtemelen sayısız güvenlik sorunları var, çok esnek değil - ama Python bir eklenti sisteminin ne kadar basit olabileceğini göstermelidir ..

Muhtemelen içine bakmak istiyorum imp sadece bir çok şey yapabilir rağmen, çok modül __import__, os.listdirve bazı dize kullanımı.


4
Bence değiştirmek def call_plugin(name, *args)isteyebilirsin def call_plugin(name, *args, **kwargs), ve sonraplugin.plugin_main(*args) içinplugin.plugin_main(*args, **kwargs)
Ron Klein

12
Python 3'te, implehineimportlib
Adam Baxter


25

Bu soru gerçekten ilginç olsa da, daha fazla ayrıntı olmadan cevaplamak oldukça zor. Bu ne tür bir uygulama? GUI'si var mı? Bir komut satırı aracı mı? Bir dizi senaryo mu? Eşsiz bir giriş noktasına sahip bir program vb.

Sahip olduğum küçük bilgiler göz önüne alındığında, çok genel bir şekilde cevap vereceğim.

Eklenti eklemenin anlamı nedir?

  • Muhtemelen yüklenecek yolları / dizinleri listeleyecek bir yapılandırma dosyası eklemeniz gerekecektir.
  • Başka bir yol, "bu eklenti / dizindeki herhangi bir dosya yüklenecek" demek olabilir, ancak kullanıcılarınızın dosyalar arasında hareket etmesini zorunlu kılar.
  • Son bir ara seçenek, tüm eklentilerin aynı eklenti / klasörde olmasını ve ardından bir yapılandırma dosyasındaki göreli yolları kullanarak bunları etkinleştirmeyi / devre dışı bırakmayı gerektirir.

Saf bir kod / tasarım pratiğinde, kullanıcılarınızın hangi davranış / özel eylemleri genişletmesini istediğinizi açıkça belirlemeniz gerekir. Her zaman geçersiz kılınacak ortak giriş noktasını / bir dizi işlevi belirleyin ve bu eylemler içindeki grupları belirleyin. Bu yapıldıktan sonra, uygulamanızı genişletmek kolay olmalı,

MediaWiki'den esinlenilen kancaları kullanan örnek (PHP, ancak dil gerçekten önemli mi?):

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)

Mercurial esinlenerek bir başka örnek. Burada, uzantılar yalnızca hg komut satırı yürütülebilir dosyasına komutlar ekleyerek davranışı genişletir.

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 }

Her iki yaklaşım için de , uzantınız için ortak başlatma ve sonlandırmaya ihtiyacınız olabilir . Tüm uzantınızın uygulamak zorunda olduğu ortak bir arabirim kullanabilirsiniz (ikinci yaklaşımla daha iyi uyuyor; mercurial, tüm uzantılar için çağrılan bir reposetup (ui, repo) kullanır veya bir kancalar. kurulum kanca.

Ama yine de, daha yararlı cevaplar istiyorsanız, sorunuzu daraltmanız gerekir;)


11

Marty Allchin'in basit eklenti çerçevesi kendi ihtiyaçlarım için kullandığım temeldir . Gerçekten bir göz atmanızı tavsiye ederim, basit ve kolayca hacklenebilir bir şey istiyorsanız gerçekten iyi bir başlangıç ​​olduğunu düşünüyorum. Bunu bir Django Parçacıkları olarak da bulabilirsiniz .


Temel olarak pyduck ile böyle bir şey yapmaya çalışıyorum.
edomaur

Anlatabildiğim kadarıyla Django'ya özgü.
Zoran Pavlovic

3
@ZoranPavlovic: hiç de değil, bazı standart Python satırları, Django kullanmak zorunda değilsiniz.
edomaur

11

Ben dijital mikro-gravürlerle uğraşan ve bir SGi makinesinde çalıştırmak için bir görüntü işleme ve analiz paketi (teknik olarak bir kütüphane değil) yazmak zorunda kalan emekli bir biyologum. Kodu C dilinde yazdım ve kodlama dili için Tcl kullandım. GUI, olduğu gibi, Tk kullanılarak yapıldı. Tcl'de görünen komutlar "extensionName commandName arg0 arg1 ... param0 param1 ..." biçimindeydi, yani, boşlukla ayrılmış basit sözcükler ve sayılar. Tcl "extensionName" alt dizesini gördüğünde, denetim C paketine geçirildi. Bu, komutu bir lexer / ayrıştırıcı (lex / yacc'de yapılır) aracılığıyla çalıştırdı ve sonra C rutinlerini gerektiği gibi çağırdı.

Paketi çalıştırma komutları GUI'deki bir pencereden tek tek çalıştırılabilir, ancak toplu işler geçerli Tcl komut dosyaları olan metin dosyalarını düzenleyerek yapıldı; yapmak istediğiniz dosya düzeyinde işlem yapan şablonu seçer ve ardından asıl dizini ve dosya adlarını ve paket komutlarını içerecek bir kopyayı düzenlersiniz. Bir cazibe gibi çalıştı. A kadar ...

1) Dünya PC'lere döndü ve 2) Tcl'nin iffy organizasyon yetenekleri gerçek bir rahatsızlık olmaya başladığında, senaryolar yaklaşık 500 satırdan uzun oldu. Zaman Geçti ...

Emekli oldum, Python icat edildi ve Tcl'nin mükemmel halefi gibi görünüyordu. Şimdi, bağlantı noktasını hiç yapmadım, çünkü PC'de (oldukça büyük) C programları derleme, Python'u C paketi ile genişletme ve Python / Gt? / Tk? /? ?. Bununla birlikte, düzenlenebilir şablon komut dosyalarına sahip olmak eski fikri hala uygulanabilir görünmektedir. Ayrıca, paket komutlarını yerel bir Python biçiminde girmek çok büyük bir yük olmamalıdır, örneğin:

packageName.command (arg0, arg1, ..., param0, param1, ...)

Birkaç ekstra nokta, parens ve virgül, ancak bunlar gösterişli değil.

Birisinin Python'da lex ve yacc sürümlerini yaptığını gördüğümü hatırlıyorum (deneyin: http://www.dabeaz.com/ply/ ), bu yüzden hala gerekliyse, etraftalar.

Bu karmaşanın amacı bana Python'un kendisinin bilim adamları tarafından kullanılabilecek istenen "hafif" ön uç olduğu görülüyordu. Neden böyle olmadığını düşündüğünüzü merak ediyorum ve ciddiyim.


daha sonra eklendi: Uygulama gedit eklentilerin eklenmesini bekliyor ve sitelerinde etrafa baktığımda birkaç dakika içinde bulduğum basit bir eklenti prosedürünün en açık açıklaması var. Deneyin:

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

Sorunuzu hala daha iyi anlamak istiyorum. 1) Bilim insanlarının (Python) uygulamanızı oldukça basit bir şekilde kullanabilmelerini veya 2) bilim insanlarının uygulamanıza yeni yetenekler eklemelerine izin vermek isteyip istemediğinizi bilmiyorum. Seçim # 1, görüntülerle karşı karşıya kaldığımız ve bizi anın ihtiyacına uyacak şekilde değiştirdiğimiz genel komut dosyalarını kullanmaya iten durumdur. Sizi eklentiler fikrine götüren Seçim # 2 mi, yoksa uygulamanızın komutları vermeyi pratik olmayan bir yönü mü?


2
Bağlantı çürümesi onarımı: Gedit eklentisi artık - wiki.gnome.org/Apps/Gedit/PythonPluginHowTo
ohhorob 26:13

1
Bu güzel bir gönderi, çünkü günümüz biyologlarının bize ne kadar şanslı olduğunu açıkça ve kısaca gösteriyor. Python, modül geliştiricilerine bir miktar soyutlama vermek için kullanılan modüler kodlama dilidir, böylece ana C kodunu ayrıştırmaya gerek kalmazlar. Ancak günümüzde birkaç biyolog C'yi öğrenecek, bunun yerine Python'daki her şeyi yapacak. Modülleri yazarken ana python programlarımızın karmaşıklıklarını nasıl soyutlayabiliriz? 10 yıl sonra, belki Emoji'de programlar yazılacak ve modüller sadece bir dizi homurdanma içeren ses dosyaları olacak. Ve belki de sorun değil.
JJ

10

Python Dekoratörleri ararken, basit ama kullanışlı bir kod snippet'i buldum. İhtiyaçlarınıza uygun olmayabilir, ancak çok ilham verici olabilir.

Scipy Gelişmiş Python # Eklenti Kayıt Sistemi

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('**', '')

Kullanımı:

processor = TextProcessor()
processed = processor.process(text="**foo bar**", plugins=(CleanMarkdownBolds, ))
processed = processor.process(text="**foo bar**")

1
Not: Bu örnekte, WordProcessor.pluginhiçbir şey döndürmez (None ) , bu nedenle CleanMdashesExtensionsınıfı içe aktarmak daha sonra içe aktarılır None. Eklenti sınıfları kendi başlarına faydalıysa, .pluginsınıf yöntemini yapın return plugin.
jkmacc

@jkmacc Haklısın. Snippet'i yorumunuzdan 13 gün sonra değiştirdim. Teşekkür ederim.
guneysus

7

Pycon 2009'da Dr. Andre Roberge tarafından verilen farklı eklenti mimarileri hakkındaki güzel tartışmayı çok beğendim. Eklentileri uygulamanın basit yollarından başlayarak farklı uygulama yollarına iyi bir genel bakış sunuyor.

Altı blog girişi bir dizi eşliğinde bir podcast (maymun yaması açıklamasını izleyen ikinci bölüm) olarak kullanılabilir .

Karar vermeden önce hızlıca dinlemenizi tavsiye ederim.


4

Buraya minimal bir eklenti mimarisi aramaya geldim ve hepsi benim için aşırıya kaçmış gibi görünen birçok şey buldum. Bu yüzden Süper Basit Python Eklentileri uyguladım . Kullanmak için bir veya daha fazla dizin oluşturursunuz ve __init__.pyher birine özel bir dosya bırakırsınız . Bu dizinleri içe aktarmak, diğer tüm Python dosyalarının alt modül olarak yüklenmesine neden olur ve adları __all__listeye yerleştirilir . Sonra bu modülleri doğrulamak / başlatmak / kaydetmek size kalmış. README dosyasında bir örnek var.


4

Aslında setuptools , projenin belgelerinden alınan aşağıdaki örnekte olduğu gibi bir "eklenti dizini" ile çalışır: http://peak.telecommunity.com/DevCenter/PkgResources#locating-plugins

Örnek kullanım:

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)

Uzun vadede, setuptools çok daha güvenli bir seçimdir çünkü çakışmaları veya eksik gereksinimleri olmadan eklentileri yükleyebilir.

Diğer bir faydası, eklentilerin kendileri aynı mekanizma kullanılarak, orijinal uygulamalar ilgilenmek zorunda kalmadan genişletilebilir.


3

Eklenti sistemine bir başka yaklaşım olarak, Extend Me projesini kontrol edebilirsiniz .

Örneğin, basit sınıfı ve uzantısını tanımlayalım

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

Ve kullanmaya çalışın:

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

Ve sahnenin arkasında neyin gizli olduğunu göster:

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

expand_me kütüphanesi, sınıf oluşturma sürecini metasınıflar aracılığıyla manipüle eder, bu nedenle yukarıdaki örnekte, Python'un çoklu kalıtım sayesinde her ikisinin MyCoolClassde alt sınıfı olan MyCoolClassExtensionve her ikisinin de MyCoolClassişlevselliğine sahip yeni sınıfın örneğini oluştururken

Sınıf yaratımı üzerinde daha iyi kontrol için bu lib'de tanımlanmış az sayıda metasınıf vardır:

  • ExtensibleType - alt sınıflandırma ile basit genişletilebilirlik sağlar

  • ExtensibleByHashType - ExtensibleType'a benzer, ancak sınıfın özel sürümlerini oluşturma becerisine sahip olma, temel sınıfın küresel uzantısına ve sınıfın özel sürümlerinin genişletilmesine izin verme

Bu lib OpenERP Proxy Project'te kullanılıyor ve yeterince iyi çalışıyor gibi görünüyor!

Gerçek kullanım örneği için OpenERP Proxy 'field_datetime' uzantısına bakın :

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

Recordişte nesneyi bulabilir. RecordDateTimeuzantısıdır.

Uzantıyı etkinleştirmek için, sadece uzantı sınıfını içeren modülü içe aktarın ve (yukarıdaki durumda) Record, temel sınıflarda uzantı sınıfına sahip olacak şekilde oluşturulan tüm nesneler böylece tüm işlevlerine sahip olacaktır.

Bu kütüphanenin ana avantajı, genişletilebilir nesneleri çalıştıran kodun, uzantı hakkında bilgi sahibi olması gerekmemesidir ve uzantılar, genişletilebilir nesnelerdeki her şeyi değiştirebilir.


Sana alt sınıfından örneğini anlamına düşünüyorum yani my_cool_obj = MyCoolClassExtension1()yerinemy_cool_obj = MyCoolClass()
pylang

Hayır __new__. Bu nedenle, orijinal uygulamanın tüm uzantıları bilmesine gerek yoktur. bu yaklaşım, kütüphane oluştururken son kullanıcının davranışını kolayca değiştirmesine veya genişletmesine izin vermek için kullanışlıdır. yukarıdaki örnekte, MyCoolClass kütüphanede tanımlanabilir ve onun tarafından kullanılabilir ve MyCoolClassExtension son kullanıcı tarafından tanımlanabilir.
FireMage


3

setuptools'un bir EntryPoint'i vardır :

Giriş noktaları, Python nesnelerini (işlevler veya sınıflar gibi) diğer dağıtımlar tarafından kullanılmak üzere “tanıtmak” için dağıtımların basit bir yoludur. Genişletilebilir uygulamalar ve çerçeveler, belirli bir dağıtımdan veya sys.path üzerindeki tüm etkin dağıtımlardan belirli bir ad veya gruba sahip giriş noktalarını arayabilir ve sonra reklamı yapılan nesneleri isteğe bağlı olarak inceleyebilir veya yükleyebilir.

AFAIK bu paket pip veya virtualenv kullanıyorsanız her zaman kullanılabilir.


2

@ Edomaur'un cevabına genişleyerek Marty Alchin'in çalışmalarından esinlenen basit bir eklenti çerçevesi olan simple_plugins'e (utanmaz fiş) bakmanızı önerebilirim .

Projenin README'sine dayanan kısa kullanım örneği:

# 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

Pluginlib kullanabilirsiniz .

Eklentilerin oluşturulması kolaydır ve diğer paketlerden, dosya yollarından veya giriş noktalarından yüklenebilir.

Gerekli yöntemleri tanımlayan bir eklenti üst sınıfı oluşturun:

import pluginlib

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

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

Bir üst sınıfı devralarak bir eklenti oluşturun:

import json

class JSON(Parser):
    _alias_ = 'json'

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

Eklentileri yükleyin:

loader = pluginlib.PluginLoader(modules=['sample_plugins'])
plugins = loader.plugins
parser = plugins.parser.json()
print(parser.parse('{"json": "test"}'))

1
Örnek için teşekkürler. 1 soru ile mücadele ediyorum. Eklentileri farklı paketlerden yükleme olasılığından bahsettiğinizden, belki de zaten düşündünüz. Ebeveyn sınıfının nerede olması gerektiğini merak ediyorum. Genellikle uygulamanın paketine (muhtemelen ayrı bir kaynak kodu deposu) sahip olması önerilir, ancak daha sonra eklentinin kod tabanından nasıl devralınırız? Bunun için tüm başvuruyu mu alıyoruz? Yoksa Parser sınıfı gibi arabirim koduna veya 3. bir pakette benzer soyutlamalara sahip olmak gerekli mi (3. kod deposu olurdu)?
JAponte

1
Üst sınıf, uygulama ile aynı kod tabanında, ancak muhtemelen kendi modüllerinde bulunmalıdır. Bu nedenle, adlı bir paket için , üst sınıfları tanımladığınız fooadlı bir modülünüz olabilir foo.parents. Ardından eklentileriniz içe aktarılır foo.parents. Bu, çoğu kullanım durumunda işe yarar. 'Foo'nun kendisi de içe aktarıldığından, dairesel ithalat olasılığını önlemek için, birçok proje modülün kökünü boş bırakır __main__.pyve uygulamayı başlatmak için bir dosya veya giriş noktası kullanır.
aviso

1

Ben Python için benim ihtiyaçlarına uygun küçük eklenti sistemi bulmaya çalışırken çok zaman geçirdim. Ama sonra düşündüm, eğer zaten doğal ve esnek bir miras varsa, neden kullanmıyorsunuz.

Eklentiler için kalıtım kullanma ile ilgili tek sorun, en özel (miras ağacındaki en düşük) eklenti sınıflarının ne olduğunu bilmemenizdir.

Ancak bu, temel sınıfın mirasını takip eden metasınıf ile çözülebilir ve muhtemelen en spesifik eklentilerden miras kalan sınıf oluşturabilir (aşağıdaki şekilde 'Kök genişletilmiş')

resim açıklamasını buraya girin

Bu yüzden böyle bir metasınıfı kodlayarak bir çözüm buldum:

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

Bu nedenle, metasınıf ile yapılan Kök tabanınız olduğunda ve ondan miras alınan eklentiler ağacınız olduğunda, otomatik olarak alt sınıflama ile en belirli eklentilerden miras alan sınıf alabilirsiniz:

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

Kod tabanı oldukça küçüktür (~ 30 satırlık saf kod) ve kalıtımın izin verdiği kadar esnektir.

Eğer ilgileniyorsanız, @ https://github.com/thodnev/pluginlib


1

Ayrıca Groundwork'e de göz atabilirsiniz .

Fikir, kalıplar ve eklentiler adı verilen yeniden kullanılabilir bileşenler etrafında uygulamalar oluşturmaktır. Eklentiler türetilmiş sınıflardır GwBasePattern. İşte temel bir örnek:

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

Komut satırı arabirimleri, sinyaller veya paylaşılan nesneler gibi daha gelişmiş kalıplar da vardır.

Groundwork eklentilerini yukarıda gösterildiği gibi programlı olarak bir uygulamaya bağlayarak veya otomatik olarak aracılığıyla bulur setuptools. Eklentileri içeren Python paketleri bunları özel bir giriş noktası kullanarak bildirmelidir groundwork.plugin.

İşte dokümanlar .

Feragatname : Zemin Çalışmasının yazarlarından biriyim.


0

Mevcut sağlık bakım ürünümüzde arayüz sınıfıyla uygulanan bir eklenti mimarimiz var. Teknoloji yığınımız API için Python üstündeki Django ve ön uç için nodejs üstündeki Nuxtjs'dir.

Biz temelde Django ve Nuxtjs bağlı olarak pip ve npm paketi olan ürünümüz için yazılmış bir eklenti yöneticisi uygulaması var.

Yeni eklenti geliştirme (pip ve npm) için eklenti yöneticisini bağımlılık olarak yaptık.

Pip paketinde: setup.py yardımı ile eklenti yöneticisi (kayıt defteri, inisiyasyonlar, vb.) İle bir şeyler yapmak için eklentinin giriş noktasını ekleyebilirsiniz. Https://setuptools.readthedocs.io/en/latest/setuptools .html # otomatik-script oluşturma

Npm paketinde: Pip'e benzer şekilde, kurulumu yönetmek için npm komut dosyalarında kancalar vardır. https://docs.npmjs.com/misc/scripts

Bizim kullanım alanı:

eklenti geliştirme ekibi şimdi çekirdek devopment ekibinden ayrı. Eklenti geliştirmenin kapsamı, ürün kategorilerinden herhangi birinde tanımlanan 3. taraf uygulamalarla entegrasyon içindir. Eklenti arayüzleri aşağıdaki gibi kategorilere ayrılır: - Faks, telefon, e-posta ... vb. Eklenti yöneticisi yeni kategorilere geliştirilebilir.

Sizin durumunuzda: Belki bir eklenti yazabilir ve aynı şeyi malzeme yapmak için tekrar kullanabilirsiniz.

Eklenti geliştiricilerinin yeniden temel nesneleri kullanmaları gerekiyorsa, nesne, eklenti yöneticisi içinde bir soyutlama düzeyi kullanılarak kullanılabilir, böylece herhangi bir eklenti bu yöntemleri devralabilir.

Sadece ürünümüze nasıl uyguladığımızı paylaşmak, biraz fikir vereceğini umuyor.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.