تحميل الفئة الديناميكية في Python 2.6: RunTimewarning: لم يتم العثور على الوحدة النمطية للوحدة الأم أثناء التعامل مع الاستيراد المطلق
-
20-09-2019 - |
سؤال
أنا أعمل على نظام إضافي حيث يتم تحميل وحدات البرنامج المساعد مثل هذا:
def load_plugins():
plugins=glob.glob("plugins/*.py")
instances=[]
for p in plugins:
try:
name=p.split("/")[-1]
name=name.split(".py")[0]
log.debug("Possible plugin: %s", name)
f, file, desc=imp.find_module(name, ["plugins"])
plugin=imp.load_module('plugins.'+name, f, file, desc)
getattr(plugin, "__init__")(log)
instances=instances+plugin.get_instances()
except Exception as e:
log.info("Failed to load plugin: "+str(p))
log.info("Error: %s " % (e))
log.info(traceback.format_exc(e))
return instances
يعمل الرمز ، ولكن لكل عبارة استيراد في رمز البرنامج المساعد ، أحصل على تحذير مثل هذا:
plugins/plugin.py:2: RuntimeWarning: Parent module 'plugins' not found while handling absolute import
import os
لم يتم الإبلاغ عن أي أخطاء لرمز البرنامج الرئيسي ، وعمل الإضافات.
هل يمكن لأي شخص أن يشرح معنى التحذير وما أفعله خطأ. هل أحتاج إلى إنشاء وحدة مكونات إضافية فارغة بشكل منفصل واستيرادها للحفاظ على Python سعيدًا؟
المحلول
إذا كان الدليل plugins
كانت حزمة حقيقية (موجودة __init__.py
جيد) ، يمكنك بسهولة استخدام pkgutils لتعداد ملفات البرنامج المساعد وتحميلها.
import pkgutil
# import our package
import plugins
list(pkgutil.iter_modules(plugins.__path__))
ومع ذلك ، يمكن أن تعمل بدون حزمة مكون الإضافي على أي حال ، جرب هذا:
import pkgutil
list(pkgutil.iter_modules(["plugins"]))
من الممكن أيضًا إنشاء حزمة موجودة فقط في وقت التشغيل:
import types
import sys
plugins = types.ModuleType("plugins")
plugins.__path__ = ["plugins"]
sys.modules["plugins"] = plugins
import plugins.testplugin
لكن هذا الاختراق الذي كان في الغالب للمتعة!
نصائح أخرى
إذا كان دليل الإضافات لا يحتوي على __init__.py
, ، إنها ليست حزمة ، لذلك عند إنشاء plugins.whatever
, ، يحذرك بيثون من أن مثل هذا الشيء لا ينبغي أن يكون موجودًا حقًا. (لا يمكن إنشاؤه بواسطة "import plugins.whatever
"بغض النظر عن طريقك.)
ايضا،
- لا تنقسم
/
, ، وهو أمر غير قابل للتحويل. يستخدمos.path.split
. - لا تستخدم
.split(".py")
للحصول على الاسم بدون التمديد ، وهو عربات التي تجرها الدواب. يستخدمos.path.splitext
. - لا تستخدم
getattr
مع سلسلة حرفية.getattr(plugin, "__init__")
تم تهجئةplugin.__init__
. - أنا في حيرة من أمري لماذا تتصل بمستوى الوحدة النمطية
__init__
وظيفة. هذا لا يبدو صحيحا. ربما تريد وظيفة "set_logger" أو أفضل ، لتشكيل فئة تأخذ مسجلًا. - لا تستخدم
L = L + some_other_list
لتمديد القائمة ، استخدمextend
الطريقة ، التي لديها أداء أفضل وأكثر احتمالا. - لا تسحق استثناءات غير معروفة
except Exception
. إذا لم تتمكن من التخطيط للقيام بشيء عاقل استجابةً لاستثناء ، فلا يمكن للبرنامج أن يستمر بصحبة.
المشكلة هنا هي مع النقطة ('.') في اسم الوحدة النمطية:
imp.load_module('plugins.'+name, f, file, desc)
لا تشمل ". بعد "الإضافات" ، أو بيثون سيعتقد أنه مسار الوحدة النمطية.
يمكنك محاولة إضافة البيان أدناه في بداية عبارات الاستيراد.
from __future__ import absolute_import
بيثون imp
تم تحديث الوثائق منذ الإجابة عليها. يعالج الآن هذه المشكلة على وجه التحديد في find_module()
طريقة.
هذه الوظيفة لا تتعامل مع أسماء الوحدات النمطية الهرمية (الأسماء التي تحتوي على نقاط). من أجل العثور على مساءً, هذا هو ، الجهاز الفرعي م من الحزمة ص, ، استعمال
find_module()
وload_module()
للعثور على الحزمة وتحميلها ص, ثم استخدمfind_module()
مع ال طريق تم تعيين الحجة إلىP.__path__
. متى ص نفسها لها اسم منقط ، ضع هذه الوصفة بشكل متكرر.
لاحظ أن P.__path__
هي بالفعل قائمة عند تزويدها find_module()
. لاحظ أيضًا ما find_module()
يقول الوثائق حول العثور على الحزم.
إذا كانت الوحدة عبارة عن حزمة ، ملف هو
None
, اسم المسار هو مسار الحزمة والبند الأخير في وصف Tuple هوPKG_DIRECTORY
.
لذلك من سؤال البروتوكول الاختياري ، لاستيراد البرنامج المساعد بدون RuntimeError
تحذيرات ، اتبع الإرشادات الواردة في وثائق بيثون المحدثة:
# first find and load the package assuming it is in
# the current working directory, '.'
f, file, desc = imp.find_module('plugins', ['.'])
pkg = imp.load_module('plugins', f, file, desc)
# then find the named plugin module using pkg.__path__
# and load the module using the dotted name
f, file, desc = imp.find_module(name, pkg.__path__)
plugin = imp.load_module('plugins.' + name, f, file, desc)