حزمة تطبيق Python كملف واحد لدعم الوظائف الإضافية أو الامتدادات؟

StackOverflow https://stackoverflow.com/questions/2876967

سؤال

هناك العديد من الأدوات المساعدة - كل ذلك مع الإجراءات المختلفة والقيود وأنظمة التشغيل المستهدفة - للحصول على حزمة بيثون وجميع تبعياتها وتحويلها إلى برنامج ثنائي واحد يسهل شحنه إلى العملاء:

يمتد وضعي خطوة واحدة إلى الأمام: سوف يرغب مطورو الطرف الثالث في كتابة المكونات الإضافية أو الإضافات أو الوظائف الإضافية لتطبيقي. بالطبع ، هو سؤال شاق كيف يمكن للمستخدمين على منصات مثل Windows أن يقوموا بسهولة بتثبيت الإضافات أو الإضافات بسهولة بطريقة يمكن أن يكتشف تطبيقي بسهولة أنه تم تثبيته. ولكن وراء هذا السؤال الأساسي هو آخر: كيف يمكن لمطور الطرف الثالث أن يحمل امتداده مع أي مكتبات يحتاجها التمديد نفسه (والتي قد تكون وحدات ثنائية ، مثل LXML) بطريقة تتوفر تبعيات المكون الإضافي للاستيراد في نفسه الوقت الذي يصبح فيه البرنامج المساعد متاحًا.

كيف يمكن الاقتراب من هذا؟ هل سيحتاج طلبي إلى منطقة الإضافات الخاصة به على القرص وسجل المكونات الخاصة به لجعل هذا المعالجة قابلة للتتبع؟ أم أن هناك آليات عامة ، يمكن أن أتجنب كتابة نفسي ، والتي من شأنها أن تسمح لتطبيق يتم توزيعه على أنه منفرد قابل للتنفيذ للنظر حوله وإيجاد مكونات إضافية مثبتة أيضًا كملفات واحدة؟

هل كانت مفيدة؟

المحلول

يجب أن تكون قادرًا على الحصول على دليل إضافي يقوم بتطبيق تطبيقك في وقت التشغيل (أو لاحقًا) لاستيراد الكود المعني. فيما يلي مثال يجب أن يعمل مع رمز .py أو .pyc العادي الذي يعمل حتى مع الإضافات المخزنة داخل ملفات zip (حتى يتمكن المستخدمون من إسقاط someLugin.zip في دليل "الإضافات" ويعملون سحريًا):

import re, os, sys
class Plugin(object):
    """
    The base class from which all plugins are derived.  It is used by the
    plugin loading functions to find all the installed plugins.
    """
    def __init__(self, foo):
        self.foo = foo
    # Any useful base plugin methods would go in here.

def get_plugins(plugin_dir):
    """Adds plugins to sys.path and returns them as a list"""

    registered_plugins = []

    #check to see if a plugins directory exists and add any found plugins
    # (even if they're zipped)
    if os.path.exists(plugin_dir):
        plugins = os.listdir(plugin_dir)
        pattern = ".py$"
        for plugin in plugins:
            plugin_path = os.path.join(plugin_dir, plugin)
            if os.path.splitext(plugin)[1] == ".zip":
                sys.path.append(plugin_path)
                (plugin, ext) = os.path.splitext(plugin) # Get rid of the .zip extension
                registered_plugins.append(plugin)
            elif plugin != "__init__.py":
                if re.search(pattern, plugin):
                    (shortname, ext) = os.path.splitext(plugin)
                    registered_plugins.append(shortname)
            if os.path.isdir(plugin_path):
                plugins = os.listdir(plugin_path)
                for plugin in plugins:
                    if plugin != "__init__.py":
                        if re.search(pattern, plugin):
                            (shortname, ext) = os.path.splitext(plugin)
                            sys.path.append(plugin_path)
                            registered_plugins.append(shortname)
    return registered_plugins

def init_plugin_system(cfg):
    """
    Initializes the plugin system by appending all plugins into sys.path and
    then using load_plugins() to import them.

        cfg - A dictionary with two keys:
        plugin_path - path to the plugin directory (e.g. 'plugins')
        plugins - List of plugin names to import (e.g. ['foo', 'bar'])
    """
    if not cfg['plugin_path'] in sys.path:
        sys.path.insert(0, cfg['plugin_path'])
    load_plugins(cfg['plugins'])

def load_plugins(plugins):
    """
    Imports all plugins given a list.
    Note:  Assumes they're all in sys.path.
    """
    for plugin in plugins:
        __import__(plugin, None, None, [''])
        if plugin not in Plugin.__subclasses__():
            # This takes care of importing zipped plugins:
            __import__(plugin, None, None, [plugin])

لذلك ، دعنا نقول إن لديّ مكون إضافي يسمى "foo.py" في دليل يسمى "الإضافات" (وهو في قاعدة التطبيق الخاص بي) الذي سيضيف إمكانية جديدة إلى طلبي. قد تبدو المحتويات هكذا:

from plugin_stuff import Plugin

class Foo(Plugin):
    """An example plugin."""
    self.menu_entry = {'Tools': {'Foo': self.bar}}
    def bar(self):
        return "foo plugin!"

يمكنني تهيئة الإضافات الخاصة بي عندما أقوم بتشغيل تطبيقي مثل ذلك:

plugin_dir = "%s/plugins" % os.getcwd()
plugin_list = get_plugins(plugin_dir)
init_plugin_system({'plugin_path': plugin_dir, 'plugins': plugin_list})
plugins = find_plugins()
plugin_menu_entries = []
for plugin in plugins:
    print "Enabling plugin: %s" % plugin.__name__
    plugin_menu_entries.append(plugin.menu_entry))
add_menu_entries(plugin_menu_entries) # This is an imaginary function

يجب أن يعمل ذلك طالما أن البرنامج المساعد هو إما ملف .py أو .pyc (على افتراض أنه تم تجميعه للبايت للنظام الأساسي المعني). يمكن أن يكون ملفًا مستقلًا أو داخل دليل مع فيه.السنة التحضيرية أو داخل ملف zip مع نفس القواعد.

كيف أعرف أن هذا يعمل؟ هذه هي الطريقة التي قمت بتنفيذ الإضافات في بيسي. Pyci هو تطبيق ويب ولكن لا يوجد سبب لعدم عمل هذه الطريقة مع واجهة المستخدم الرسومية العادية. على سبيل المثال أعلاه ، اخترت استخدام وظيفة add_menu_entries () وهمية مع متغير كائن مكون الإضافي يمكن استخدامه لإضافة طرق مكون إضافي إلى قوائم واجهة المستخدم الرسومية.

نأمل أن تساعدك هذه الإجابة على بناء نظام البرنامج المساعد الخاص بك. إذا كنت تريد أن ترى بالتحديد كيفية تنفيذها ، فإنني أوصيك بتنزيل رمز مصدر PYCI والنظر في البرنامج المساعد.

نصائح أخرى

فيما يلي مثال آخر على تطبيق Python الذي يستخدم الإضافات: OpenStv. هنا ، يمكن أن تكون الإضافات فقط وحدات بيثون.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top