Package roslib :: Module substitution_args
[frames] | no frames]

Source Code for Module roslib.substitution_args

  1  # Software License Agreement (BSD License) 
  2  # 
  3  # Copyright (c) 2008, Willow Garage, Inc. 
  4  # All rights reserved. 
  5  # 
  6  # Redistribution and use in source and binary forms, with or without 
  7  # modification, are permitted provided that the following conditions 
  8  # are met: 
  9  # 
 10  #  * Redistributions of source code must retain the above copyright 
 11  #    notice, this list of conditions and the following disclaimer. 
 12  #  * Redistributions in binary form must reproduce the above 
 13  #    copyright notice, this list of conditions and the following 
 14  #    disclaimer in the documentation and/or other materials provided 
 15  #    with the distribution. 
 16  #  * Neither the name of Willow Garage, Inc. nor the names of its 
 17  #    contributors may be used to endorse or promote products derived 
 18  #    from this software without specific prior written permission. 
 19  # 
 20  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 21  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 22  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 23  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 24  # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 25  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 26  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 27  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 28  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 29  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 30  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 31  # POSSIBILITY OF SUCH DAMAGE. 
 32  # 
 33  # Revision $Id: substitution_args.py 9358 2010-04-22 22:31:00Z kwc $ 
 34   
 35  """ 
 36  Library for processing XML substitution args. This is currently used 
 37  by roslaunch and xacro, but it is not yet a top-level ROS feature. 
 38  """ 
 39   
 40  import os 
 41  import cStringIO 
 42   
 43  import roslib.exceptions 
 44  import roslib.names 
 45  import roslib.packages 
 46   
47 -class SubstitutionException(roslib.exceptions.ROSLibException):
48 """ 49 Base class for exceptions in roslib.substitution_args routines 50 """ 51 pass
52 -class ArgException(SubstitutionException):
53 """ 54 Exception for missing $(arg) values 55 """ 56 pass
57
58 -def _env(resolved, a, args, context):
59 """ 60 process $(env) arg 61 @return: updated resolved argument 62 @rtype: str 63 @raise SubstitutionException: if arg invalidly specified 64 """ 65 if len(args) != 1: 66 raise SubstitutionException("$(env var) command only accepts one argument [%s]"%a) 67 try: 68 return resolved.replace("$(%s)"%a, os.environ[args[0]]) 69 except KeyError, e: 70 raise SubstitutionException("environment variable %s is not set"%str(e))
71
72 -def _optenv(resolved, a, args, context):
73 """ 74 process $(optenv) arg 75 @return: updated resolved argument 76 @rtype: str 77 @raise SubstitutionException: if arg invalidly specified 78 """ 79 if len(args) == 0: 80 raise SubstitutionException("$(optenv var) must specify an environment variable [%s]"%a) 81 if args[0] in os.environ: 82 return resolved.replace("$(%s)"%a, os.environ[args[0]]) 83 elif len(args) > 1: 84 return resolved.replace("$(%s)"%a, ' '.join(args[1:])) 85 else: 86 return resolved.replace("$(%s)"%a, '')
87
88 -def _anon(resolved, a, args, context):
89 """ 90 process $(anon) arg 91 @return: updated resolved argument 92 @rtype: str 93 @raise SubstitutionException: if arg invalidly specified 94 """ 95 # #1559 #1660 96 if len(args) == 0: 97 raise SubstitutionException("$(anon var) must specify a name [%s]"%a) 98 elif len(args) > 1: 99 raise SubstitutionException("$(anon var) may only specify one name [%s]"%a) 100 id = args[0] 101 if 'anon' not in context: 102 context['anon'] = {} 103 anon_context = context['anon'] 104 if id in anon_context: 105 return resolved.replace("$(%s)"%a, anon_context[id]) 106 else: 107 resolve_to = roslib.names.anonymous_name(id) 108 anon_context[id] = resolve_to 109 return resolved.replace("$(%s)"%a, resolve_to)
110
111 -def _find(resolved, a, args, context):
112 """ 113 process $(find) arg 114 @return: updated resolved argument 115 @rtype: str 116 @raise SubstitutionException: if arg invalidly specified 117 """ 118 if len(args) != 1: 119 raise SubstitutionException("$(find pkg) command only accepts one argument [%s]"%a) 120 arg = "$(%s)"%a 121 sep = os.sep #set to var for easier testing 122 123 #Force / and \ file separators to the os-native 124 #convention. We replace everything from the end of the 125 #$(find) command to the next space. As we don't support 126 #filenames with spaces, this is dandy. 127 idx = resolved.find(arg)+len(arg) 128 endidx = resolved.find(' ', idx) 129 if endidx < 0: 130 endidx = len(resolved) 131 slash_orig = resolved[idx:endidx] 132 resolved = resolved.replace(slash_orig, slash_orig.replace('/', sep)) 133 resolved = resolved.replace(slash_orig, slash_orig.replace('\\', sep)) 134 135 return resolved[0:idx-len(arg)] + roslib.packages.get_pkg_dir(args[0]) + resolved[idx:]
136
137 -def _arg(resolved, a, args, context):
138 """ 139 process $(arg) arg 140 141 @return: updated resolved argument 142 @rtype: str 143 @raise ArgException: if arg invalidly specified 144 """ 145 if len(args) == 0: 146 raise SubstitutionException("$(arg var) must specify an environment variable [%s]"%a) 147 elif len(args) > 1: 148 raise SubstitutionException("$(arg var) may only specify one arg [%s]"%a) 149 150 if 'arg' not in context: 151 context['arg'] = {} 152 arg_context = context['arg'] 153 154 arg_name = args[0] 155 if arg_name in arg_context: 156 arg_value = arg_context[arg_name] 157 return resolved.replace("$(%s)"%a, arg_value) 158 else: 159 raise ArgException(arg_name)
160 161
162 -def resolve_args(arg_str, context=None, resolve_anon=True):
163 """ 164 Resolves substitution args (see wiki spec U{http://ros.org/wiki/roslaunch}). 165 166 @param arg_str: string to resolve zero or more substitution args 167 in. arg_str may be None, in which case resolve_args will 168 return None 169 @type arg_str: str 170 @param context dict: (optional) dictionary for storing results of 171 the 'anon' and 'arg' substitution args. multiple calls to 172 resolve_args should use the same context so that 'anon' 173 substitions resolve consistently. If no context is provided, a 174 new one will be created for each call. Values for the 'arg' 175 context should be stored as a dictionary in the 'arg' key. 176 @type context: dict 177 @param resolve_anon bool: If True (default), will resolve $(anon 178 foo). If false, will leave these args as-is. 179 @type resolve_anon: bool 180 181 @return str: arg_str with substitution args resolved 182 @rtype: str 183 @raise SubstitutionException: if there is an error resolving substitution args 184 """ 185 if context is None: 186 context = {} 187 #parse found substitution args 188 if not arg_str: 189 return arg_str 190 valid = ['find', 'env', 'optenv', 'anon', 'arg'] 191 # disabled 'export' due to lack of use and API change 192 resolved = arg_str 193 for a in _collect_args(arg_str): 194 splits = [s for s in a.split(' ') if s] 195 if not splits[0] in valid: 196 raise SubstitutionException("Unknown substitution command [%s]. Valid commands are %s"%(a, valid)) 197 command = splits[0] 198 args = splits[1:] 199 if command == 'find': 200 resolved = _find(resolved, a, args, context) 201 elif command == 'env': 202 resolved = _env(resolved, a, args, context) 203 elif command == 'optenv': 204 resolved = _optenv(resolved, a, args, context) 205 elif command == 'anon' and resolve_anon: 206 resolved = _anon(resolved, a, args, context) 207 elif command == 'arg': 208 resolved = _arg(resolved, a, args, context) 209 210 return resolved
211 212 _OUT = 0 213 _DOLLAR = 1 214 _LP = 2 215 _IN = 3
216 -def _collect_args(arg_str):
217 """ 218 State-machine parser for resolve_args. Substitution args are of the form: 219 $(find rospy)/scripts/foo.py $(export some/attribute blar) non-relevant stuff 220 221 @param arg_str: argument string to parse args from 222 @type arg_str: str 223 @raise SubstitutionException: if args are invalidly specified 224 @return: list of arguments 225 @rtype: [str] 226 """ 227 buff = cStringIO.StringIO() 228 args = [] 229 state = _OUT 230 for c in arg_str: 231 # No escapes supported 232 if c == '$': 233 if state == _OUT: 234 state = _DOLLAR 235 elif state == _DOLLAR: 236 pass 237 else: 238 raise SubstitutionException("Dollar signs '$' cannot be inside of substitution args [%s]"%arg_str) 239 elif c == '(': 240 if state == _DOLLAR: 241 state = _LP 242 elif state != _OUT: 243 raise SubstitutionException("Invalid left parenthesis '(' in substitution args [%s]"%arg_str) 244 elif c == ')': 245 if state == _IN: 246 #save contents of collected buffer 247 args.append(buff.getvalue()) 248 buff.truncate(0) 249 buff.seek(0) 250 state = _OUT 251 else: 252 state = _OUT 253 elif state == _DOLLAR: 254 # left paren must immediately follow dollar sign to enter _IN state 255 state = _OUT 256 elif state == _LP: 257 state = _IN 258 259 if state == _IN: 260 buff.write(c) 261 return args
262