gramps/gramps/gen/plug/utils.py
John Ralls 13a49a63ff [r21614]Move VERSION, VERSION_TUPLE, major_version
from const.py to version.py

As noted in the previous change, importing const into setup.py tried to
initialize GrampsLocale and ResourcePath, which won't work. Since all we
want is the VERSION string, move that to a new file, gramps/version.py

svn: r21619
2013-03-11 22:54:31 +00:00

355 lines
11 KiB
Python

#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2009 B. Malengier
#
# 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$
"""
General utility functions useful for the generic plugin system
"""
#-------------------------------------------------------------------------
#
# Standard Python modules
#
#-------------------------------------------------------------------------
import locale
import sys
import os
if sys.version_info[0] < 3:
from StringIO import StringIO
else:
from io import StringIO, BytesIO
#-------------------------------------------------------------------------
#
# Gramps modules
#
#-------------------------------------------------------------------------
from ._pluginreg import make_environment
from ..const import USER_PLUGINS
from ...version import VERSION_TUPLE
from ..utils.file import get_unicode_path_from_file_chooser
from ..const import GRAMPS_LOCALE as glocale
_ = glocale.get_translation().gettext
#-------------------------------------------------------------------------
#
# Local utility functions for gen.plug
#
#-------------------------------------------------------------------------
def gfloat(val):
"""Convert to floating number, taking care of possible locale differences.
Useful for reading float values from text entry fields
while under non-English locale.
"""
try:
return float(val)
except:
try:
return float(val.replace('.', ', '))
except:
return float(val.replace(', ', '.'))
return 0.0
def gformat(val):
"""Performs ('%.3f' % val) formatting with the resulting string always
using dot ('.') as a decimal point.
Useful for writing float values into XML when under non-English locale.
"""
decimal_point = locale.localeconv()['decimal_point']
return_val = "%.3f" % val
return return_val.replace(decimal_point, '.')
def version_str_to_tup(sversion, positions):
"""
Given a string version and positions count, returns a tuple of
integers.
>>> version_str_to_tup("1.02.9", 2)
(1, 2)
"""
try:
tup = tuple(map(int, sversion.split(".")))
tup += (0,) * (positions - len(tup))
except:
tup = (0,) * positions
return tup[:positions]
class newplugin(object):
"""
Fake newplugin.
"""
def __init__(self):
globals()["register_results"].append({})
def __setattr__(self, attr, value):
globals()["register_results"][-1][attr] = value
def register(ptype, **kwargs):
"""
Fake registration. Side-effect sets register_results to kwargs.
"""
retval = {"ptype": ptype}
retval.update(kwargs)
# Get the results back to calling function
if "register_results" in globals():
globals()["register_results"].append(retval)
else:
globals()["register_results"] = [retval]
class Zipfile(object):
"""
Class to duplicate the methods of tarfile.TarFile, for Python 2.5.
"""
def __init__(self, buffer):
import zipfile
self.buffer = buffer
self.zip_obj = zipfile.ZipFile(buffer)
def extractall(self, path, members=None):
"""
Extract all of the files in the zip into path.
"""
names = self.zip_obj.namelist()
for name in self.get_paths(names):
fullname = os.path.join(path, name)
if not os.path.exists(fullname):
os.mkdir(fullname)
for name in self.get_files(names):
fullname = os.path.join(path, name)
outfile = file(fullname, 'wb')
outfile.write(self.zip_obj.read(name))
outfile.close()
def extractfile(self, name):
"""
Extract a name from the zip file.
>>> Zipfile(buffer).extractfile("Dir/dile.py").read()
<Contents>
"""
class ExtractFile(object):
def __init__(self, zip_obj, name):
self.zip_obj = zip_obj
self.name = name
def read(self):
data = self.zip_obj.read(self.name)
del self.zip_obj
return data
return ExtractFile(self.zip_obj, name)
def close(self):
"""
Close the zip object.
"""
self.zip_obj.close()
def getnames(self):
"""
Get the files and directories of the zipfile.
"""
return self.zip_obj.namelist()
def get_paths(self, items):
"""
Get the directories from the items.
"""
return (name for name in items if self.is_path(name) and not self.is_file(name))
def get_files(self, items):
"""
Get the files from the items.
"""
return (name for name in items if self.is_file(name))
def is_path(self, name):
"""
Is the name a path?
"""
return os.path.split(name)[0]
def is_file(self, name):
"""
Is the name a directory?
"""
return os.path.split(name)[1]
def load_addon_file(path, callback=None):
"""
Load an addon from a particular path (from URL or file system).
"""
if sys.version_info[0] < 3:
from urllib2 import urlopen
else:
from urllib.request import urlopen
import tarfile
import io
if (path.startswith("http://") or
path.startswith("https://") or
path.startswith("ftp://")):
try:
fp = urlopen(path)
except:
if callback:
callback(_("Unable to open '%s'") % path)
return
else:
try:
fp = open(path)
except:
if callback:
callback(_("Unable to open '%s'") % path)
return
try:
content = fp.read()
if sys.version_info[0] < 3:
buffer = StringIO(content)
else:
buffer = BytesIO(content)
except:
if callback:
callback(_("Error in reading '%s'") % path)
return
fp.close()
# file_obj is either Zipfile or TarFile
if path.endswith(".zip") or path.endswith(".ZIP"):
file_obj = Zipfile(buffer)
elif path.endswith(".tar.gz") or path.endswith(".tgz"):
try:
file_obj = tarfile.open(None, fileobj=buffer)
except:
if callback:
callback(_("Error: cannot open '%s'") % path)
return
else:
if callback:
callback(_("Error: unknown file type: '%s'") % path)
return
# First, see what versions we have/are getting:
good_gpr = set()
for gpr_file in [name for name in file_obj.getnames() if name.endswith(".gpr.py")]:
if callback:
callback((_("Examining '%s'...") % gpr_file) + "\n")
contents = file_obj.extractfile(gpr_file).read()
# Put a fake register and _ function in environment:
env = make_environment(register=register,
newplugin=newplugin,
_=lambda text: text)
# clear out the result variable:
globals()["register_results"] = []
# evaluate the contents:
try:
exec(contents, env)
except:
if callback:
msg = _("Error in '%s' file: cannot load.") % gpr_file
callback(" " + msg + "\n")
continue
# There can be multiple addons per gpr file:
for results in globals()["register_results"]:
gramps_target_version = results.get("gramps_target_version", None)
id = results.get("id", None)
if gramps_target_version:
vtup = version_str_to_tup(gramps_target_version, 2)
# Is it for the right version of gramps?
if vtup == VERSION_TUPLE[0:2]:
# If this version is not installed, or > installed, install it
good_gpr.add(gpr_file)
if callback:
callback(" " + (_("'%s' is for this version of Gramps.") % id) + "\n")
else:
# If the plugin is for another version; inform and do nothing
if callback:
callback(" " + (_("'%s' is NOT for this version of Gramps.") % id) + "\n")
callback(" " + (_("It is for version %(v1)d.%(v2)d") % {
'v1': vtup[0],
'v2': vtup[1]}
+ "\n"))
continue
else:
# another register function doesn't have gramps_target_version
if gpr_file in good_gpr:
s.remove(gpr_file)
if callback:
callback(" " + (_("Error: missing gramps_target_version in '%s'...") % gpr_file) + "\n")
if len(good_gpr) > 0:
# Now, install the ok ones
file_obj.extractall(USER_PLUGINS)
if callback:
callback((_("Installing '%s'...") % path) + "\n")
gpr_files = set([os.path.split(os.path.join(USER_PLUGINS, name))[0]
for name in good_gpr])
for gpr_file in gpr_files:
u_gpr_file = get_unicode_path_from_file_chooser(gpr_file)
if callback:
callback(" " + (_("Registered '%s'") % u_gpr_file) + "\n")
file_obj.close()
#-------------------------------------------------------------------------
#
# OpenFileOrStdout class
#
#-------------------------------------------------------------------------
class OpenFileOrStdout:
"""Context manager to open a file or stdout for writing."""
def __init__(self, filename):
self.filename = filename
self.filehandle = None
def __enter__(self):
if self.filename == '-':
self.filehandle = sys.stdout
else:
self.filehandle = open(self.filename, 'w')
return self.filehandle
def __exit__(self, exc_type, exc_value, traceback):
if self.filehandle and self.filename != '-':
self.filehandle.close()
return False
#-------------------------------------------------------------------------
#
# OpenFileOrStdin class
#
#-------------------------------------------------------------------------
class OpenFileOrStdin:
"""Context manager to open a file or stdin for reading."""
def __init__(self, filename, add_mode=''):
self.filename = filename
self.mode = 'r%s' % add_mode
self.filehandle = None
def __enter__(self):
if self.filename == '-':
self.filehandle = sys.stdin
else:
self.filehandle = open(self.filename, self.mode)
return self.filehandle
def __exit__(self, exc_type, exc_value, traceback):
if self.filename != '-':
self.filehandle.close()
return False