The problem is really that the paths get munged into strings in the
system codepage, losing or misinterpreting most Unicode. Python's os
module is smart enough to encode the unicode to the file system
encoding, but the bsddb module needs a little help.
Provide a new function, constfunc.get_env_var(name, default=None) to
cleanly import Unicode environment variables in Windows and use it in
place of all instances of foo = os.environ['BAR] or foo =
os.environ.get('BAR').
Os path functions are smart enough to convert unicode to the file system
encoding on their own, but Db functions aren't, so provide an _encode
function in gen.db.write.py and apply it where a path is being passed to
DBEnv.open().
Also convert paths from the UI to unicode from 'utf8' rather than
sysfilesystemencoding. The latter happens to be correct most of the time
on Linux and OSX but is wrong on Windows.
450 lines
15 KiB
Python
450 lines
15 KiB
Python
#
|
|
# Gramps - a GTK+/GNOME based genealogy program
|
|
#
|
|
# Copyright (C) 2000-2006 Donald N. Allingham
|
|
# Copyright (C) 2009 Benny Malengier
|
|
# Copyright (C) 2009-2010 Stephen George
|
|
# Copyright (C) 2010 Doug Blank <doug.blank@gmail.com>
|
|
# Copyright (C) 2011 Paul Franklin
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
#
|
|
|
|
# $Id$
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# Python modules
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
from __future__ import print_function, unicode_literals
|
|
|
|
import sys
|
|
## hack to avoid mentioning 'utf8' encoding everywhere unicode or str is is used
|
|
if sys.version_info[0] < 3:
|
|
reload(sys)
|
|
sys.setdefaultencoding('utf8')
|
|
##
|
|
import os
|
|
import signal
|
|
|
|
import logging
|
|
|
|
LOG = logging.getLogger(".")
|
|
|
|
from subprocess import Popen, PIPE
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# GRAMPS modules
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
from .gen.const import APP_GRAMPS, USER_DIRLIST, HOME_DIR
|
|
from .version import VERSION_TUPLE
|
|
from .gen.constfunc import win, get_env_var
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# Setup logging
|
|
#
|
|
# Ideally, this needs to be done before any Gramps modules are
|
|
# imported, so that any code that is executed as the modules are
|
|
# imported can log errors or warnings. const and constfunc have to be
|
|
# imported before this code is executed because they are used in this
|
|
# code. That unfortunately initializes GrampsLocale, so it has its own
|
|
# logging setup during initialization.
|
|
#-------------------------------------------------------------------------
|
|
"""Setup basic logging support."""
|
|
|
|
# Setup a formatter
|
|
form = logging.Formatter(fmt="%(asctime)s.%(msecs).03d: %(levelname)s: "
|
|
"%(filename)s: line %(lineno)d: %(message)s",
|
|
datefmt='%Y-%m-%d %H:%M:%S')
|
|
|
|
# Create the log handlers
|
|
if win():
|
|
# If running in GUI mode redirect stdout and stderr to log file
|
|
if hasattr(sys.stdout, "fileno") and sys.stdout.fileno() < 0:
|
|
logfile = os.path.join(HOME_DIR,
|
|
"Gramps%s%s.log") % (VERSION_TUPLE[0],
|
|
VERSION_TUPLE[1])
|
|
# We now carry out the first step in build_user_paths(), to make sure
|
|
# that the user home directory is available to store the log file. When
|
|
# build_user_paths() is called, the call is protected by a try...except
|
|
# block, and any failure will be logged. However, if the creation of the
|
|
# user directory fails here, there is no way to report the failure,
|
|
# because stdout/stderr are not available, and neither is the logfile.
|
|
if os.path.islink(HOME_DIR):
|
|
pass # ok
|
|
elif not os.path.isdir(HOME_DIR):
|
|
os.makedirs(HOME_DIR)
|
|
sys.stdout = sys.stderr = open(logfile, "w")
|
|
stderrh = logging.StreamHandler(sys.stderr)
|
|
stderrh.setFormatter(form)
|
|
stderrh.setLevel(logging.DEBUG)
|
|
|
|
# Setup the base level logger, this one gets
|
|
# everything.
|
|
l = logging.getLogger()
|
|
l.setLevel(logging.WARNING)
|
|
l.addHandler(stderrh)
|
|
|
|
# put a hook on to catch any completely unhandled exceptions.
|
|
def exc_hook(type, value, tb):
|
|
if type == KeyboardInterrupt:
|
|
# Ctrl-C is not a bug.
|
|
return
|
|
if type == IOError:
|
|
# strange Windows logging error on close
|
|
return
|
|
import traceback
|
|
LOG.error("Unhandled exception\n" +
|
|
"".join(traceback.format_exception(type, value, tb)))
|
|
|
|
sys.excepthook = exc_hook
|
|
|
|
from .gen.mime import mime_type_is_defined
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# Instantiate Localization
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
|
|
from .gen.const import GRAMPS_LOCALE as glocale
|
|
_ = glocale.translation.gettext
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# Minimum version check
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
|
|
MIN_PYTHON_VERSION = (2, 7, 0, '', 0)
|
|
if not sys.version_info >= MIN_PYTHON_VERSION :
|
|
logging.warning(_("Your Python version does not meet the "
|
|
"requirements. At least python %(v1)d.%(v2)d.%(v3)d is needed to"
|
|
" start Gramps.\n\n"
|
|
"Gramps will terminate now.") % {
|
|
'v1': MIN_PYTHON_VERSION[0],
|
|
'v2': MIN_PYTHON_VERSION[1],
|
|
'v3': MIN_PYTHON_VERSION[2]})
|
|
sys.exit(1)
|
|
|
|
if sys.version_info[0] >= 3:
|
|
#check if bsddb3 is installed
|
|
try:
|
|
import bsddb3
|
|
except ImportError:
|
|
logging.warning(_("\nYou don't have the python bsddb3 package installed."
|
|
" This package is needed to start Gramps.\n\n"
|
|
"Gramps will terminate now."))
|
|
sys.exit(1)
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# gramps libraries
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
try:
|
|
signal.signal(signal.SIGCHLD, signal.SIG_DFL)
|
|
except:
|
|
pass
|
|
|
|
args = sys.argv
|
|
|
|
def build_user_paths():
|
|
""" check/make user-dirs on each Gramps session"""
|
|
for path in USER_DIRLIST:
|
|
if os.path.islink(path):
|
|
pass # ok
|
|
elif not os.path.isdir(path):
|
|
os.makedirs(path)
|
|
|
|
def show_settings():
|
|
"""
|
|
Shows settings of all of the major components.
|
|
"""
|
|
py_str = '%d.%d.%d' % sys.version_info[:3]
|
|
try:
|
|
from gi.repository import Gtk
|
|
try:
|
|
gtkver_str = '%d.%d.%d' % (Gtk.get_major_version(),
|
|
Gtk.get_minor_version(), Gtk.get_micro_version())
|
|
except : # any failure to 'get' the version
|
|
gtkver_str = 'unknown version'
|
|
except ImportError:
|
|
gtkver_str = 'not found'
|
|
# no DISPLAY is a RuntimeError in an older pygtk (e.g. 2.17 in Fedora 14)
|
|
except RuntimeError:
|
|
gtkver_str = 'DISPLAY not set'
|
|
#exept TypeError: To handle back formatting on version split
|
|
|
|
try:
|
|
from gi.repository import GObject
|
|
try:
|
|
pygobjectver_str = '%d.%d.%d' % GObject.pygobject_version
|
|
except :# any failure to 'get' the version
|
|
pygobjectver_str = 'unknown version'
|
|
|
|
except ImportError:
|
|
pygobjectver_str = 'not found'
|
|
|
|
try:
|
|
from gi.repository import Pango
|
|
try:
|
|
pangover_str = Pango.version_string()
|
|
except :# any failure to 'get' the version
|
|
pangover_str = 'unknown version'
|
|
|
|
except ImportError:
|
|
pangover_str = 'not found'
|
|
|
|
try:
|
|
import cairo
|
|
try:
|
|
pycairover_str = '%d.%d.%d' % cairo.version_info
|
|
cairover_str = cairo.cairo_version_string()
|
|
except :# any failure to 'get' the version
|
|
pycairover_str = 'unknown version'
|
|
cairover_str = 'unknown version'
|
|
|
|
except ImportError:
|
|
pycairover_str = 'not found'
|
|
cairover_str = 'not found'
|
|
|
|
try:
|
|
from gi import Repository
|
|
repository = Repository.get_default()
|
|
if repository.enumerate_versions("OsmGpsMap"):
|
|
from gi.repository import OsmGpsMap as osmgpsmap
|
|
try:
|
|
osmgpsmap_str = osmgpsmap._version
|
|
except :# any failure to 'get' the version
|
|
osmgpsmap_str = 'unknown version'
|
|
else:
|
|
osmgpsmap_str = 'not found'
|
|
|
|
except ImportError:
|
|
osmgpsmap_str = 'not found'
|
|
|
|
try:
|
|
from gi import Repository
|
|
repository = Repository.get_default()
|
|
if repository.enumerate_versions("GExiv2"):
|
|
from gi.repository import GExiv2
|
|
try:
|
|
gexiv2_str = GExiv2._version
|
|
except: # any failure to 'get' the version
|
|
gexiv2_str = 'unknown version'
|
|
else:
|
|
gexiv2_str = 'not found'
|
|
|
|
except ImportError:
|
|
gexiv2_str = 'not found'
|
|
|
|
try:
|
|
import PyICU
|
|
try:
|
|
pyicu_str = PyICU.VERSION
|
|
icu_str = PyICU.ICU_VERSION
|
|
except: # any failure to 'get' the version
|
|
pyicu_str = 'unknown version'
|
|
icu_str = 'unknown version'
|
|
|
|
except ImportError:
|
|
pyicu_str = 'not found'
|
|
icu_str = 'not found'
|
|
|
|
from .gen.config import config
|
|
usebsddb3 = config.get('preferences.use-bsddb3') or sys.version_info[0] >= 3
|
|
try:
|
|
if usebsddb3:
|
|
import bsddb3 as bsddb
|
|
else:
|
|
import bsddb
|
|
bsddb_str = bsddb.__version__
|
|
bsddb_db_str = str(bsddb.db.version()).replace(', ', '.')\
|
|
.replace('(', '').replace(')', '')
|
|
except:
|
|
bsddb_str = 'not found'
|
|
bsddb_db_str = 'not found'
|
|
|
|
try:
|
|
from .gen.const import VERSION
|
|
gramps_str = VERSION
|
|
except:
|
|
gramps_str = 'not found'
|
|
|
|
if hasattr(os, "uname"):
|
|
kernel = os.uname()[2]
|
|
else:
|
|
kernel = None
|
|
|
|
lang_str = get_env_var('LANG','not set')
|
|
language_str = get_env_var('LANGUAGE','not set')
|
|
grampsi18n_str = get_env_var('GRAMPSI18N','not set')
|
|
grampshome_str = get_env_var('GRAMPSHOME','not set')
|
|
grampsdir_str = get_env_var('GRAMPSDIR','not set')
|
|
|
|
try:
|
|
dotversion_str = Popen(['dot', '-V'], stderr=PIPE).communicate(input=None)[1]
|
|
if isinstance(dotversion_str, bytes) and sys.stdin.encoding:
|
|
dotversion_str = dotversion_str.decode(sys.stdin.encoding)
|
|
if dotversion_str:
|
|
dotversion_str = dotversion_str.replace('\n','')[23:27]
|
|
except:
|
|
dotversion_str = 'Graphviz not in system PATH'
|
|
|
|
try:
|
|
if win():
|
|
gsversion_str = Popen(['gswin32c', '--version'], stdout=PIPE).communicate(input=None)[0]
|
|
else:
|
|
gsversion_str = Popen(['gs', '--version'], stdout=PIPE).communicate(input=None)[0]
|
|
if isinstance(gsversion_str, bytes) and sys.stdin.encoding:
|
|
gsversion_str = gsversion_str.decode(sys.stdin.encoding)
|
|
if gsversion_str:
|
|
gsversion_str = gsversion_str.replace('\n', '')
|
|
except:
|
|
gsversion_str = 'Ghostscript not in system PATH'
|
|
|
|
os_path = get_env_var('PATH','not set')
|
|
os_path = os_path.split(os.pathsep)
|
|
|
|
print ("Gramps Settings:")
|
|
print ("----------------")
|
|
print (' python : %s' % py_str)
|
|
print (' gramps : %s' % gramps_str)
|
|
print (' gtk++ : %s' % gtkver_str)
|
|
print (' pygobject : %s' % pygobjectver_str)
|
|
print (' pango : %s' % pangover_str)
|
|
if usebsddb3:
|
|
print (' Using bsddb3')
|
|
else:
|
|
print (' Not using bsddb3')
|
|
print (' bsddb : %s' % bsddb_str)
|
|
print (' bsddb.db : %s' % bsddb_db_str)
|
|
print (' cairo : %s' % cairover_str)
|
|
print (' pycairo : %s' % pycairover_str)
|
|
print (' osmgpsmap : %s' % osmgpsmap_str)
|
|
print (' GExiv2 : %s' % gexiv2_str)
|
|
print (' ICU : %s' % icu_str)
|
|
print (' PyICU : %s' % pyicu_str)
|
|
print (' o.s. : %s' % sys.platform)
|
|
if kernel:
|
|
print (' kernel : %s' % kernel)
|
|
print ('')
|
|
print ("Environment settings:")
|
|
print ("---------------------")
|
|
print (' LANG : %s' % lang_str)
|
|
print (' LANGUAGE : %s' % language_str)
|
|
print (' GRAMPSI18N: %s' % grampsi18n_str)
|
|
print (' GRAMPSHOME: %s' % grampshome_str)
|
|
print (' GRAMPSDIR : %s' % grampsdir_str)
|
|
print (' PYTHONPATH:')
|
|
for folder in sys.path:
|
|
print (" ", folder)
|
|
print ('')
|
|
print ("Non-python dependencies:")
|
|
print ("------------------------")
|
|
print (' Graphviz : %s' % dotversion_str)
|
|
print (' Ghostscr. : %s' % gsversion_str)
|
|
print ('')
|
|
print ("System PATH env variable:")
|
|
print ("-------------------------")
|
|
for folder in os_path:
|
|
print (" ", folder)
|
|
print ('')
|
|
|
|
def run():
|
|
error = []
|
|
|
|
try:
|
|
build_user_paths()
|
|
except OSError as msg:
|
|
error += [(_("Configuration error:"), str(msg))]
|
|
return error
|
|
except msg:
|
|
LOG.error("Error reading configuration.", exc_info=True)
|
|
return [(_("Error reading configuration"), str(msg))]
|
|
|
|
if not mime_type_is_defined(APP_GRAMPS):
|
|
error += [(_("Configuration error:"),
|
|
_("A definition for the MIME-type %s could not "
|
|
"be found \n\n Possibly the installation of Gramps "
|
|
"was incomplete. Make sure the MIME-types "
|
|
"of Gramps are properly installed.")
|
|
% APP_GRAMPS)]
|
|
|
|
#we start with parsing the arguments to determine if we have a cli or a
|
|
# gui session
|
|
|
|
if "-v" in sys.argv or "--version" in sys.argv:
|
|
show_settings()
|
|
return error
|
|
|
|
from .cli.argparser import ArgParser
|
|
argv_copy = sys.argv[:]
|
|
argpars = ArgParser(argv_copy)
|
|
|
|
# Calls to LOG must be after setup_logging() and ArgParser()
|
|
LOG = logging.getLogger(".locale")
|
|
LOG.debug("Encoding: %s", glocale.encoding)
|
|
LOG.debug("Translating Gramps to %s", glocale.language[0])
|
|
LOG.debug("Collation Locale: %s", glocale.collation)
|
|
LOG.debug("Date/Time Locale: %s", glocale.calendar)
|
|
LOG.debug("Currency Locale: %s", glocale.currency)
|
|
LOG.debug("Number-format Locale: %s", glocale.numeric)
|
|
|
|
if 'LANG' in os.environ:
|
|
LOG.debug('Using LANG: %s' %
|
|
get_env_var('LANG'))
|
|
else:
|
|
LOG.debug('environment: LANG is not defined')
|
|
if 'LANGUAGE' in os.environ:
|
|
LOG.debug('Using LANGUAGE: %s' %
|
|
get_env_var('LANGUAGE'))
|
|
else:
|
|
LOG.debug('environment: LANGUAGE is not defined')
|
|
|
|
if argpars.need_gui():
|
|
LOG.debug("A GUI is needed, set it up")
|
|
if "--qml" in sys.argv:
|
|
from .guiQML.grampsqml import startqml
|
|
startqml(error, argpars)
|
|
else:
|
|
try:
|
|
from .gui.grampsgui import startgtkloop
|
|
# no DISPLAY is a RuntimeError in an older pygtk (e.g. F14's 2.17)
|
|
except RuntimeError as msg:
|
|
error += [(_("Configuration error:"), str(msg))]
|
|
return error
|
|
startgtkloop(error, argpars)
|
|
else:
|
|
#CLI use of GRAMPS
|
|
argpars.print_help()
|
|
argpars.print_usage()
|
|
from .cli.grampscli import startcli
|
|
startcli(error, argpars)
|
|
|
|
def main():
|
|
errors = run()
|
|
if errors and isinstance(errors, list):
|
|
for error in errors:
|
|
logging.warning(error[0] + error[1])
|
|
|
|
if __name__ == '__main__':
|
|
main()
|