Detect dependencys only on demand

This allows us to import the app module without triggering
dependency detection

Also add is_installed() for checking if a dependency is installed and
disable_dependency() in case we dont want to use a dependency
This commit is contained in:
Philipp Hörist 2018-04-24 19:36:33 +02:00
parent 986898f69d
commit e265514d88
19 changed files with 204 additions and 165 deletions

View File

@ -384,9 +384,10 @@ class Account(Gtk.Box):
def set_activatable(self):
if self.account == app.ZEROCONF_ACC_NAME:
self.get_parent().set_activatable(app.HAVE_ZEROCONF)
self.get_parent().set_sensitive(app.HAVE_ZEROCONF)
if not app.HAVE_ZEROCONF:
zeroconf = app.is_installed('ZEROCONF')
self.get_parent().set_activatable(zeroconf)
self.get_parent().set_sensitive(zeroconf)
if not zeroconf:
self.get_parent().set_tooltip_text(
_('Please check if Avahi or Bonjour is installed.'))
@ -482,7 +483,7 @@ class GenericOptionPage(Gtk.Box):
switch.set_vexpand(False)
switch.set_valign(Gtk.Align.CENTER)
switch.set_halign(Gtk.Align.END)
if self.account == app.ZEROCONF_ACC_NAME and not app.HAVE_ZEROCONF:
if self.account == app.ZEROCONF_ACC_NAME and not app.is_installed('ZEROCONF'):
switch.set_sensitive(False)
switch.set_active(False)

View File

@ -394,7 +394,7 @@ class ChatControl(ChatControlBase):
# Jingle detection
if self.contact.supports(NS_JINGLE_ICE_UDP) and \
app.HAVE_FARSTREAM and self.contact.resource:
app.is_installed('FARSTREAM') and self.contact.resource:
self.audio_available = self.contact.supports(NS_JINGLE_RTP_AUDIO)
self.video_available = self.contact.supports(NS_JINGLE_RTP_VIDEO)
else:

View File

@ -63,7 +63,7 @@ from gajim.command_system.implementation.middleware import CommandTools
from gajim.command_system.implementation import standard
from gajim.command_system.implementation import execute
if app.HAVE_SPELL:
if app.is_installed('GSPELL'):
from gi.repository import Gspell
@ -487,7 +487,7 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
Gtk.IconSize.MENU)
def set_speller(self):
if not app.HAVE_SPELL or not app.config.get('use_speller'):
if not app.is_installed('GSPELL') or not app.config.get('use_speller'):
return
gspell_lang = self.get_speller_language()

View File

@ -11,6 +11,7 @@
## Copyright (C) 2007-2008 Brendan Taylor <whateley AT gmail.com>
## Stephan Erb <steve-e AT h3c.de>
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
## Copyright (C) 2018 Philipp Hörist <philipp @ hoerist.com>
##
## This file is part of Gajim.
##
@ -34,10 +35,8 @@ import locale
import uuid
from distutils.version import LooseVersion as V
from collections import namedtuple
import gi
import nbxmpp
from gi.repository import GLib
import nbxmpp
from gajim.common import config as c_config
from gajim.common import configpaths
@ -58,8 +57,6 @@ ged = ged_module.GlobalEventsDispatcher() # Global Events Dispatcher
nec = None # Network Events Controller
plugin_manager = None # Plugins Manager
glog = logging.getLogger('gajim')
logger = None
# For backwards compatibility needed
@ -152,122 +149,6 @@ ZEROCONF_ACC_NAME = 'Local'
idlequeue = None
socks5queue = None
HAVE_ZEROCONF = True
try:
__import__('avahi')
except ImportError:
try:
__import__('pybonjour')
except Exception: # Linux raises ImportError, Windows raises WindowsError
HAVE_ZEROCONF = False
HAVE_PYCRYPTO = True
try:
__import__('Crypto')
except ImportError:
HAVE_PYCRYPTO = False
HAVE_GPG = True
GPG_BINARY = 'gpg'
try:
import gnupg
'''
We need https://pypi.python.org/pypi/python-gnupg
but https://pypi.python.org/pypi/gnupg shares the same package name.
It cannot be used as a drop-in replacement.
We test with a version check if python-gnupg is installed as it is
on a much lower version number than gnupg
Also we need at least python-gnupg 0.3.8
'''
v_gnupg = gnupg.__version__
if V(v_gnupg) < V('0.3.8') or V(v_gnupg) > V('1.0.0'):
glog.info('Gajim needs python-gnupg >= 0.3.8')
HAVE_GPG = False
except ImportError:
HAVE_GPG = False
else:
import subprocess
def test_gpg(binary='gpg'):
if os.name == 'nt':
gpg_cmd = binary + ' -h >nul 2>&1'
else:
gpg_cmd = binary + ' -h >/dev/null 2>&1'
if subprocess.call(gpg_cmd, shell=True):
return False
return True
if test_gpg(binary='gpg2'):
GPG_BINARY = 'gpg2'
if not test_gpg(binary='gpg'):
HAVE_GPG = False
HAVE_FARSTREAM = True
try:
if os.name == 'nt':
os.environ['FS_PLUGIN_PATH'] = 'gtk\\lib\\farstream-0.1'
os.environ['GST_PLUGIN_PATH'] = 'gtk\\lib\\gstreamer-0.10'
gi.require_version('Farstream', '0.2')
from gi.repository import Farstream
gi.require_version('Gst', '1.0')
from gi.repository import Gst
from gi.repository import GLib
try:
Gst.init(None)
conference = Gst.ElementFactory.make('fsrtpconference', None)
session = conference.new_session(Farstream.MediaType.AUDIO)
del session
del conference
except Exception as e:
glog.info(e)
HAVE_FARSTREAM = False
except (ImportError, ValueError):
HAVE_FARSTREAM = False
HAVE_GEOCLUE = True
try:
gi.require_version('Geoclue', '2.0')
from gi.repository import Geoclue
except (ImportError, ValueError):
HAVE_GEOCLUE = False
HAVE_UPNP_IGD = True
try:
gi.require_version('GUPnPIgd', '1.0')
from gi.repository import GUPnPIgd
gupnp_igd = GUPnPIgd.SimpleIgd()
except ValueError:
HAVE_UPNP_IGD = False
HAVE_PYCURL = True
try:
__import__('pycurl')
except ImportError:
HAVE_PYCURL = False
try:
from gajim.common import sleepy
if sleepy.SUPPORTED:
HAVE_IDLE = True
except Exception:
glog.info(_('Unable to load idle module'))
HAVE_IDLE = False
HAVE_SPELL = False
try:
spell_log = logging.getLogger('gajim.speller')
gi.require_version('Gspell', '1')
from gi.repository import Gspell
langs = Gspell.language_get_available()
for lang in langs:
spell_log.info('%s (%s) dict available',
lang.get_name(), lang.get_code())
if langs:
HAVE_SPELL = True
else:
spell_log.info('No dicts available')
except (ImportError, ValueError):
pass
gajim_identity = {'type': 'pc', 'category': 'client', 'name': 'Gajim'}
gajim_common_features = [nbxmpp.NS_BYTESTREAM, nbxmpp.NS_SI, nbxmpp.NS_FILE,
nbxmpp.NS_MUC, nbxmpp.NS_MUC_USER, nbxmpp.NS_MUC_ADMIN, nbxmpp.NS_MUC_OWNER,
@ -287,6 +168,161 @@ gajim_optional_features = {}
# Capabilities hash per account
caps_hash = {}
_dependencies = {
'AVAHI': False,
'PYBONJOUR': False,
'PYCRYPTO': False,
'PYGPG': False,
'GPG_BINARY': False,
'FARSTREAM': False,
'GEOCLUE': False,
'UPNP': False,
'PYCURL': False,
'GSPELL': False,
'IDLE': False,
}
def is_installed(dependency):
if dependency == 'GPG':
# Alias for checking python-gnupg and the GPG binary
return _dependencies['PYGPG'] and _dependencies['GPG_BINARY']
if dependency == 'ZEROCONF':
# Alias for checking zeroconf libs
return _dependencies['AVAHI'] or _dependencies['PYBONJOUR']
return _dependencies[dependency]
def disable_dependency(dependency):
_dependencies[dependency] = False
def detect_dependencies():
import gi
# ZEROCONF
try:
if os.name == 'nt':
import pybonjour
_dependencies['PYBONJOUR'] = True
else:
import avahi
_dependencies['AVAHI'] = True
except Exception:
pass
# PYCRYPTO
try:
import Crypto
_dependencies['PYCRYPTO'] = True
except ImportError:
pass
# python-gnupg
try:
import gnupg
'''
We need https://pypi.python.org/pypi/python-gnupg
but https://pypi.python.org/pypi/gnupg shares the same package name.
It cannot be used as a drop-in replacement.
We test with a version check if python-gnupg is installed as it is
on a much lower version number than gnupg
Also we need at least python-gnupg 0.3.8
'''
v_gnupg = gnupg.__version__
if V(v_gnupg) < V('0.3.8') or V(v_gnupg) > V('1.0.0'):
log('gajim').info('Gajim needs python-gnupg >= 0.3.8')
raise ImportError
_dependencies['PYGPG'] = True
except ImportError:
pass
# GPG BINARY
import subprocess
def test_gpg(binary='gpg'):
if os.name == 'nt':
gpg_cmd = binary + ' -h >nul 2>&1'
else:
gpg_cmd = binary + ' -h >/dev/null 2>&1'
if subprocess.call(gpg_cmd, shell=True):
return False
return True
if test_gpg(binary='gpg2'):
_dependencies['GPG_BINARY'] = 'gpg2'
elif test_gpg(binary='gpg'):
_dependencies['GPG_BINARY'] = 'gpg'
# FARSTREAM
try:
if os.name == 'nt':
os.environ['FS_PLUGIN_PATH'] = 'gtk\\lib\\farstream-0.1'
os.environ['GST_PLUGIN_PATH'] = 'gtk\\lib\\gstreamer-0.10'
gi.require_version('Farstream', '0.2')
from gi.repository import Farstream
gi.require_version('Gst', '1.0')
from gi.repository import Gst
try:
Gst.init(None)
conference = Gst.ElementFactory.make('fsrtpconference', None)
session = conference.new_session(Farstream.MediaType.AUDIO)
except Exception as error:
log('gajim').info(error)
_dependencies['FARSTREAM'] = True
except (ImportError, ValueError):
pass
# GEOCLUE
try:
gi.require_version('Geoclue', '2.0')
from gi.repository import Geoclue
_dependencies['GEOCLUE'] = True
except (ImportError, ValueError):
pass
# UPNP
try:
gi.require_version('GUPnPIgd', '1.0')
from gi.repository import GUPnPIgd
gupnp_igd = GUPnPIgd.SimpleIgd()
_dependencies['UPNP'] = True
except ValueError:
pass
# PYCURL
try:
import pycurl
_dependencies['PYCURL'] = True
except ImportError:
pass
# IDLE
try:
from gajim.common import sleepy
if sleepy.SUPPORTED:
_dependencies['IDLE'] = True
except Exception:
pass
# GSPELL
try:
gi.require_version('Gspell', '1')
from gi.repository import Gspell
langs = Gspell.language_get_available()
for lang in langs:
log('gajim').info('%s (%s) dict available',
lang.get_name(), lang.get_code())
if langs:
_dependencies['GSPELL'] = True
except (ImportError, ValueError):
pass
# Print results
for dep, val in _dependencies.items():
log('gajim').info('%-13s %s', dep, val)
def get_gpg_binary():
return _dependencies['GPG_BINARY']
def get_an_id():
return str(uuid.uuid4())
@ -556,8 +592,9 @@ def get_priority(account, show):
return prio
def log(domain):
root = 'gajim.'
return logging.getLogger(root + domain)
if domain != 'gajim':
domain = 'gajim.%s' % domain
return logging.getLogger(domain)
def prefers_app_menu():
if sys.platform == 'darwin':

View File

@ -136,7 +136,7 @@ class CommonConnection:
self.server_resource = self._compute_resource()
self.gpg = None
self.USE_GPG = False
if app.HAVE_GPG:
if app.is_installed('GPG'):
self.USE_GPG = True
self.gpg = gpg.GnuPG()
self.status = ''
@ -598,7 +598,7 @@ class CommonConnection:
self.old_show = show
self.on_purpose = False
self.server_resource = self._compute_resource()
if app.HAVE_GPG:
if app.is_installed('GPG'):
self.USE_GPG = True
self.gpg = gpg.GnuPG()
app.nec.push_incoming_event(BeforeChangeShowEvent(None,
@ -638,7 +638,7 @@ class CommonConnection:
self.connected = app.SHOW_LIST.index(show)
idle_time = None
if auto:
if app.HAVE_IDLE and app.config.get('autoaway'):
if app.is_installed('IDLE') and app.config.get('autoaway'):
idle_sec = int(app.interface.sleeper.getIdleSec())
idle_time = time.strftime('%Y-%m-%dT%H:%M:%SZ',
time.gmtime(time.time() - idle_sec))
@ -898,7 +898,7 @@ class Connection(CommonConnection, ConnectionHandlers):
app.nec.push_incoming_event(AccountNotCreatedEvent(
None, conn=self, reason=reason))
return
if app.HAVE_GPG:
if app.is_installed('GPG'):
self.USE_GPG = True
self.gpg = gpg.GnuPG()
app.nec.push_incoming_event(
@ -2755,7 +2755,7 @@ class Connection(CommonConnection, ConnectionHandlers):
p = self.add_sha(p, ptype != 'unavailable')
self.add_lang(p)
if auto:
if app.HAVE_IDLE and app.config.get('autoaway'):
if app.is_installed('IDLE') and app.config.get('autoaway'):
idle_sec = int(app.interface.sleeper.getIdleSec())
idle_time = time.strftime('%Y-%m-%dT%H:%M:%SZ',
time.gmtime(time.time() - idle_sec))

View File

@ -1712,7 +1712,7 @@ ConnectionHTTPUpload):
def _nec_last_request_received(self, obj):
if obj.conn.name != self.name:
return
if app.HAVE_IDLE and app.config.get_per('accounts', self.name,
if app.is_installed('IDLE') and app.config.get_per('accounts', self.name,
'send_idle_time'):
iq_obj = obj.stanza.buildReply('result')
qp = iq_obj.setQuery()

View File

@ -26,14 +26,14 @@ import os
import logging
from gajim.common import app
if app.HAVE_GPG:
if app.is_installed('GPG'):
import gnupg
gnupg.logger = logging.getLogger('gajim.c.gnupg')
class GnuPG(gnupg.GPG):
def __init__(self):
use_agent = app.config.get('use_gpg_agent')
gnupg.GPG.__init__(self, gpgbinary=app.GPG_BINARY, use_agent=use_agent)
gnupg.GPG.__init__(self, gpgbinary=app.get_gpg_binary(), use_agent=use_agent)
encoding = app.config.get('pgp_encoding')
if encoding:
self.encoding = encoding

View File

@ -732,7 +732,7 @@ def parse_datetime(timestring, check_utc=False, convert='utc', epoch=False):
return None
from gajim.common import app
if app.HAVE_PYCURL:
if app.is_installed('PYCURL'):
import pycurl
from io import StringIO
@ -1360,13 +1360,13 @@ def update_optional_features(account = None):
app.gajim_optional_features[a].append(nbxmpp.NS_CHATSTATES)
if not app.config.get('ignore_incoming_xhtml'):
app.gajim_optional_features[a].append(nbxmpp.NS_XHTML_IM)
if app.HAVE_PYCRYPTO \
if app.is_installed('PYCRYPTO') \
and app.config.get_per('accounts', a, 'enable_esessions'):
app.gajim_optional_features[a].append(nbxmpp.NS_ESESSION)
if app.config.get_per('accounts', a, 'answer_receipts'):
app.gajim_optional_features[a].append(nbxmpp.NS_RECEIPTS)
app.gajim_optional_features[a].append(nbxmpp.NS_JINGLE)
if app.HAVE_FARSTREAM:
if app.is_installed('FARSTREAM'):
app.gajim_optional_features[a].append(nbxmpp.NS_JINGLE_RTP)
app.gajim_optional_features[a].append(nbxmpp.NS_JINGLE_RTP_AUDIO)
app.gajim_optional_features[a].append(nbxmpp.NS_JINGLE_RTP_VIDEO)
@ -1545,7 +1545,7 @@ def _get_img_proxy(attrs, proxy):
Download an image through a proxy. This function should be launched in a
separated thread.
"""
if not app.HAVE_PYCURL:
if not app.is_installed('PYCURL'):
return '', _('PyCURL is not installed')
mem, alt, max_size = '', '', 2*1024*1024
if 'max_size' in attrs:

View File

@ -37,7 +37,7 @@ from gajim.common import app
from gajim.common.jingle_session import JingleSession, JingleStates
from gajim.common.jingle_ft import JingleFileTransfer
from gajim.common.jingle_transport import JingleTransportSocks5, JingleTransportIBB
if app.HAVE_FARSTREAM:
if app.is_installed('FARSTREAM'):
from gajim.common.jingle_rtp import JingleAudio, JingleVideo
logger = logging.getLogger('gajim.c.jingle')

View File

@ -416,7 +416,7 @@ class ConnectionSocks5Bytestream(ConnectionBytestream):
self._add_streamhosts_to_query(query, sender, port, additional_hosts)
def _add_upnp_igd_as_streamhost_to_query(self, query, file_props, iq):
if not app.HAVE_UPNP_IGD:
if not app.is_installed('UPNP'):
self.connection.send(iq)
return
@ -487,7 +487,7 @@ class ConnectionSocks5Bytestream(ConnectionBytestream):
def no_upnp_reply():
log.debug('Got not GUPnP-IGD answer')
# stop trying to use it
app.HAVE_UPNP_IGD = False
app.disable_dependency('UPNP')
self.no_gupnp_reply_id = 0
self.connection.send(iq)
cleanup_gupnp()

View File

@ -40,7 +40,7 @@ from gajim.common import crypto
import logging
log = logging.getLogger('gajim.c.stanza_session')
if app.HAVE_PYCRYPTO:
if app.is_installed('PYCRYPTO'):
from Crypto.Cipher import AES
from Crypto.PublicKey import RSA

View File

@ -67,7 +67,7 @@ try:
except (ImportError, ValueError):
HAS_GST = False
if app.HAVE_SPELL:
if app.is_installed('GSPELL'):
from gi.repository import Gspell
#---------- PreferencesWindow class -------------#
@ -179,7 +179,7 @@ class PreferencesWindow:
self.xml.get_object('xhtml_checkbutton').set_active(st)
# use speller
if app.HAVE_SPELL:
if app.is_installed('GSPELL'):
st = app.config.get('use_speller')
self.xml.get_object('speller_checkbutton').set_active(st)
else:
@ -400,7 +400,7 @@ class PreferencesWindow:
if config == value:
combobox.set_active(index)
if HAS_GST and app.HAVE_FARSTREAM:
if HAS_GST and app.is_installed('FARSTREAM'):
create_av_combobox('audio_input', AudioInputManager().get_devices())
create_av_combobox('audio_output', AudioOutputManager().get_devices(
))

View File

@ -62,7 +62,7 @@ from gajim.common.caps_cache import muc_caps_cache
from gajim.common.exceptions import GajimGeneralException
from gajim.common.connection_handlers_events import MessageOutgoingEvent
if app.HAVE_SPELL:
if app.is_installed('GSPELL'):
from gi.repository import Gspell
import logging
@ -3322,7 +3322,7 @@ class SingleMessageWindow:
else:
self.to_entry.set_text(to)
if app.config.get('use_speller') and app.HAVE_SPELL and action == 'send':
if app.config.get('use_speller') and app.is_installed('GSPELL') and action == 'send':
lang = app.config.get('speller_language')
gspell_lang = Gspell.language_lookup(lang)
if gspell_lang is None:

View File

@ -140,14 +140,14 @@ class FeaturesWindow:
self.desc_label.set_text(text)
def zeroconf_available(self):
return app.HAVE_ZEROCONF
return app.is_installed('ZEROCONF')
def dbus_available(self):
from gajim.common import dbus_support
return dbus_support.supported
def gpg_available(self):
return app.HAVE_GPG
return app.is_installed('GPG')
def some_keyring_available(self):
if os.name == 'nt':
@ -160,14 +160,14 @@ class FeaturesWindow:
return True
def speller_available(self):
return app.HAVE_SPELL
return app.is_installed('GSPELL')
def idle_available(self):
from gajim.common import sleepy
return sleepy.SUPPORTED
def pycrypto_available(self):
return app.HAVE_PYCRYPTO
return app.is_installed('PYCRYPTO')
def docutils_available(self):
try:
@ -177,8 +177,8 @@ class FeaturesWindow:
return True
def farstream_available(self):
return app.HAVE_FARSTREAM
return app.is_installed('FARSTREAM')
def gupnp_igd_available(self):
return app.HAVE_UPNP_IGD
return app.is_installed('UPNP')

View File

@ -156,6 +156,7 @@ class GajimApplication(Gtk.Application):
# Create and initialize Application Paths & Databases
from gajim.common import app
app.detect_dependencies()
configpaths.create_paths()
from gajim.common import exceptions
from gajim.common import logger

View File

@ -56,7 +56,7 @@ from gajim.common import events
from gajim.music_track_listener import MusicTrackListener
if app.HAVE_GEOCLUE:
if app.is_installed('GEOCLUE'):
from gajim.common import location_listener
from gajim import gtkgui_helpers
@ -1138,7 +1138,7 @@ class Interface:
app.config.get_per('accounts', account, 'publish_tune')):
self.enable_music_listener()
# enable location listener
if (obj.conn.pep_supported and app.HAVE_GEOCLUE and
if (obj.conn.pep_supported and app.is_installed('GEOCLUE') and
app.config.get_per('accounts', account, 'publish_location')):
location_listener.enable()
@ -2881,7 +2881,7 @@ class Interface:
self.create_zeroconf_default_config()
if app.config.get_per('accounts', app.ZEROCONF_ACC_NAME, 'active') \
and app.HAVE_ZEROCONF:
and app.is_installed('ZEROCONF'):
app.connections[app.ZEROCONF_ACC_NAME] = \
connection_zeroconf.ConnectionZeroconf(app.ZEROCONF_ACC_NAME)
for account in app.config.get_per('accounts'):

View File

@ -30,7 +30,7 @@ from gi.repository import Pango
from gajim.common import app
from gajim import gtkgui_helpers
if app.HAVE_SPELL:
if app.is_installed('GSPELL'):
from gi.repository import Gspell
@ -133,7 +133,7 @@ class MessageTextView(Gtk.TextView):
self.toggle_speller(False)
def toggle_speller(self, activate):
if app.HAVE_SPELL and app.config.get('use_speller'):
if app.is_installed('GSPELL') and app.config.get('use_speller'):
spell_view = Gspell.TextView.get_from_gtk_text_view(self)
spell_view.set_inline_spell_checking(activate)

View File

@ -598,4 +598,4 @@ class GPGOption(DialogOption):
def set_activatable(self, name, value):
active = self.account in app.connections
self.get_parent().set_activatable(app.HAVE_GPG and active)
self.get_parent().set_activatable(app.is_installed('GPG') and active)

View File

@ -64,7 +64,7 @@ from gajim.common import app
from gajim.common import helpers
from gajim.common.exceptions import GajimGeneralException
from gajim.common import i18n
if app.HAVE_GEOCLUE:
if app.is_installed('GEOCLUE'):
from gajim.common import location_listener
from gajim.common import ged
from gajim.message_window import MessageWindowMgr
@ -4940,7 +4940,7 @@ class RosterWindow:
item = Gtk.CheckMenuItem(_('Publish Location'))
pep_submenu.append(item)
if not app.HAVE_GEOCLUE:
if not app.is_installed('GEOCLUE'):
item.set_sensitive(False)
else:
activ = app.config.get_per('accounts', account,