[GRASS-SVN] r65676 - grass/trunk/scripts/g.extension
svn_grass at osgeo.org
svn_grass at osgeo.org
Mon Jul 20 01:02:19 PDT 2015
Author: wenzeslaus
Date: 2015-07-20 01:02:19 -0700 (Mon, 20 Jul 2015)
New Revision: 65676
Modified:
grass/trunk/scripts/g.extension/g.extension.py
Log:
g.extension: docstrings, better variable names
Modified: grass/trunk/scripts/g.extension/g.extension.py
===================================================================
--- grass/trunk/scripts/g.extension/g.extension.py 2015-07-20 08:00:44 UTC (rev 65675)
+++ grass/trunk/scripts/g.extension/g.extension.py 2015-07-20 08:02:19 UTC (rev 65676)
@@ -146,6 +146,7 @@
def check_progs():
+ """Check if the necessary programs are available"""
for prog in ('svn', 'make', 'gcc'):
if not grass.find_program(prog, '--help'):
grass.fatal(_("'%s' required. Please install '%s' first.")
@@ -154,7 +155,17 @@
# expand prefix to class name
-def expand_module_class_name(c):
+def expand_module_class_name(class_letters):
+ """Convert module class (family) letter or letters to class (family) name
+
+ The letter or letters are used in module names, e.g. r.slope.aspect.
+ The names are used in directories in Addons but also in the source code.
+
+ >>> expand_module_class_name('r')
+ raster
+ >>> expand_module_class_name('v')
+ vector
+ """
name = {'d': 'display',
'db': 'database',
'g': 'general',
@@ -169,12 +180,13 @@
'wx': 'gui/wxpython'
}
- return name.get(c, c)
+ return name.get(class_letters, class_letters)
# list installed extensions
def get_installed_extensions(force=False):
+ """Get list of installed extensions or toolboxes (if -t is set)"""
if flags['t']:
return get_installed_toolboxes(force)
@@ -182,17 +194,23 @@
def get_installed_toolboxes(force=False):
- fXML = os.path.join(options['prefix'], 'toolboxes.xml')
- if not os.path.exists(fXML):
- write_xml_toolboxes(fXML)
+ """Get list of installed toolboxes
+ Writes toolboxes file if it does not exist.
+ Creates a new toolboxes file if it is not possible
+ to read the current one.
+ """
+ xml_file = os.path.join(options['prefix'], 'toolboxes.xml')
+ if not os.path.exists(xml_file):
+ write_xml_toolboxes(xml_file)
+
# read XML file
- fo = open(fXML, 'r')
+ fo = open(xml_file, 'r')
try:
tree = etree.fromstring(fo.read())
except:
- os.remove(fXML)
- write_xml_toolboxes(fXML)
+ os.remove(xml_file)
+ write_xml_toolboxes(xml_file)
return []
fo.close()
@@ -204,21 +222,27 @@
def get_installed_modules(force=False):
- fXML = os.path.join(options['prefix'], 'modules.xml')
- if not os.path.exists(fXML):
+ """Get list of installed modules.
+
+ Writes modules file if it does not exist and *force* is set to ``True``.
+ Creates a new modules file if it is not possible
+ to read the current one.
+ """
+ xml_file = os.path.join(options['prefix'], 'modules.xml')
+ if not os.path.exists(xml_file):
if force:
- write_xml_modules(fXML)
+ write_xml_modules(xml_file)
else:
grass.debug(1, "No addons metadata file available")
return []
# read XML file
- fo = open(fXML, 'r')
+ fo = open(xml_file, 'r')
try:
tree = etree.fromstring(fo.read())
except:
- os.remove(fXML)
- write_xml_modules(fXML)
+ os.remove(xml_file)
+ write_xml_modules(xml_file)
return []
fo.close()
@@ -232,9 +256,13 @@
def list_available_extensions(url):
+ """List available extensions/modules or toolboxes (if -t is given)
+
+ For toolboxes it lists also all modules.
+ """
if flags['t']:
grass.message(_("List of available extensions (toolboxes):"))
- tlist = list_available_toolboxes(url)
+ tlist = get_available_toolboxes(url)
for toolbox_code, toolbox_data in tlist.iteritems():
if flags['g']:
print('toolbox_name=' + toolbox_data['name'])
@@ -251,7 +279,8 @@
list_available_modules(url)
-def list_available_toolboxes(url):
+def get_available_toolboxes(url):
+ """Return toolboxes available in the repository"""
tdict = dict()
url = url + "toolboxes.xml"
try:
@@ -276,6 +305,11 @@
def get_toolbox_modules(url, name):
+ """Get modules inside a toolbox in toolbox file at given URL
+
+ :param url: URL of the directory (file name will be attached)
+ :param name: toolbox name
+ """
tlist = list()
url = url + "toolboxes.xml"
@@ -295,6 +329,10 @@
def get_optional_params(mnode):
+ """Return description and keywords as a tuple
+
+ :param mnode: XML node for a module
+ """
try:
desc = mnode.find('description').text
except AttributeError:
@@ -312,7 +350,10 @@
def list_available_modules(url, mlist=None):
- # try to download XML metadata file first
+ """List modules available in the repository
+
+ Tries to use XML metadata file first. Fallbacks to HTML page with a list.
+ """
url = url + "modules.xml"
grass.debug("url=%s" % url, 1)
try:
@@ -349,6 +390,10 @@
def list_available_extensions_svn():
+ """List available extensions from HTML given by URL
+
+ ``<li><a href=...`` is parsed to find module names.
+ """
grass.message(_('Fetching list of extensions from'
' GRASS-Addons SVN repository (be patient)...'))
pattern = re.compile(r'(<li><a href=".+">)(.+)(</a></li>)', re.IGNORECASE)
@@ -360,10 +405,10 @@
grass.warning(
_("Flag 'g' ignored, addons metadata file not available"))
- prefix = ['d', 'db', 'g', 'i', 'm', 'ps',
- 'p', 'r', 'r3', 's', 'v']
- for d in prefix:
- modclass = expand_module_class_name(d)
+ prefixes = ['d', 'db', 'g', 'i', 'm', 'ps',
+ 'p', 'r', 'r3', 's', 'v']
+ for prefix in prefixes:
+ modclass = expand_module_class_name(prefix)
grass.verbose(_("Checking for '%s' modules...") % modclass)
url = '%s/%s' % (options['svnurl'], modclass)
@@ -380,15 +425,14 @@
if not sline:
continue
name = sline.group(2).rstrip('/')
- if name.split('.', 1)[0] == d:
+ if name.split('.', 1)[0] == prefix:
print(name)
# get_wxgui_extensions()
-# list wxGUI extensions
-
def get_wxgui_extensions():
+ """Return list of extensions/addons in wxGUI directory at given URL"""
mlist = list()
grass.debug('Fetching list of wxGUI extensions from '
'GRASS-Addons SVN repository (be patient)...')
@@ -415,22 +459,28 @@
def cleanup():
+ """Cleanup after the downloads and copilation"""
if REMOVE_TMPDIR:
try_rmdir(TMPDIR)
else:
grass.message("\n%s\n" % _("Path to the source code:"))
sys.stderr.write('%s\n' % os.path.join(TMPDIR, options['extension']))
-# write out meta-file
+def write_xml_modules(name, tree=None):
+ """Write element tree as a modules matadata file
-def write_xml_modules(name, tree=None):
+ If the *tree* is not given, an empty file is created.
+
+ :param name: file name
+ :param tree: XML element tree
+ """
fo = open(name, 'w')
fo.write('<?xml version="1.0" encoding="UTF-8"?>\n')
fo.write('<!DOCTYPE task SYSTEM "grass-addons.dtd">\n')
fo.write('<addons version="%s">\n' % version[0])
- libgisRev = grass.version()['libgis_revision']
+ libgis_revison = grass.version()['libgis_revision']
if tree is not None:
for tnode in tree.findall('task'):
indent = 4
@@ -452,7 +502,7 @@
indent -= 4
fo.write('%s</binary>\n' % (' ' * indent))
fo.write('%s<libgis revision="%s" />\n' %
- (' ' * indent, libgisRev))
+ (' ' * indent, libgis_revison))
indent -= 4
fo.write('%s</task>\n' % (' ' * indent))
@@ -461,6 +511,13 @@
def write_xml_toolboxes(name, tree=None):
+ """Write element tree as a toolboxes matadata file
+
+ If the *tree* is not given, an empty file is created.
+
+ :param name: file name
+ :param tree: XML element tree
+ """
fo = open(name, 'w')
fo.write('<?xml version="1.0" encoding="UTF-8"?>\n')
fo.write('<!DOCTYPE toolbox SYSTEM "grass-addons.dtd">\n')
@@ -483,10 +540,9 @@
fo.write('</addons>\n')
fo.close()
-# install extension - toolbox or module
-
def install_extension(url):
+ """Install extension (e.g. one module) or a toolbox (list of modules)"""
gisbase = os.getenv('GISBASE')
if not gisbase:
grass.fatal(_('$GISBASE not defined'))
@@ -509,7 +565,7 @@
if sys.platform == "win32":
ret += install_extension_win(module)
else:
- ret += install_extension_other(module)
+ ret += install_extension_std_platforms(module)
if len(mlist) > 1:
print('-' * 60)
@@ -533,10 +589,9 @@
' you set the GRASS_ADDON_BASE environment'
' variable (see "g.manual variables")'))
-# update local meta-file when installing new extension (toolbox / modules)
-
def install_toolbox_xml(url, name):
+ """Update local toolboxes metadata file"""
# read metadata from remote server (toolboxes)
url = url + "toolboxes.xml"
@@ -570,15 +625,14 @@
grass.warning(_("No addons metadata available for <%s>") % name)
return
- fXML = os.path.join(options['prefix'], 'toolboxes.xml')
+ xml_file = os.path.join(options['prefix'], 'toolboxes.xml')
# create an empty file if not exists
- if not os.path.exists(fXML):
- write_xml_modules(fXML)
+ if not os.path.exists(xml_file):
+ write_xml_modules(xml_file)
# read XML file
- fo = open(fXML, 'r')
- tree = etree.fromstring(fo.read())
- fo.close()
+ with open(xml_file, 'r') as xml:
+ tree = etree.fromstring(xml.read())
# update tree
tnode = None
@@ -607,12 +661,16 @@
mnode = etree.Element('task', attrib={'name': tname})
tnode.append(mnode)
- write_xml_toolboxes(fXML, tree)
+ write_xml_toolboxes(xml_file, tree)
-# return list of executables for update_manual_page()
+def install_extension_xml(url, mlist):
+ """Update XML files with metadata about installed modules and toolbox
-def install_extension_xml(url, mlist):
+ Uses the remote/repository XML files for modules to obtain the metadata.
+
+ :returns: list of executables (useable for ``update_manual_page()``)
+ """
if len(mlist) > 1:
# read metadata from remote server (toolboxes)
install_toolbox_xml(url, options['extension'])
@@ -621,7 +679,7 @@
url = url + "modules.xml"
data = {}
- bList = []
+ bin_list = []
try:
f = urlopen(url, proxies=PROXIES)
try:
@@ -629,35 +687,35 @@
except:
grass.warning(_("Unable to parse '%s'."
" Addons metadata file not updated.") % url)
- return bList
+ return bin_list
for mnode in tree.findall('task'):
name = mnode.get('name')
if name not in mlist:
continue
- fList = list()
+ file_list = list()
bnode = mnode.find('binary')
windows = sys.platform == 'win32'
if bnode is not None:
for fnode in bnode.findall('file'):
path = fnode.text.split('/')
if path[0] == 'bin':
- bList.append(path[-1])
+ bin_list.append(path[-1])
if windows:
path[-1] += '.exe'
elif path[0] == 'scripts':
- bList.append(path[-1])
+ bin_list.append(path[-1])
if windows:
path[-1] += '.py'
- fList.append(os.path.sep.join(path))
+ file_list.append(os.path.sep.join(path))
desc, keyw = get_optional_params(mnode)
data[name] = {
'desc': desc,
'keyw': keyw,
- 'files': fList,
+ 'files': file_list,
}
except:
@@ -668,13 +726,13 @@
grass.warning(_("No addons metadata available"))
return []
- fXML = os.path.join(options['prefix'], 'modules.xml')
+ xml_file = os.path.join(options['prefix'], 'modules.xml')
# create an empty file if not exists
- if not os.path.exists(fXML):
- write_xml_modules(fXML)
+ if not os.path.exists(xml_file):
+ write_xml_modules(xml_file)
# read XML file
- fo = open(fXML, 'r')
+ fo = open(xml_file, 'r')
tree = etree.fromstring(fo.read())
fo.close()
@@ -703,9 +761,9 @@
if bnode is not None:
tnode.remove(bnode)
bnode = etree.Element('binary')
- for f in ndata['files']:
+ for file_name in ndata['files']:
fnode = etree.Element('file')
- fnode.text = f
+ fnode.text = file_name
bnode.append(fnode)
tnode.append(bnode)
else:
@@ -718,21 +776,20 @@
knode.text = ndata['keyw']
tnode.append(knode)
bnode = etree.Element('binary')
- for f in ndata['files']:
+ for file_name in ndata['files']:
fnode = etree.Element('file')
- fnode.text = f
+ fnode.text = file_name
bnode.append(fnode)
tnode.append(bnode)
tree.append(tnode)
- write_xml_modules(fXML, tree)
+ write_xml_modules(xml_file, tree)
- return bList
+ return bin_list
-# install extension on MS Windows
-
def install_extension_win(name):
+ """Install extension on MS Windows"""
# do not use hardcoded url -
# http://wingrass.fsv.cvut.cz/grassXX/addonsX.X.X
grass.message(_("Downloading precompiled GRASS Addons <%s>...") %
@@ -752,23 +809,23 @@
if not os.path.exists(options['prefix']):
try:
os.mkdir(options['prefix'])
- except OSError as e:
+ except OSError as error:
grass.fatal(_("Unable to create <{}>. {}")
- .format(options['prefix'], e))
+ .format(options['prefix'], error))
# download data
fo = tempfile.TemporaryFile()
fo.write(f.read())
try:
zfobj = zipfile.ZipFile(fo)
- except zipfile.BadZipfile as e:
- grass.fatal('%s: %s' % (e, zfile))
+ except zipfile.BadZipfile as error:
+ grass.fatal('%s: %s' % (error, zfile))
for name in zfobj.namelist():
if name.endswith('/'):
- d = os.path.join(options['prefix'], name)
- if not os.path.exists(d):
- os.mkdir(d)
+ directory = os.path.join(options['prefix'], name)
+ if not os.path.exists(directory):
+ os.mkdir(directory)
else:
outfile = open(os.path.join(options['prefix'], name), 'wb')
outfile.write(zfobj.read(name))
@@ -780,10 +837,9 @@
return 0
-# install extension on other plaforms
-
-def install_extension_other(name):
+def install_extension_std_platforms(name):
+ """Install extension on standard plaforms"""
gisbase = os.getenv('GISBASE')
classchar = name.split('.', 1)[0]
moduleclass = expand_module_class_name(classchar)
@@ -814,30 +870,30 @@
'etc': os.path.join(TMPDIR, name, 'etc'),
}
- makeCmd = ['make',
- 'MODULE_TOPDIR=%s' % gisbase.replace(' ', '\ '),
- 'RUN_GISRC=%s' % os.environ['GISRC'],
- 'BIN=%s' % dirs['bin'],
- 'HTMLDIR=%s' % dirs['html'],
- 'RESTDIR=%s' % dirs['rest'],
- 'MANBASEDIR=%s' % dirs['man'],
- 'SCRIPTDIR=%s' % dirs['script'],
- 'STRINGDIR=%s' % dirs['string'],
- 'ETC=%s' % os.path.join(dirs['etc'])
- ]
+ make_cmd = ['make',
+ 'MODULE_TOPDIR=%s' % gisbase.replace(' ', r'\ '),
+ 'RUN_GISRC=%s' % os.environ['GISRC'],
+ 'BIN=%s' % dirs['bin'],
+ 'HTMLDIR=%s' % dirs['html'],
+ 'RESTDIR=%s' % dirs['rest'],
+ 'MANBASEDIR=%s' % dirs['man'],
+ 'SCRIPTDIR=%s' % dirs['script'],
+ 'STRINGDIR=%s' % dirs['string'],
+ 'ETC=%s' % os.path.join(dirs['etc'])
+ ]
- installCmd = ['make',
- 'MODULE_TOPDIR=%s' % gisbase,
- 'ARCH_DISTDIR=%s' % os.path.join(TMPDIR, name),
- 'INST_DIR=%s' % options['prefix'],
- 'install'
- ]
+ install_cmd = ['make',
+ 'MODULE_TOPDIR=%s' % gisbase,
+ 'ARCH_DISTDIR=%s' % os.path.join(TMPDIR, name),
+ 'INST_DIR=%s' % options['prefix'],
+ 'install'
+ ]
if flags['d']:
grass.message("\n%s\n" % _("To compile run:"))
- sys.stderr.write(' '.join(makeCmd) + '\n')
+ sys.stderr.write(' '.join(make_cmd) + '\n')
grass.message("\n%s\n" % _("To install run:"))
- sys.stderr.write(' '.join(installCmd) + '\n')
+ sys.stderr.write(' '.join(install_cmd) + '\n')
return 0
os.chdir(os.path.join(TMPDIR, name))
@@ -847,7 +903,7 @@
'Make', 'Module.make')):
grass.fatal(_("Please install GRASS development package"))
- if 0 != grass.call(makeCmd,
+ if 0 != grass.call(make_cmd,
stdout=outdev):
grass.fatal(_('Compilation failed, sorry.'
' Please check above error messages.'))
@@ -857,13 +913,12 @@
grass.message(_("Installing..."))
- return grass.call(installCmd,
+ return grass.call(install_cmd,
stdout=outdev)
-# remove existing extension - toolbox or module
-
def remove_extension(force=False):
+ """Remove existing extension (module or toolbox if -t is given)"""
if flags['t']:
mlist = get_toolbox_modules(options['extension'])
else:
@@ -890,12 +945,17 @@
def remove_modules(mlist, force=False):
+ """Remove extensions/modules specified in a list
+
+ Collects the file names from the file with metadata and fallbacks
+ to standard layout of files on prefix path on error.
+ """
# try to read XML metadata file first
- fXML = os.path.join(options['prefix'], 'modules.xml')
+ xml_file = os.path.join(options['prefix'], 'modules.xml')
installed = get_installed_modules()
- if os.path.exists(fXML):
- f = open(fXML, 'r')
+ if os.path.exists(xml_file):
+ f = open(xml_file, 'r')
tree = etree.fromstring(f.read())
f.close()
else:
@@ -912,8 +972,8 @@
for task in tree.findall('task'):
if name == task.get('name') and \
task.find('binary') is not None:
- for f in task.find('binary').findall('file'):
- flist.append(f.text)
+ for file_node in task.find('binary').findall('file'):
+ flist.append(file_node.text)
break
if flist:
@@ -933,17 +993,16 @@
grass.fatal(_("Extension <%s> not found") % name)
if err:
- for e in err:
- grass.error(e)
+ for error_line in err:
+ grass.error(error_line)
else:
remove_extension_std(name, force)
else:
remove_extension_std(name, force)
-# remove exising extension (using standard files layout)
-
def remove_extension_std(name, force=False):
+ """Remove extension/module expecting the standard layout"""
for fpath in [os.path.join(options['prefix'], 'bin', name),
os.path.join(options['prefix'], 'scripts', name),
os.path.join(
@@ -959,16 +1018,15 @@
else:
print(fpath)
-# update local meta-file when removing existing extension
-
-def remove_toolbox_xml(name):
- fXML = os.path.join(options['prefix'], 'toolboxes.xml')
- if not os.path.exists(fXML):
+def remove_from_toolbox_xml(name):
+ """Update local meta-file when removing existing toolbox"""
+ xml_file = os.path.join(options['prefix'], 'toolboxes.xml')
+ if not os.path.exists(xml_file):
return
# read XML file
- fo = open(fXML, 'r')
+ fo = open(xml_file, 'r')
tree = etree.fromstring(fo.read())
fo.close()
@@ -977,20 +1035,21 @@
continue
tree.remove(node)
- write_xml_toolboxes(fXML, tree)
+ write_xml_toolboxes(xml_file, tree)
def remove_extension_xml(modules):
+ """Update local meta-file when removing existing extension"""
if len(modules) > 1:
# update also toolboxes metadata
- remove_toolbox_xml(options['extension'])
+ remove_from_toolbox_xml(options['extension'])
- fXML = os.path.join(options['prefix'], 'modules.xml')
- if not os.path.exists(fXML):
+ xml_file = os.path.join(options['prefix'], 'modules.xml')
+ if not os.path.exists(xml_file):
return
# read XML file
- fo = open(fXML, 'r')
+ fo = open(xml_file, 'r')
tree = etree.fromstring(fo.read())
fo.close()
@@ -1000,12 +1059,17 @@
continue
tree.remove(node)
- write_xml_modules(fXML, tree)
+ write_xml_modules(xml_file, tree)
# check links in CSS
def check_style_files(fil):
+ """Ensures that a specified HTML documentation support file exists
+
+ If the file, e.g. a CSS file does not exist, the file is copied from
+ the distribution.
+ """
dist_file = os.path.join(os.getenv('GISBASE'), 'docs', 'html', fil)
addons_file = os.path.join(options['prefix'], 'docs', 'html', fil)
@@ -1014,23 +1078,28 @@
try:
shutil.copyfile(dist_file, addons_file)
- except OSError as e:
- grass.fatal(_("Unable to create '%s': %s") % (addons_file, e))
+ except OSError as error:
+ grass.fatal(_("Unable to create '%s': %s") % (addons_file, error))
def create_dir(path):
+ """Creates the specified directory (with all dirs in between)
+
+ NOOP for existing directory.
+ """
if os.path.isdir(path):
return
try:
os.makedirs(path)
- except OSError as e:
- grass.fatal(_("Unable to create '%s': %s") % (path, e))
+ except OSError as error:
+ grass.fatal(_("Unable to create '%s': %s") % (path, error))
grass.debug("'%s' created" % path)
def check_dirs():
+ """Ensure that the necessary directories in prefix path exist"""
create_dir(os.path.join(options['prefix'], 'bin'))
create_dir(os.path.join(options['prefix'], 'docs', 'html'))
create_dir(os.path.join(options['prefix'], 'docs', 'rest'))
@@ -1044,6 +1113,7 @@
def update_manual_page(module):
+ """Fix manual page for addons which are at different directory then rest"""
if module.split('.', 1)[0] == 'wx':
return # skip for GUI modules
@@ -1054,8 +1124,8 @@
try:
f = open(htmlfile)
shtml = f.read()
- except IOError as e:
- grass.fatal(_("Unable to read manual page: %s") % e)
+ except IOError as error:
+ grass.fatal(_("Unable to read manual page: %s") % error)
else:
f.close()
@@ -1090,8 +1160,8 @@
try:
f = open(htmlfile, 'w')
f.write(ohtml)
- except IOError as e:
- grass.fatal(_("Unable for write manual page: %s") % e)
+ except IOError as error:
+ grass.fatal(_("Unable for write manual page: %s") % error)
else:
f.close()
More information about the grass-commit
mailing list