पायथन में एक न्यूनतम प्लगइन वास्तुकला का निर्माण


190

मेरे पास एक आवेदन है, जिसे पायथन में लिखा गया है, जिसका उपयोग काफी तकनीकी दर्शकों (वैज्ञानिकों) द्वारा किया जाता है।

मैं एक अच्छा तरीका तलाश रहा हूं ताकि उपयोगकर्ताओं द्वारा एप्लिकेशन को एक्सेसेबल बनाया जा सके, यानी स्क्रिप्टिंग / प्लगइन आर्किटेक्चर।

मैं कुछ बेहद हल्के की तलाश में हूं । अधिकांश स्क्रिप्ट या प्लगइन्स, तृतीय-पक्ष द्वारा विकसित और वितरित नहीं किए जा रहे हैं और इंस्टॉल किए जा रहे हैं, लेकिन उपयोगकर्ता द्वारा कुछ मिनटों में दोहराए जाने वाले कार्य को स्वचालित करने, फ़ाइल प्रारूप के लिए समर्थन जोड़ने के लिए कुछ करने जा रहे हैं, आदि तो प्लगइन्स में पूर्ण न्यूनतम बॉयलरप्लेट कोड होना चाहिए, और किसी फ़ोल्डर में कॉपी करने के अलावा किसी भी 'इंस्टॉलेशन' की आवश्यकता नहीं होती है (इसलिए सेटटॉप्स एंट्री पॉइंट्स या ज़ोप प्लगइन आर्किटेक्चर जैसा कुछ बहुत अधिक लगता है।)

क्या इस तरह की कोई प्रणाली पहले से ही है, या ऐसी कोई भी परियोजना जो एक समान योजना को लागू करती है जिसे मुझे विचारों / प्रेरणा के लिए देखना चाहिए?

जवाबों:


150

मेरा, मूल रूप से, "प्लगइन्स" नामक एक निर्देशिका है, जो मुख्य ऐप को पोल कर सकता है और फिर फ़ाइलों को चुनने के लिए imp.load_module का उपयोग कर सकता है, संभवतः मॉड्यूल-स्तर के कॉन्फ़िगर पैरामस के साथ एक प्रसिद्ध प्रवेश बिंदु की तलाश करें, और वहां से जाएं। मैं एक निश्चित मात्रा में गतिशीलता के लिए फ़ाइल-निगरानी सामान का उपयोग करता हूं जिसमें प्लगइन्स सक्रिय हैं, लेकिन यह एक अच्छा-से-होने वाला है।

बेशक, कोई भी आवश्यकता जो यह कहती है कि "मुझे [बड़ी, जटिल चीज़] X की आवश्यकता नहीं है; मुझे बस कुछ हल्का चाहिए" एक बार में एक्स की खोज की आवश्यकता को फिर से लागू करने का जोखिम है। लेकिन यह कहना है कि आप इसे वैसे भी कर कुछ मज़ा नहीं कर सकते हैं :)


26
आपका बहुत बहुत धन्यवाद! मैंने आपकी पोस्ट के आधार पर थोड़ा सा ट्यूटोरियल लिखा: lkubuntu.wordpress.com/2012/10/02/writing-a-python-plugin-api
MiJyn

9
impमॉड्यूल के पक्ष में पदावनत किया जा रहा है importlibअजगर 3.4 से शुरू
b0fh

1
कई उपयोग-मामलों में आप एक प्रतिस्थापन के रूप में importlib.import_module का उपयोग कर सकते हैं imp.load_module
क्रिस अरंड्ट

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)

यह निश्चित रूप से "न्यूनतम" है, इसमें बिल्कुल कोई त्रुटि की जाँच नहीं है, शायद अनगिनत सुरक्षा समस्याएं हैं, यह बहुत लचीला नहीं है - लेकिन यह आपको दिखाना चाहिए कि पायथन में एक प्लगइन सिस्टम कितना सरल हो सकता है।

आप शायद छोटा सा भूत में भी देखना चाहते हैं, हालांकि आप बहुत कुछ कर सकते हैं __import__, os.listdirऔर कुछ स्ट्रिंग हेरफेर कर सकते हैं।


4
मुझे लगता है कि आप बदलना चाहते हो सकता है def call_plugin(name, *args)के लिए def call_plugin(name, *args, **kwargs)तो है, और plugin.plugin_main(*args)करने के लिएplugin.plugin_main(*args, **kwargs)
रॉन क्लेन

12
अजगर 3 में, के impपक्ष में पदावनत किया जाता हैimportlib
एडम बैक्सटर


25

हालांकि यह प्रश्न वास्तव में दिलचस्प है, मुझे लगता है कि अधिक विवरण के बिना उत्तर देना काफी कठिन है। यह किस प्रकार का अनुप्रयोग है? क्या इसमें GUI है? क्या यह एक कमांड-लाइन टूल है? स्क्रिप्ट का एक सेट? एक अद्वितीय प्रविष्टि बिंदु के साथ एक कार्यक्रम, आदि ...

मेरे पास जो थोड़ी बहुत जानकारी है, उसे देखते हुए, मैं बहुत ही सामान्य तरीके से जवाब दूंगा।

प्लग इन को जोड़ने का क्या मतलब है?

  • आपको संभवतः एक कॉन्फ़िगरेशन फ़ाइल जोड़ना होगा, जो लोड करने के लिए पथ / निर्देशिका को सूचीबद्ध करेगा।
  • एक और तरीका यह होगा कि "उस प्लगइन / निर्देशिका में किसी भी फाइल को लोड किया जाएगा", लेकिन इसमें आपके उपयोगकर्ताओं को फ़ाइलों के चारों ओर जाने के लिए असुविधाजनक है।
  • एक अंतिम, मध्यवर्ती विकल्प सभी प्लगइन्स को एक ही प्लगइन / फ़ोल्डर में होना आवश्यक है, और फिर एक कॉन्फ़िगर फ़ाइल में रिश्तेदार पथों का उपयोग करके उन्हें सक्रिय / निष्क्रिय करना होगा।

एक शुद्ध कोड / डिजाइन अभ्यास पर, आपको स्पष्ट रूप से निर्धारित करना होगा कि आप अपने उपयोगकर्ताओं को किस व्यवहार / विशिष्ट कार्यों को विस्तारित करना चाहते हैं। सामान्य एंट्री पॉइंट / फ़ंक्शंस का एक सेट पहचानें जो हमेशा ओवरराइड होगा, और इन क्रियाओं के भीतर समूह निर्धारित करेगा। एक बार यह पूरा हो जाने के बाद, आपके आवेदन का विस्तार करना आसान होना चाहिए,

उदाहरण का उपयोग कर हुक , MediaWiki से प्रेरित (PHP, लेकिन क्या वास्तव में भाषा मायने रखती है?):

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)

एक और उदाहरण, मर्क्यूरियल से प्रेरित है। इधर, एक्सटेंशन केवल आदेशों को जोड़ने एचजी कमांडलाइन निष्पादन, व्यवहार का विस्तार।

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 }

दोनों दृष्टिकोणों के लिए, आपको अपने विस्तार के लिए सामान्य आरंभ और अंतिम आवश्यकता हो सकती है । आप या तो एक सामान्य इंटरफ़ेस का उपयोग कर सकते हैं, जिसे आपके सभी एक्सटेंशन को लागू करना होगा (दूसरे दृष्टिकोण के साथ बेहतर होगा; मर्क्यूरियल एक रिपॉसेटअप (यूआई, रेपो) का उपयोग करता है जिसे सभी एक्सटेंशन के लिए कहा जाता है), या हुक-अप दृष्टिकोण का उपयोग करें, एक के साथ हुकसेसेटअप हुक।

लेकिन फिर, यदि आप अधिक उपयोगी उत्तर चाहते हैं, तो आपको अपने प्रश्न को सीमित करना होगा;)


11

मार्टी अल्चिन का सरल प्लगइन ढांचा वह आधार है जिसका उपयोग मैं अपनी जरूरतों के लिए करता हूं। मैं वास्तव में इस पर एक नज़र डालने की सिफारिश करता हूं, मुझे लगता है कि यह वास्तव में एक अच्छी शुरुआत है यदि आप कुछ सरल और आसानी से हैक करने योग्य चाहते हैं। आप इसे Django Snippets के रूप में भी पा सकते हैं ।


मैं आधार के रूप में pyduck के साथ ऐसा कुछ करने की कोशिश कर रहा हूं।
edomaur

यह बहुत Django- विशिष्ट है जो मैं बता सकता हूं।
ज़ोरान पावलोविच

3
@ZoranPavlovic: बिल्कुल भी नहीं, मानक पायथन की कुछ लाइनें, आपको Django का उपयोग करने की आवश्यकता नहीं है।
edomaur

11

मैं एक सेवानिवृत्त जीवविज्ञानी हूं, जिन्होंने डिजिटल माइक्रोग्रैक्फ़ से निपटा और खुद को एक एसजीआई मशीन पर चलाने के लिए एक छवि प्रसंस्करण और विश्लेषण पैकेज (तकनीकी रूप से पुस्तकालय नहीं) लिखने के लिए मिला। मैंने C में कोड लिखा और स्क्रिप्टिंग भाषा के लिए Tcl का उपयोग किया। GUI, जैसे कि यह था, Tk का उपयोग करके किया गया था। Tcl में दिखाई देने वाली कमांड "extensionName commandName arg0 arg1 ... param0 param1 ..." के रूप में थी, जो कि सरल अंतरिक्ष-अलग शब्द और संख्याएँ हैं। जब Tcl ने "extensionName" को प्रतिस्थापित किया, तो नियंत्रण को C पैकेज में पास कर दिया गया। इसके बदले में एक लेक्सर / पार्सर (lex / yacc में किया गया) के माध्यम से कमांड को चलाया गया और फिर आवश्यक रूप से C रूटीन को बुलाया गया।

पैकेज को संचालित करने के आदेशों को जीयूआई में एक खिड़की के माध्यम से एक-एक करके चलाया जा सकता था, लेकिन बैच की फाइलें टेक्स्ट फाइलों को संपादित करके की गईं जो कि मान्य Tcl स्क्रिप्ट थीं; आप उस टेम्पलेट को चुनेंगे, जिस तरह का फ़ाइल-स्तरीय ऑपरेशन आप करना चाहते थे और फिर वास्तविक निर्देशिका और फ़ाइल नाम के साथ-साथ पैकेज कमांड को कॉपी करने के लिए एक प्रतिलिपि संपादित करें। इसने एक जादू की तरह काम किया। जब तक ...

1) दुनिया पीसी और 2 में बदल गई) स्क्रिप्ट लगभग 500 लाइनों से अधिक लंबी हो गई, जब Tcl की iffy संगठनात्मक क्षमताओं को वास्तविक असुविधा होने लगी। समय बीत गया ...

मैं सेवानिवृत्त हो गया, पायथन का आविष्कार हो गया, और यह Tcl के लिए सही उत्तराधिकारी की तरह लग रहा था। अब, मैंने कभी भी पोर्ट नहीं किया है, क्योंकि मैंने पीसी पर संकलन (बहुत बड़े) सी कार्यक्रमों की चुनौतियों का सामना नहीं किया है, पायथन को सी पैकेज के साथ विस्तारित किया है, और पायथन / जीटी / टीके में GUIs कर रहा है? / ?। हालाँकि, संपादन योग्य टेम्प्लेट स्क्रिप्ट के पुराने विचार अभी भी व्यावहारिक हैं। इसके अलावा, मूल पायथन फॉर्म में पैकेज कमांड दर्ज करने के लिए यह बहुत बड़ा बोझ नहीं होना चाहिए, जैसे:

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

कुछ अतिरिक्त डॉट्स, परेंस और कॉमा, लेकिन वे शोस्टॉपर नहीं हैं।

मुझे याद है कि किसी ने पायथन और याक के संस्करणों को पायथन में किया है (कोशिश करें: http://www.dabeaz.com/ply/ ), इसलिए यदि वे अभी भी आवश्यक हैं, तो वे आसपास हैं।

इस जुगाड़ की बात यह है कि मुझे ऐसा लग रहा है कि पाइथन खुद ही वैज्ञानिकों द्वारा वांछित "हल्के" फ्रंट एंड प्रयोज्य है। मैं यह जानने के लिए उत्सुक हूं कि आपको क्यों लगता है कि यह नहीं है, और मेरा मतलब है कि गंभीरता से।


बाद में जोड़ा गया: आवेदन gedit प्लगइन्स जोड़े जाने की आशंका है और उनकी साइट पर एक सरल प्लगइन प्रक्रिया के स्पष्ट विवरण के बारे में है जो मैंने कुछ मिनटों के आसपास देखा है। प्रयत्न:

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

मैं अभी भी आपके प्रश्न को बेहतर ढंग से समझना चाहूंगा। मैं स्पष्ट नहीं हूं कि क्या आप 1) चाहते हैं कि वैज्ञानिक आपके (पायथन) एप्लिकेशन को विभिन्न तरीकों से उपयोग करने में सक्षम हों या 2) वैज्ञानिकों को आपके आवेदन में नई क्षमताओं को जोड़ने की अनुमति देना चाहते हैं। विकल्प # 1 वह स्थिति है जिसका हमने छवियों के साथ सामना किया है और जिसके कारण हमें सामान्य लिपियों का उपयोग करना पड़ा जिसे हमने इस समय की आवश्यकता के अनुरूप संशोधित किया। क्या यह चॉइस # 2 है जो आपको प्लगइन्स के विचार की ओर ले जाता है, या क्या यह आपके एप्लिकेशन का कुछ पहलू है जो इसे जारी करने के आदेश को अव्यवहार्य बनाता है?


2
लिंक सड़ने की मरम्मत: Gedit प्लगइन अब है - wiki.gnome.org/Apps/Gedit/PythonPluginHowTo
OHhorob

1
यह एक सुंदर पोस्ट है, क्योंकि यह स्पष्ट रूप से और स्पष्ट रूप से दिखाता है कि आधुनिक आधुनिक जीवविज्ञानी कितने भाग्यशाली हैं। उसके लिए, अजगर, मॉड्यूलर स्क्रिप्टिंग भाषा है जिसका उपयोग मॉड्यूल डेवलपर्स को कुछ अमूर्तता देने के लिए किया जाता है ताकि उन्हें मुख्य सी कोड को पार्स करने की आवश्यकता न हो। हालांकि, अब कुछ दिन जीवविज्ञानी C को सीखेंगे, बजाय इसके कि आप पायथन में सब कुछ कर रहे हैं। मॉड्यूल लिखते समय हम अपने मुख्य अजगर कार्यक्रमों की जटिलताओं को कैसे दूर करते हैं? अब से 10 वर्षों में, शायद इमोजी में प्रोग्राम लिखे जाएंगे और मॉड्यूल सिर्फ ऑडियो फाइल होंगे जिसमें ग्रंट्स की एक श्रृंखला होगी। और शायद यह ठीक है।
जेजे

10

जब मैंने पायथन डेकोरेटर्स की खोज की, तो एक सरल लेकिन उपयोगी कोड स्निपेट मिला। यह आपकी आवश्यकताओं में फिट नहीं हो सकता है लेकिन बहुत प्रेरणादायक है।

स्किपी उन्नत पायथन # प्लगिन पंजीकरण प्रणाली

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

उपयोग:

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

1
नोट: इस उदाहरण में, WordProcessor.pluginकुछ भी वापस नहीं करता है ( None), इसलिए CleanMdashesExtensionबाद में वर्ग आयात करना बस आयात करता है None। यदि प्लगइन कक्षाएं अपने आप से उपयोगी हैं, तो .pluginक्लास विधि बनाएं return plugin
जमैकाक

@jkmacc आप सही हैं। मैंने आपकी टिप्पणी के 13 दिन बाद स्निपेट को संशोधित किया है। धन्यवाद।
गनियुस

7

डॉ। आंद्रे रॉबर्टे द्वारा पायकोन 2009 में दिए गए विभिन्न प्लगइन आर्किटेक्चर पर मैंने अच्छी चर्चा का आनंद लिया। वह प्लगइन्स को लागू करने के विभिन्न तरीकों का एक अच्छा अवलोकन देता है, जो वास्तव में सरल से शुरू होता है।

यह पॉडकास्ट के रूप में उपलब्ध है (बंदर-पेटिंग के स्पष्टीकरण के बाद दूसरा भाग) छह ब्लॉग प्रविष्टियों की एक श्रृंखला के साथ ।

मैं आपको निर्णय लेने से पहले इसे शीघ्र सुनने की सलाह देता हूं।


4

मैं एक न्यूनतम प्लगइन वास्तुकला की तलाश में यहां पहुंचा, और बहुत सी चीजें मिलीं जो सभी को मेरे लिए ओवरकिल की तरह लग रही थीं। तो, मैंने सुपर सिंपल पायथन प्लगइन्स लागू किया है । इसका उपयोग करने के लिए, आप एक या अधिक निर्देशिकाएँ बनाते हैं और __init__.pyप्रत्येक में एक विशेष फ़ाइल छोड़ते हैं । उन निर्देशिकाओं को आयात करने से अन्य सभी पायथन फाइल को सबमॉड्यूल के रूप में लोड किया जाएगा, और उनका नाम __all__सूची में रखा जाएगा । तब यह उन मॉड्यूल को मान्य / आरंभ / पंजीकृत करने के लिए आप पर निर्भर है। README फ़ाइल में एक उदाहरण है।


4

वास्तव में सेटटॉप्स एक "प्लगइन्स डायरेक्टरी" के साथ काम करते हैं, प्रोजेक्ट के डॉक्यूमेंटेशन से लिए गए निम्न उदाहरण के रूप में: http://peak.telecommunity.com/DevCenter/PkgResources#locating-plugins

उदाहरण उपयोग:

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)

लंबे समय में, सेटपूल एक बहुत ही सुरक्षित विकल्प है क्योंकि यह प्लगइन्स को बिना संघर्ष या लापता आवश्यकताओं के लोड कर सकता है।

एक और लाभ यह है कि प्लगइन्स को स्वयं उसी तंत्र का उपयोग करके बढ़ाया जा सकता है, मूल अनुप्रयोगों के बिना इसकी परवाह किए बिना।


3

प्लगइन सिस्टम के लिए एक और दृष्टिकोण के रूप में, आप एक्स्टेंड मी प्रोजेक्ट की जांच कर सकते हैं ।

उदाहरण के लिए, चलिए सरल वर्ग और उसके विस्तार को परिभाषित करते हैं

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

और इसका उपयोग करने का प्रयास करें:

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

और दिखाओ कि दृश्य के पीछे क्या छिपा है:

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

extend_me metaclasses के माध्यम से पुस्तकालय manipulates वर्ग बनाने की प्रक्रिया है, इस प्रकार के उदाहरण में ऊपर, नया उदाहरण बनाते समय की MyCoolClassहम नए वर्ग के उदाहरण दोनों की उपवर्ग है कि मिल गया है MyCoolClassExtensionऔर MyCoolClassपायथन के लिए उन दोनों के होने कार्यक्षमता, धन्यवाद एकाधिक वंशानुक्रम

वर्ग निर्माण पर बेहतर नियंत्रण के लिए इस परिवाद में परिभाषित कुछ मेटाक्लस हैं:

  • ExtensibleType - उपवर्ग द्वारा सरल विस्तार की अनुमति देता है

  • ExtensibleByHashType - एक्सटेंसिबल टाइप के समान, लेकिन क्लास के विशेष संस्करणों के निर्माण की क्षमता, बेस क्लास के वैश्विक विस्तार और क्लास के विशेष संस्करणों के विस्तार की अनुमति

इस lib का उपयोग OpenERP प्रॉक्सी प्रोजेक्ट में किया जाता है , और लगता है कि यह काफी अच्छा काम कर रहा है!

उपयोग के वास्तविक उदाहरण के लिए, OpenERP प्रॉक्सी '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यहाँ विलुप्त वस्तु है। RecordDateTimeविस्तार है।

एक्सटेंशन को सक्षम करने के लिए, बस इंपोर्ट मॉड्यूल जिसमें एक्सटेंशन क्लास है, और (ऊपर मामले में) इसके Recordबाद बनाई गई सभी ऑब्जेक्ट बेस क्लास में एक्सटेंशन क्लास होगी, इस प्रकार इसकी सभी कार्यक्षमता होगी।

इस लाइब्रेरी का मुख्य लाभ यह है कि, कोड जो एक्स्टेंसिबल ऑब्जेक्ट्स को संचालित करता है, उसे एक्सटेंशन के बारे में जानने की आवश्यकता नहीं होती है और एक्सटेंशन एक्सटेंसिबल ऑब्जेक्ट्स में सब कुछ बदल सकता है।


मुझे लगता है कि आप उपवर्ग से तात्पर्य करना चाहते हैं, अर्थात my_cool_obj = MyCoolClassExtension1()इसके बजायmy_cool_obj = MyCoolClass()
पाइलैंग

नहीं, एक्सटेंसिबल क्लास में ओवरराइड __new__विधि है, इसलिए यह ऑटोमैटिकली सभी उपवर्गों को खोजता है, और नए वर्ग का निर्माण करता है, जो कि उन सभी का उपवर्ग है, और इस निर्मित वर्ग के नए उदाहरण लौटाते हैं। इस प्रकार, मूल एप्लिकेशन को सभी एक्सटेंशन के बारे में जानने की आवश्यकता नहीं है। पुस्तकालय का निर्माण करते समय यह दृष्टिकोण उपयोगी है, अंत उपयोगकर्ता को इसे आसानी से संशोधित या विस्तारित करने की अनुमति देने के लिए। ऊपर के उदाहरण में, MyCoolClass को लाइब्रेरी में परिभाषित किया जा सकता है, और इसका उपयोग किया जा सकता है, और MyCoolClassExtension को अंतिम उपयोगकर्ता द्वारा परिभाषित किया जा सकता है।
फायरमैज

उत्तर के लिए एक और उदाहरण जोड़ा गया
FireMage

3

सेटओपूलों में एक एंट्रीपॉइंट है :

प्रवेश बिंदु अन्य वितरणों द्वारा उपयोग के लिए पायथन ऑब्जेक्ट्स (जैसे फ़ंक्शंस या कक्षाएं) के वितरण के लिए एक सरल तरीका है। एक्स्टेंसिबल एप्लिकेशन और फ्रेमवर्क एक विशेष नाम या समूह के साथ प्रवेश बिंदुओं की खोज कर सकते हैं, या तो एक विशिष्ट वितरण से या sys.path पर सभी सक्रिय वितरणों से, और फिर वसीयत में विज्ञापित वस्तुओं का निरीक्षण या लोड कर सकते हैं।

AFAIK यह पैकेज हमेशा उपलब्ध है यदि आप पाइप या वर्चुनेल का उपयोग करते हैं।


2

@ Edomaur के उत्तर पर विस्तार करते हुए मैं simple_plugins (बेशर्म प्लग) पर एक नज़र डालने का सुझाव दे सकता हूं , जो कि मार्टी अल्चिन के काम से प्रेरित एक सरल प्लगइन फ्रेमवर्क है

परियोजना की README पर आधारित एक छोटा उपयोग उदाहरण:

# 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

जब मैंने पायथन में एक प्लगइन ढांचे की खोज की थी, तब मैंने इस धागे को पढ़ने में समय बिताया है। मैंने कुछ का उपयोग किया है, लेकिन उनके साथ कमियां थीं । यहां मैं 2017 में आपकी जांच के लिए आया हूं, एक इंटरफ़ेस मुक्त, शिथिल युग्मित प्लगइन प्रबंधन प्रणाली: मुझे बाद में लोड करें । यहाँ इसका उपयोग करने के तरीके के बारे में ट्यूटोरियल दिए गए हैं ।


2

आप pluginlib का उपयोग कर सकते हैं ।

प्लगइन्स बनाना आसान है और इसे अन्य पैकेज, फ़ाइल पथ या प्रविष्टि बिंदुओं से लोड किया जा सकता है।

किसी भी आवश्यक तरीकों को परिभाषित करते हुए एक प्लगइन पैरेंट क्लास बनाएँ:

import pluginlib

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

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

पैरेंट क्लास इनहेरिट करके एक प्लगइन बनाएं:

import json

class JSON(Parser):
    _alias_ = 'json'

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

प्लगइन्स लोड करें:

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

1
उदाहरण के लिए धन्यवाद। मैं 1 प्रश्न के साथ संघर्ष कर रहा हूं। चूंकि आपने विभिन्न पैकेजों से प्लगइन्स को लोड करने की संभावना का उल्लेख किया है, शायद आपने पहले से ही इसके बारे में सोचा था। मुझे आश्चर्य है कि मूल वर्ग को कहां रहना चाहिए। आमतौर पर इसे एप्लिकेशन के पैकेज में रखने की सिफारिश की जाती है (संभवतः एक अलग स्रोत कोड रिपॉजिटरी), लेकिन फिर हम प्लगइन के कोडबेस में इसे कैसे प्राप्त करेंगे? क्या हम इसके लिए पूरा आवेदन आयात करते हैं? या यह एक 3 पैकेज (जो एक 3 जी भंडार होगा) में पार्सर वर्ग या इसी तरह के सार की तरह इंटरफ़ेस कोड होना आवश्यक है?
जपप्ते

1
अभिभावक वर्ग को आवेदन के समान कोड बेस में रहना चाहिए, लेकिन शायद अपने स्वयं के मॉड्यूल में। इसलिए, नामक एक पैकेज के लिए foo, आपके पास एक मॉड्यूल हो सकता है जिसे कहा जाता है foo.parentsजहां आप मूल कक्षाओं को परिभाषित करते हैं। तब आपका प्लगइन्स आयात करेगा foo.parents। यह ज्यादातर उपयोग के मामलों के लिए अच्छी तरह से काम करता है। क्योंकि 'फू' स्वयं भी आयातित हो जाता है, परिपत्र आयात की संभावना से बचने के लिए, बहुत सारी परियोजनाएं मॉड्यूल की जड़ को खाली छोड़ देती हैं और __main__.pyएप्लिकेशन लॉन्च करने के लिए फ़ाइल या एंट्री पॉइंट का उपयोग करती हैं।
एविसो २५'१

1

मैंने पायथन के लिए छोटे प्लगइन सिस्टम को खोजने में बहुत समय बिताया है, जो मेरी आवश्यकताओं को पूरा करेगा। लेकिन तब मैंने बस सोचा था, अगर पहले से ही एक विरासत है, जो प्राकृतिक और लचीला है, तो इसका उपयोग क्यों न करें।

प्लगइन्स के लिए इनहेरिटेंस का उपयोग करने के साथ एकमात्र समस्या यह है कि आप नहीं जानते कि सबसे विशिष्ट (इनहेरिटेंस ट्री पर सबसे कम) प्लगइन क्लासेस क्या हैं।

लेकिन यह मेटाक्लस के साथ हल किया जा सकता है, जो बेस क्लास की विरासत का ट्रैक रखता है, और संभवतः क्लास का निर्माण कर सकता है, जो अधिकांश विशिष्ट प्लगइन्स से विरासत में मिला है (नीचे दिए गए आंकड़े पर 'रूट विस्तारित')

यहां छवि विवरण दर्ज करें

तो मैं इस तरह के मेटाक्लस को कोड करके एक समाधान के साथ आया:

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

इसलिए जब आपके पास रूट बेस होता है, मेटाक्लास के साथ बनाया जाता है, और इसमें प्लग इन का पेड़ होता है, जो इसे विरासत में मिलता है, तो आप स्वचालित रूप से क्लास प्राप्त कर सकते हैं, जो कि केवल सबक्लासिंग द्वारा सबसे विशिष्ट प्लग इन से विरासत में मिला है:

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

कोड आधार बहुत छोटा है (~ शुद्ध लाइनों की 30 पंक्तियाँ) और विरासत के रूप में लचीला अनुमति देता है।

यदि आप रुचि रखते हैं, तो @ https://github.com/thodnev/pluginlib शामिल हों


1

ग्राउंडवर्क पर भी आपकी नज़र हो सकती है ।

विचार पुन: प्रयोज्य घटकों, पैटर्न और प्लगइन्स नामक अनुप्रयोगों के निर्माण का है। प्लगइन्स वे कक्षाएं हैं जो से प्राप्त होती हैं GwBasePattern। यहाँ एक मूल उदाहरण दिया गया है:

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

कमांड लाइन इंटरफेस, सिग्नलिंग या साझा किए गए ऑब्जेक्ट्स को संभालने के लिए अधिक उन्नत पैटर्न भी हैं।

ग्राउंडवर्क अपने प्लगइन्स को या तो प्रोग्रामेटिक रूप से उन्हें किसी ऐप से बाँधता है जैसा कि ऊपर दिखाया गया है या स्वचालित रूप से setuptools। प्लगइन्स वाले पायथन पैकेजों को एक विशेष प्रविष्टि बिंदु का उपयोग करके इन्हें घोषित करना चाहिए groundwork.plugin

यहाँ डॉक्स हैं

अस्वीकरण : मैं ग्राउंडवर्क के लेखकों में से एक हूं।


0

हमारे वर्तमान हेल्थकेयर उत्पाद में हमारे पास इंटरफ़ेस क्लास के साथ कार्यान्वित एक प्लगइन आर्किटेक्चर है। हमारे टेक स्टैक Django हैं जो पायथन के लिए पायथन के ऊपर और नुक्तेज के शीर्ष पर नुजेंड्स के लिए नुजेंड्स हैं।

हमारे पास हमारे उत्पाद के लिए लिखा गया एक प्लगइन मैनेजर ऐप है जो मूल रूप से Django और Nuxtjs के पालन में पाइप और npm पैकेज है।

नए प्लगइन विकास (पाइप और एनपीएम) के लिए हमने प्लगइन मैनेजर को निर्भरता के रूप में बनाया।

Pip पैकेज में: setup.py की मदद से आप प्लगइन के एंट्रीपॉइंट को प्लगइन मैनेजर (रजिस्ट्री, आरंभ, ... आदि) के साथ कुछ करने के लिए जोड़ सकते हैं। https://setuptools.readthedocs.io/en/latest/setuptools .html # स्वचालित स्क्रिप्ट निर्माण

Npm पैकेज में: पाइप के समान अधिष्ठापन को संभालने के लिए npm स्क्रिप्ट में हुक होते हैं। https://docs.npmjs.com/misc/scripts

हमारे usecase:

प्लगइन डेवलपमेंट टीम अब कोर डेवोपमेंट टीम से अलग है। प्लगइन विकास का दायरा 3rd पार्टी ऐप्स के साथ एकीकृत करने के लिए है, जो उत्पाद की किसी भी श्रेणी में परिभाषित किए गए हैं। प्लगइन इंटरफेस को उदाहरण के लिए वर्गीकृत किया गया है: - फैक्स, फोन, ईमेल ... आदि प्लगइन प्रबंधक को नई श्रेणियों में बढ़ाया जा सकता है।

आपके मामले में: हो सकता है कि आपके पास लिखा हुआ एक प्लगइन हो और सामान करने के लिए उसी का पुन: उपयोग किया जाए।

यदि प्लगइन डेवलपर्स को कोर ऑब्जेक्ट्स को फिर से उपयोग करने की आवश्यकता होती है जो कि ऑब्जेक्ट का उपयोग प्लगइन मैनेजर के भीतर एब्सट्रैक्शन के स्तर को करके किया जा सकता है ताकि कोई भी प्लग इन विधियों को इनहेरिट कर सके।

बस साझा करना कि हमने अपने उत्पाद में कैसे लागू किया है आशा है कि यह थोड़ा विचार देगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.