Какая хорошая практика Python для импорта и предоставления дополнительных функций?
Я пишу часть программного обеспечения на github. Это в основном значок в трее с некоторыми дополнительными функциями. Я хочу предоставить рабочую часть кода, не требуя, чтобы пользователь установил то, что по существу зависит от дополнительных функций, и я фактически не хочу импортировать вещи, которые я не буду использовать, поэтому я думал, что такой код будет "хорошее решение":
---- IN LOADING FUNCTION ----
features = []
for path in sys.path:
if os.path.exists(os.path.join(path, 'pynotify')):
features.append('pynotify')
if os.path.exists(os.path.join(path, 'gnomekeyring.so')):
features.append('gnome-keyring')
#user dialog to ask for stuff
#notifications available, do you want them enabled?
dlg = ConfigDialog(features)
if not dlg.get_notifications():
features.remove('pynotify')
service_start(features ...)
---- SOMEWHERE ELSE ------
def service_start(features, other_config):
if 'pynotify' in features:
import pynotify
#use pynotify...
Однако есть некоторые проблемы. Если пользователь форматирует свой компьютер и устанавливает новейшую версию своей ОС и перераспределяет это приложение, функции внезапно исчезают без предупреждения. Решение должно представить это в окне конфигурации:
if 'pynotify' in features:
#gtk checkbox
else:
#gtk label reading "Get pynotify and enjoy notification pop ups!"
Но если это так, мак, откуда я знаю, что я не отправляю пользователя на дикую охоту за гусями, ища зависимость, которую они никогда не могут заполнить?
Вторая проблема:
if os.path.exists(os.path.join(path, 'gnomekeyring.so')):
вопрос. Могу ли я быть уверенным, что файл всегда называется gnomekeyring.so во всех дистрибутивах Linux?
Как другие люди проверяют эти функции? Проблема с базовым
try:
import pynotify
except:
pynotify = disabled
заключается в том, что код является глобальным, они могут быть завалены вокруг и даже если пользователь не хочет pynotify.... он загружен в любом случае.
Так что думают люди, это лучший способ решить эту проблему?
Ответы
Ответ 1
Возможно, вам стоит взглянуть на модуль imp, который в основном делает то, что вы делаете вручную выше. Поэтому вы можете сначала найти модуль с find_module()
, а затем загрузить его через load_module()
или просто импортировать его (после проверки конфигурации).
И btw, если использовать за исключением: я всегда добавлял бы к нему конкретное исключение (здесь ImportError), чтобы случайно не поймать несвязанные ошибки.
Ответ 2
Метод try:
не должен быть глобальным - его можно использовать в любой области, и поэтому модули могут быть "ленивыми" во время выполнения. Например:
def foo():
try:
import external_module
except ImportError:
pass
if external_module:
external_module.some_whizzy_feature()
else:
print "You could be using a whizzy feature right now, if you had external_module."
При запуске script не будет предпринята попытка загрузить external_module
. В первый раз вызывается foo()
, external_module
(если доступно) загружается и вставляется в локальную область функции. Последующие вызовы foo()
повторно вставляют external_module
в свою область действия без необходимости перезагрузки модуля.
В общем, лучше всего позволить Python обрабатывать логику импорта - это делалось некоторое время.: -)
Ответ 3
Один из способов решения проблемы разных зависимостей для разных функций - реализовать дополнительные функции в виде плагинов. Таким образом, у пользователя есть контроль над тем, какие функции активированы в приложении, но не отвечает за отслеживание самих зависимостей. Затем эта задача обрабатывается во время каждой установки плагина.