Refactor configpaths

- init configpaths earlier so logging can access it to store debug logs
- First step for more consistency across Gajim when looking up paths

  Recommended usage for the future:
  app.configpaths.get()
  configpaths.get()
This commit is contained in:
Philipp Hörist 2018-04-21 12:44:10 +02:00
parent 8e9c040acf
commit 4a26ecb12c
13 changed files with 117 additions and 110 deletions

View File

@ -36,7 +36,6 @@ from distutils.version import LooseVersion as V
from collections import namedtuple from collections import namedtuple
import gi import gi
import nbxmpp import nbxmpp
import hashlib
from gi.repository import GLib from gi.repository import GLib
@ -63,24 +62,26 @@ glog = logging.getLogger('gajim')
logger = None logger = None
# For backwards compatibility needed
# some plugins use that
gajimpaths = configpaths.gajimpaths gajimpaths = configpaths.gajimpaths
VCARD_PATH = gajimpaths['VCARD'] VCARD_PATH = configpaths.get('VCARD')
AVATAR_PATH = gajimpaths['AVATAR'] AVATAR_PATH = configpaths.get('AVATAR')
MY_EMOTS_PATH = gajimpaths['MY_EMOTS'] MY_EMOTS_PATH = configpaths.get('MY_EMOTS')
MY_ICONSETS_PATH = gajimpaths['MY_ICONSETS'] MY_ICONSETS_PATH = configpaths.get('MY_ICONSETS')
MY_MOOD_ICONSETS_PATH = gajimpaths['MY_MOOD_ICONSETS'] MY_MOOD_ICONSETS_PATH = configpaths.get('MY_MOOD_ICONSETS')
MY_ACTIVITY_ICONSETS_PATH = gajimpaths['MY_ACTIVITY_ICONSETS'] MY_ACTIVITY_ICONSETS_PATH = configpaths.get('MY_ACTIVITY_ICONSETS')
MY_CACERTS = gajimpaths['MY_CACERTS'] MY_CACERTS = configpaths.get('MY_CACERTS')
MY_PEER_CERTS_PATH = gajimpaths['MY_PEER_CERTS'] MY_PEER_CERTS_PATH = configpaths.get('MY_PEER_CERTS')
TMP = gajimpaths['TMP'] TMP = configpaths.get('TMP')
DATA_DIR = gajimpaths['DATA'] DATA_DIR = configpaths.get('DATA')
ICONS_DIR = gajimpaths['ICONS'] ICONS_DIR = configpaths.get('ICONS')
HOME_DIR = gajimpaths['HOME'] HOME_DIR = configpaths.get('HOME')
PLUGINS_DIRS = [gajimpaths['PLUGINS_BASE'], PLUGINS_DIRS = [configpaths.get('PLUGINS_BASE'),
gajimpaths['PLUGINS_USER']] configpaths.get('PLUGINS_USER')]
PLUGINS_CONFIG_DIR = gajimpaths['PLUGINS_CONFIG_DIR'] PLUGINS_CONFIG_DIR = configpaths.get('PLUGINS_CONFIG_DIR')
MY_CERT_DIR = gajimpaths['MY_CERT'] MY_CERT_DIR = configpaths.get('MY_CERT')
RecentGroupchat = namedtuple('RecentGroupchat', ['room', 'server', 'nickname']) RecentGroupchat = namedtuple('RecentGroupchat', ['room', 'server', 'nickname'])

View File

@ -188,9 +188,8 @@ def check_and_possibly_move_config():
vars['MY_MOOD_ICONSETS_PATH'] = app.MY_MOOD_ICONSETS_PATH vars['MY_MOOD_ICONSETS_PATH'] = app.MY_MOOD_ICONSETS_PATH
vars['MY_ACTIVITY_ICONSETS_PATH'] = app.MY_ACTIVITY_ICONSETS_PATH vars['MY_ACTIVITY_ICONSETS_PATH'] = app.MY_ACTIVITY_ICONSETS_PATH
from gajim.common import configpaths from gajim.common import configpaths
MY_DATA = configpaths.gajimpaths['MY_DATA'] MY_DATA = configpaths.get('MY_DATA')
MY_CONFIG = configpaths.gajimpaths['MY_CONFIG'] MY_CONFIG = configpaths.get('MY_CONFIG')
MY_CACHE = configpaths.gajimpaths['MY_CACHE']
if os.path.exists(LOG_DB_PATH): if os.path.exists(LOG_DB_PATH):
# File already exists # File already exists
@ -273,11 +272,11 @@ def check_and_possibly_create_paths():
VCARD_PATH = app.VCARD_PATH VCARD_PATH = app.VCARD_PATH
AVATAR_PATH = app.AVATAR_PATH AVATAR_PATH = app.AVATAR_PATH
from gajim.common import configpaths from gajim.common import configpaths
MY_DATA = configpaths.gajimpaths['MY_DATA'] MY_DATA = configpaths.get('MY_DATA')
MY_CONFIG = configpaths.gajimpaths['MY_CONFIG'] MY_CONFIG = configpaths.get('MY_CONFIG')
MY_CACHE = configpaths.gajimpaths['MY_CACHE'] MY_CACHE = configpaths.get('MY_CACHE')
XTLS_CERTS = configpaths.gajimpaths['MY_PEER_CERTS'] XTLS_CERTS = configpaths.get('MY_PEER_CERTS')
LOCAL_XTLS_CERTS = configpaths.gajimpaths['MY_CERT'] LOCAL_XTLS_CERTS = configpaths.get('MY_CERT')
PLUGINS_CONFIG_PATH = app.PLUGINS_CONFIG_DIR PLUGINS_CONFIG_PATH = app.PLUGINS_CONFIG_DIR

View File

@ -27,31 +27,13 @@ import sys
import tempfile import tempfile
from enum import Enum, unique from enum import Enum, unique
@unique @unique
class Type(Enum): class Type(Enum):
CONFIG = 0 CONFIG = 0
CACHE = 1 CACHE = 1
DATA = 2 DATA = 2
# Note on path and filename encodings:
#
# In general it is very difficult to do this correctly.
# We may pull information from environment variables, and what encoding that is
# in is anyone's guess. Any information we request directly from the file
# system will be in filesystemencoding, and (parts of) paths that we write in
# this source code will be in whatever encoding the source is in. (I hereby
# declare this file to be UTF-8 encoded.)
#
# To make things more complicated, modern Windows filesystems use UTF-16, but
# the API tends to hide this from us.
#
# I tried to minimize problems by passing Unicode strings to OS functions as
# much as possible. Hopefully this makes the function return an Unicode string
# as well. If not, we get an 8-bit string in filesystemencoding, which we can
# happily pass to functions that operate on files and directories, so we can
# just leave it as is. Since these paths are meant to be internal to Gajim and
# not displayed to the user, Unicode is not really necessary here.
def windowsify(s): def windowsify(s):
if os.name == 'nt': if os.name == 'nt':
@ -60,29 +42,41 @@ def windowsify(s):
def get(key): def get(key):
return gajimpaths[key] return _paths[key]
def set_separation(active: bool):
_paths.profile_separation = active
def set_profile(profile: str):
_paths.profile = profile
def set_config_root(config_root: str):
_paths.config_root = config_root
def init():
_paths.init()
class ConfigPaths: class ConfigPaths:
def __init__(self): def __init__(self):
# {'name': (type, path), } type can be Type.CONFIG, Type.CACHE, Type.DATA
# or None
self.paths = {} self.paths = {}
self.profile = ''
self.profile_separation = False
self.config_root = None
if os.name == 'nt': if os.name == 'nt':
try: try:
# Documents and Settings\[User Name]\Application Data\Gajim # Documents and Settings\[User Name]\Application Data\Gajim
# How are we supposed to know what encoding the environment
# variable 'appdata' is in? Assuming it to be in filesystem
# encoding.
self.config_root = self.cache_root = self.data_root = \ self.config_root = self.cache_root = self.data_root = \
os.path.join(os.environ['appdata'], 'Gajim') os.path.join(os.environ['appdata'], 'Gajim')
except KeyError: except KeyError:
# win9x, in cwd # win9x, in cwd
self.config_root = self.cache_root = self.data_root = '.' self.config_root = self.cache_root = self.data_root = '.'
else: # Unices else:
# Pass in an Unicode string, and hopefully get one back.
expand = os.path.expanduser expand = os.path.expanduser
base = os.getenv('XDG_CONFIG_HOME') base = os.getenv('XDG_CONFIG_HOME')
if base is None or base[0] != '/': if base is None or base[0] != '/':
@ -128,40 +122,45 @@ class ConfigPaths:
for key in self.paths.keys(): for key in self.paths.keys():
yield (key, self[key]) yield (key, self[key])
def init(self, root=None, profile='', profile_separation=False): def init(self):
if root is not None: if self.config_root is not None:
self.config_root = self.cache_root = self.data_root = root self.cache_root = self.data_root = self.config_root
self.init_profile(profile) self.add('CONFIG_ROOT', None, self.config_root)
self.add('CACHE_ROOT', None, self.cache_root)
self.add('DATA_ROOT', None, self.data_root)
if len(profile) > 0 and profile_separation: self.init_profile(self.profile)
profile = u'.' + profile
if len(self.profile) > 0 and self.profile_separation:
self.profile = u'.' + self.profile
else: else:
profile = '' self.profile = ''
d = {'LOG_DB': 'logs.db', 'MY_CACERTS': 'cacerts.pem', d = {'LOG_DB': 'logs.db', 'MY_CACERTS': 'cacerts.pem',
'MY_EMOTS': 'emoticons', 'MY_ICONSETS': 'iconsets', 'MY_EMOTS': 'emoticons', 'MY_ICONSETS': 'iconsets',
'MY_MOOD_ICONSETS': 'moods', 'MY_ACTIVITY_ICONSETS': 'activities', 'MY_MOOD_ICONSETS': 'moods', 'MY_ACTIVITY_ICONSETS': 'activities',
'PLUGINS_USER': 'plugins'} 'PLUGINS_USER': 'plugins'}
for name in d: for name in d:
d[name] += profile d[name] += self.profile
self.add(name, Type.DATA, windowsify(d[name])) self.add(name, Type.DATA, windowsify(d[name]))
if len(profile): if len(self.profile):
self.add('MY_DATA', Type.DATA, 'data.dir') self.add('MY_DATA', Type.DATA, 'data.dir')
else: else:
self.add('MY_DATA', Type.DATA, '') self.add('MY_DATA', Type.DATA, '')
d = {'CACHE_DB': 'cache.db', 'VCARD': 'vcards', d = {'CACHE_DB': 'cache.db',
'AVATAR': 'avatars'} 'VCARD': 'vcards',
'AVATAR': 'avatars'}
for name in d: for name in d:
d[name] += profile d[name] += self.profile
self.add(name, Type.CACHE, windowsify(d[name])) self.add(name, Type.CACHE, windowsify(d[name]))
if len(profile): if len(self.profile):
self.add('MY_CACHE', Type.CACHE, 'cache.dir') self.add('MY_CACHE', Type.CACHE, 'cache.dir')
else: else:
self.add('MY_CACHE', Type.CACHE, '') self.add('MY_CACHE', Type.CACHE, '')
if len(profile): if len(self.profile):
self.add('MY_CONFIG', Type.CONFIG, 'config.dir') self.add('MY_CONFIG', Type.CONFIG, 'config.dir')
else: else:
self.add('MY_CONFIG', Type.CONFIG, '') self.add('MY_CONFIG', Type.CONFIG, '')
@ -169,8 +168,8 @@ class ConfigPaths:
try: try:
self.add('TMP', None, tempfile.gettempdir()) self.add('TMP', None, tempfile.gettempdir())
except IOError as e: except IOError as e:
print('Error opening tmp folder: %s\nUsing %s' % (str(e), print('Error opening tmp folder: %s\nUsing %s' % (
os.path.expanduser('~')), file=sys.stderr) str(e), os.path.expanduser('~')), file=sys.stderr)
self.add('TMP', None, os.path.expanduser('~')) self.add('TMP', None, os.path.expanduser('~'))
def init_profile(self, profile): def init_profile(self, profile):
@ -193,4 +192,9 @@ class ConfigPaths:
self.add('PLUGINS_CONFIG_DIR', Type.CONFIG, pluginsconfdir) self.add('PLUGINS_CONFIG_DIR', Type.CONFIG, pluginsconfdir)
self.add('MY_CERT', Type.CONFIG, localcertsdir) self.add('MY_CERT', Type.CONFIG, localcertsdir)
gajimpaths = ConfigPaths()
_paths = ConfigPaths()
# For backwards compatibility needed
# some plugins use that
gajimpaths = _paths

View File

@ -853,8 +853,7 @@ def play_sound(event):
path_to_soundfile = app.config.get_per('soundevents', event, 'path') path_to_soundfile = app.config.get_per('soundevents', event, 'path')
play_sound_file(path_to_soundfile) play_sound_file(path_to_soundfile)
def check_soundfile_path(file_, dirs=(app.gajimpaths.data_root, def check_soundfile_path(file_, dirs=None):
app.DATA_DIR)):
""" """
Check if the sound file exists Check if the sound file exists
@ -863,6 +862,10 @@ app.DATA_DIR)):
(eg: ~/.gajim/sounds/, DATADIR/sounds...). (eg: ~/.gajim/sounds/, DATADIR/sounds...).
:return the path to file or None if it doesn't exists. :return the path to file or None if it doesn't exists.
""" """
if dirs is None:
dirs = [app.configpaths.get('DATA_ROOT'),
app.DATA_DIR]
if not file_: if not file_:
return None return None
elif os.path.exists(file_): elif os.path.exists(file_):
@ -874,8 +877,7 @@ app.DATA_DIR)):
return d return d
return None return None
def strip_soundfile_path(file_, dirs=(app.gajimpaths.data_root, def strip_soundfile_path(file_, dirs=None, abs=True):
app.DATA_DIR), abs=True):
""" """
Remove knowns paths from a sound file Remove knowns paths from a sound file
@ -888,6 +890,10 @@ app.DATA_DIR), abs=True):
if not file_: if not file_:
return None return None
if dirs is None:
dirs = [app.configpaths.get('DATA_ROOT'),
app.DATA_DIR]
name = os.path.basename(file_) name = os.path.basename(file_)
for d in dirs: for d in dirs:
d = os.path.join(d, 'sounds', name) d = os.path.join(d, 'sounds', name)

View File

@ -120,7 +120,7 @@ class JingleFileTransfer(JingleContent):
} }
if jingle_xtls.PYOPENSSL_PRESENT: if jingle_xtls.PYOPENSSL_PRESENT:
cert_name = os.path.join(configpaths.gajimpaths['MY_CERT'], cert_name = os.path.join(configpaths.get('MY_CERT'),
jingle_xtls.SELF_SIGNED_CERTIFICATE) jingle_xtls.SELF_SIGNED_CERTIFICATE)
if not (os.path.exists(cert_name + '.cert') if not (os.path.exists(cert_name + '.cert')
and os.path.exists(cert_name + '.pkey')): and os.path.exists(cert_name + '.pkey')):

View File

@ -42,12 +42,13 @@ from enum import IntEnum, unique
from gajim.common import exceptions from gajim.common import exceptions
from gajim.common import app from gajim.common import app
from gajim.common import configpaths
import sqlite3 as sqlite import sqlite3 as sqlite
LOG_DB_PATH = app.gajimpaths['LOG_DB'] LOG_DB_PATH = configpaths.get('LOG_DB')
LOG_DB_FOLDER, LOG_DB_FILE = os.path.split(LOG_DB_PATH) LOG_DB_FOLDER, LOG_DB_FILE = os.path.split(LOG_DB_PATH)
CACHE_DB_PATH = app.gajimpaths['CACHE_DB'] CACHE_DB_PATH = configpaths.get('CACHE_DB')
import logging import logging
log = logging.getLogger('gajim.c.logger') log = logging.getLogger('gajim.c.logger')

View File

@ -53,6 +53,7 @@ gi.require_version('Pango', '1.0')
from gi.repository import GLib, Gio, Gtk from gi.repository import GLib, Gio, Gtk
from gajim.common import i18n from gajim.common import i18n
from gajim.common import configpaths
from gajim.common import logging_helpers from gajim.common import logging_helpers
MIN_NBXMPP_VER = "0.6.4" MIN_NBXMPP_VER = "0.6.4"
@ -112,9 +113,6 @@ class GajimApplication(Gtk.Application):
self.connect('startup', self._startup) self.connect('startup', self._startup)
self.connect('activate', self._activate) self.connect('activate', self._activate)
self.profile = ''
self.config_path = None
self.profile_separation = False
self.interface = None self.interface = None
GLib.set_prgname('gajim') GLib.set_prgname('gajim')
@ -157,10 +155,6 @@ class GajimApplication(Gtk.Application):
sys.exit(1) sys.exit(1)
# Create and initialize Application Paths & Databases # Create and initialize Application Paths & Databases
from gajim.common import configpaths
configpaths.gajimpaths.init(
self.config_path, self.profile, self.profile_separation)
from gajim.common import app from gajim.common import app
from gajim.common import check_paths from gajim.common import check_paths
from gajim.common import exceptions from gajim.common import exceptions
@ -209,7 +203,7 @@ class GajimApplication(Gtk.Application):
# libintl = ctypes.cdll.LoadLibrary(libintl_path) # libintl = ctypes.cdll.LoadLibrary(libintl_path)
# libintl.bindtextdomain(APP, DIR) # libintl.bindtextdomain(APP, DIR)
# libintl.bind_textdomain_codeset(APP, 'UTF-8') # libintl.bind_textdomain_codeset(APP, 'UTF-8')
# plugins_locale_dir = os.path.join(common.configpaths.gajimpaths[ # plugins_locale_dir = os.path.join(common.configpaths[
# 'PLUGINS_USER'], 'locale').encode(locale.getpreferredencoding()) # 'PLUGINS_USER'], 'locale').encode(locale.getpreferredencoding())
# libintl.bindtextdomain('gajim_plugins', plugins_locale_dir) # libintl.bindtextdomain('gajim_plugins', plugins_locale_dir)
# libintl.bind_textdomain_codeset('gajim_plugins', 'UTF-8') # libintl.bind_textdomain_codeset('gajim_plugins', 'UTF-8')
@ -345,23 +339,22 @@ class GajimApplication(Gtk.Application):
def _handle_local_options(self, application, def _handle_local_options(self, application,
options: GLib.VariantDict) -> int: options: GLib.VariantDict) -> int:
# Parse all options that have to be executed before ::startup # Parse all options that have to be executed before ::startup
logging_helpers.init()
if options.contains('profile'): if options.contains('profile'):
# Incorporate profile name into application id # Incorporate profile name into application id
# to have a single app instance for each profile. # to have a single app instance for each profile.
profile = options.lookup_value('profile').get_string() profile = options.lookup_value('profile').get_string()
app_id = '%s.%s' % (self.get_application_id(), profile) app_id = '%s.%s' % (self.get_application_id(), profile)
self.set_application_id(app_id) self.set_application_id(app_id)
self.profile = profile configpaths.set_profile(profile)
if options.contains('separate'): if options.contains('separate'):
self.profile_separation = True configpaths.set_separation(True)
if options.contains('config-path'): if options.contains('config-path'):
self.config_path = options.lookup_value('config-path').get_string() path = options.lookup_value('config-path').get_string()
if options.contains('version'): configpaths.set_config_root(path)
from gajim import __version__
print(__version__) configpaths.init()
return 0 logging_helpers.init()
if options.contains('quiet'): if options.contains('quiet'):
logging_helpers.set_quiet() logging_helpers.set_quiet()
if options.contains('verbose'): if options.contains('verbose'):

View File

@ -107,11 +107,10 @@ from threading import Thread
from gajim.common import ged from gajim.common import ged
from gajim.common.caps_cache import muc_caps_cache from gajim.common.caps_cache import muc_caps_cache
from gajim.common.configpaths import gajimpaths from gajim.common import configpaths
config_filename = gajimpaths['CONFIG_FILE']
from gajim.common import optparser from gajim.common import optparser
parser = optparser.OptionsParser(config_filename) parser = optparser.OptionsParser(configpaths.get('CONFIG_FILE'))
import logging import logging
log = logging.getLogger('gajim.interface') log = logging.getLogger('gajim.interface')

View File

@ -67,8 +67,9 @@ def parseOpts():
config_path = parseOpts() config_path = parseOpts()
del parseOpts del parseOpts
import gajim.common.configpaths from gajim.common import configpaths
gajim.common.configpaths.gajimpaths.init(config_path) configpaths.set_config_root(config_path)
configpaths.init()
del config_path del config_path
from gajim.common import app from gajim.common import app
from gajim import gtkgui_helpers from gajim import gtkgui_helpers

View File

@ -48,8 +48,8 @@ import urllib
if __name__ == '__main__': if __name__ == '__main__':
from gajim.common import i18n from gajim.common import i18n
import gajim.common.configpaths from gajim.common import configpaths
gajim.common.configpaths.gajimpaths.init(None) configpaths.init()
from gajim.common import app from gajim.common import app
from gajim import gtkgui_helpers from gajim import gtkgui_helpers
from gajim.gtkgui_helpers import get_icon_pixmap from gajim.gtkgui_helpers import get_icon_pixmap

View File

@ -19,7 +19,7 @@
## along with Gajim. If not, see <http://www.gnu.org/licenses/>. ## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
## ##
from gajim.common.configpaths import gajimpaths from gajim.common import configpaths
import Crypto import Crypto
from gajim.common import crypto from gajim.common import crypto
@ -28,9 +28,10 @@ from gajim.common import exceptions
import os import os
import pickle import pickle
secrets_filename = gajimpaths['SECRETS_FILE'] secrets_filename = configpaths.get('SECRETS_FILE')
secrets_cache = None secrets_cache = None
class Secrets(): class Secrets():
def __init__(self, filename): def __init__(self, filename):
self.filename = filename self.filename = filename

View File

@ -37,8 +37,9 @@ def setup_env():
os.mkdir(configdir) os.mkdir(configdir)
os.mkdir(pluginsconfigdir) os.mkdir(pluginsconfigdir)
import gajim.common.configpaths from gajim.common import configpaths
gajim.common.configpaths.gajimpaths.init(configdir) configpaths.set_config_root(configdir)
configpaths.init()
# for some reason gajim.common.app needs to be imported before xmpppy? # for some reason gajim.common.app needs to be imported before xmpppy?
from gajim.common import app from gajim.common import app

View File

@ -49,8 +49,9 @@ if os.path.isdir(configdir):
os.mkdir(configdir) os.mkdir(configdir)
import gajim.common.configpaths from gajim.common import configpaths
gajim.common.configpaths.gajimpaths.init(configdir) configpaths.set_config_root(configdir)
configpaths.init()
# for some reason common.app needs to be imported before xmpppy? # for some reason common.app needs to be imported before xmpppy?
from gajim.common import app from gajim.common import app