You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

218 lines
6.9 KiB

#!/usr/bin/env python3
"""
Library for processing XML substitution args.
"""
# Software License Agreement (BSD License)
#
# Copyright (c) 2008, Willow Garage, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Willow Garage, Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Revision $Id: substitution_args.py 15178 2011-10-10 21:22:53Z kwc $
from io import StringIO
import os
class SubstitutionException(Exception):
"""
Base class for exceptions in substitution_args routines
"""
pass
class ArgException(SubstitutionException):
"""
Exception for missing $(arg) values
"""
pass
def _split_command(resolved, command_with_args):
cmd = '$(%s)' % command_with_args
idx1 = resolved.find(cmd)
idx2 = idx1 + len(cmd)
return resolved[0:idx1], resolved[idx2:]
def _separate_first_path(value):
idx = value.find(' ')
if idx < 0:
path, rest = value, ''
else:
path, rest = value[0:idx], value[idx:]
return path, rest
def _sanitize_path(path):
path = path.replace('/', os.sep)
path = path.replace('\\', os.sep)
return path
def _arg(resolved, a, args, context):
"""
process $(arg) arg
:returns: updated resolved argument, ``str``
:raises: :exc:`ArgException` If arg invalidly specified
"""
if len(args) == 0:
raise SubstitutionException(
"$(arg var) must specify an environment variable [%s]" %
(a))
elif len(args) > 1:
raise SubstitutionException(
"$(arg var) may only specify one arg [%s]" %
(a))
if 'arg' not in context:
context['arg'] = {}
arg_context = context['arg']
arg_name = args[0]
if arg_name in arg_context:
arg_value = arg_context[arg_name]
return resolved.replace("$(%s)" % a, arg_value)
else:
raise ArgException(arg_name)
def resolve_args(arg_str, context=None, resolve_anon=True):
"""
Resolves substitution args (see wiki spec U{http://ros.org/wiki/roslaunch}).
@param arg_str: string to resolve zero or more substitution args
in. arg_str may be None, in which case resolve_args will
return None
@type arg_str: str
@param context dict: (optional) dictionary for storing results of
the 'anon' and 'arg' substitution args. multiple calls to
resolve_args should use the same context so that 'anon'
substitions resolve consistently. If no context is provided, a
new one will be created for each call. Values for the 'arg'
context should be stored as a dictionary in the 'arg' key.
@type context: dict
@param resolve_anon bool: If True (default), will resolve $(anon
foo). If false, will leave these args as-is.
@type resolve_anon: bool
@return str: arg_str with substitution args resolved
@rtype: str
@raise SubstitutionException: if there is an error resolving substitution args
"""
if context is None:
context = {}
# parse found substitution args
if not arg_str:
return arg_str
# first resolve variables like 'env' and 'arg'
commands = {
'arg': _arg,
}
resolved = _resolve_args(arg_str, context, resolve_anon, commands)
# than resolve 'find' as it requires the subsequent path to be expanded
# already
resolved = _resolve_args(resolved, context, resolve_anon, commands)
return resolved
def _resolve_args(arg_str, context, resolve_anon, commands):
valid = ['arg']
resolved = arg_str
for a in _collect_args(arg_str):
splits = [s for s in a.split(' ') if s]
if not splits[0] in valid:
raise SubstitutionException(
"Unknown substitution command [%s]. Valid commands are %s" %
(a, valid))
command = splits[0]
args = splits[1:]
if command in commands:
resolved = commands[command](resolved, a, args, context)
return resolved
_OUT = 0
_DOLLAR = 1
_LP = 2
_IN = 3
def _collect_args(arg_str):
"""
State-machine parser for resolve_args. Substitution args are of the form:
$(find package_name)/scripts/foo.py $(export some/attribute blar) non-relevant stuff
@param arg_str: argument string to parse args from
@type arg_str: str
@raise SubstitutionException: if args are invalidly specified
@return: list of arguments
@rtype: [str]
"""
buff = StringIO()
args = []
state = _OUT
for c in arg_str:
# No escapes supported
if c == '$':
if state == _OUT:
state = _DOLLAR
elif state == _DOLLAR:
pass
else:
raise SubstitutionException(
"Dollar signs '$' cannot be inside of substitution args [%s]" %
arg_str)
elif c == '(':
if state == _DOLLAR:
state = _LP
elif state != _OUT:
raise SubstitutionException(
"Invalid left parenthesis '(' in substitution args [%s]" % arg_str)
elif c == ')':
if state == _IN:
# save contents of collected buffer
args.append(buff.getvalue())
buff.truncate(0)
buff.seek(0)
state = _OUT
else:
state = _OUT
elif state == _DOLLAR:
# left paren must immediately follow dollar sign to enter _IN state
state = _OUT
elif state == _LP:
state = _IN
if state == _IN:
buff.write(c)
return args