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 14215 2011-07-11 20:30:07Z 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   
 42  try: 
 43      from cStringIO import StringIO # Python 2.x 
 44  except ImportError: 
 45      from io import StringIO # Python 3.x 
 46   
 47  import roslib.exceptions 
 48  import roslib.names 
 49  import roslib.packages 
 50   
51 -class SubstitutionException(roslib.exceptions.ROSLibException):
52 """ 53 Base class for exceptions in roslib.substitution_args routines 54 """ 55 pass
56 -class ArgException(SubstitutionException):
57 """ 58 Exception for missing $(arg) values 59 """ 60 pass
61
62 -def _env(resolved, a, args, context):
63 """ 64 process $(env) arg 65 @return: updated resolved argument 66 @rtype: str 67 @raise SubstitutionException: if arg invalidly specified 68 """ 69 if len(args) != 1: 70 raise SubstitutionException("$(env var) command only accepts one argument [%s]"%a) 71 try: 72 return resolved.replace("$(%s)"%a, os.environ[args[0]]) 73 except KeyError as e: 74 raise SubstitutionException("environment variable %s is not set"%str(e))
75
76 -def _optenv(resolved, a, args, context):
77 """ 78 process $(optenv) arg 79 @return: updated resolved argument 80 @rtype: str 81 @raise SubstitutionException: if arg invalidly specified 82 """ 83 if len(args) == 0: 84 raise SubstitutionException("$(optenv var) must specify an environment variable [%s]"%a) 85 if args[0] in os.environ: 86 return resolved.replace("$(%s)"%a, os.environ[args[0]]) 87 elif len(args) > 1: 88 return resolved.replace("$(%s)"%a, ' '.join(args[1:])) 89 else: 90 return resolved.replace("$(%s)"%a, '')
91
92 -def _anon(resolved, a, args, context):
93 """ 94 process $(anon) arg 95 @return: updated resolved argument 96 @rtype: str 97 @raise SubstitutionException: if arg invalidly specified 98 """ 99 # #1559 #1660 100 if len(args) == 0: 101 raise SubstitutionException("$(anon var) must specify a name [%s]"%a) 102 elif len(args) > 1: 103 raise SubstitutionException("$(anon var) may only specify one name [%s]"%a) 104 id = args[0] 105 if 'anon' not in context: 106 context['anon'] = {} 107 anon_context = context['anon'] 108 if id in anon_context: 109 return resolved.replace("$(%s)"%a, anon_context[id]) 110 else: 111 resolve_to = roslib.names.anonymous_name(id) 112 anon_context[id] = resolve_to 113 return resolved.replace("$(%s)"%a, resolve_to)
114
115 -def _find(resolved, a, args, context):
116 """ 117 process $(find) arg 118 @return: updated resolved argument 119 @rtype: str 120 @raise SubstitutionException: if arg invalidly specified 121 """ 122 if len(args) != 1: 123 raise SubstitutionException("$(find pkg) command only accepts one argument [%s]"%a) 124 arg = "$(%s)"%a 125 sep = os.sep #set to var for easier testing 126 127 #Force / and \ file separators to the os-native 128 #convention. We replace everything from the end of the 129 #$(find) command to the next space. As we don't support 130 #filenames with spaces, this is dandy. 131 idx = resolved.find(arg)+len(arg) 132 endidx = resolved.find(' ', idx) 133 if endidx < 0: 134 endidx = len(resolved) 135 slash_orig = resolved[idx:endidx] 136 resolved = resolved.replace(slash_orig, slash_orig.replace('/', sep)) 137 resolved = resolved.replace(slash_orig, slash_orig.replace('\\', sep)) 138 139 return resolved[0:idx-len(arg)] + roslib.packages.get_pkg_dir(args[0]) + resolved[idx:]
140
141 -def _arg(resolved, a, args, context):
142 """ 143 process $(arg) arg 144 145 @return: updated resolved argument 146 @rtype: str 147 @raise ArgException: if arg invalidly specified 148 """ 149 if len(args) == 0: 150 raise SubstitutionException("$(arg var) must specify an environment variable [%s]"%(a)) 151 elif len(args) > 1: 152 raise SubstitutionException("$(arg var) may only specify one arg [%s]"%(a)) 153 154 if 'arg' not in context: 155 context['arg'] = {} 156 arg_context = context['arg'] 157 158 arg_name = args[0] 159 if arg_name in arg_context: 160 arg_value = arg_context[arg_name] 161 return resolved.replace("$(%s)"%a, arg_value) 162 else: 163 raise ArgException(arg_name)
164 165
166 -def resolve_args(arg_str, context=None, resolve_anon=True):
167 """ 168 Resolves substitution args (see wiki spec U{http://ros.org/wiki/roslaunch}). 169 170 @param arg_str: string to resolve zero or more substitution args 171 in. arg_str may be None, in which case resolve_args will 172 return None 173 @type arg_str: str 174 @param context dict: (optional) dictionary for storing results of 175 the 'anon' and 'arg' substitution args. multiple calls to 176 resolve_args should use the same context so that 'anon' 177 substitions resolve consistently. If no context is provided, a 178 new one will be created for each call. Values for the 'arg' 179 context should be stored as a dictionary in the 'arg' key. 180 @type context: dict 181 @param resolve_anon bool: If True (default), will resolve $(anon 182 foo). If false, will leave these args as-is. 183 @type resolve_anon: bool 184 185 @return str: arg_str with substitution args resolved 186 @rtype: str 187 @raise SubstitutionException: if there is an error resolving substitution args 188 """ 189 if context is None: 190 context = {} 191 #parse found substitution args 192 if not arg_str: 193 return arg_str 194 valid = ['find', 'env', 'optenv', 'anon', 'arg'] 195 # disabled 'export' due to lack of use and API change 196 resolved = arg_str 197 for a in _collect_args(arg_str): 198 splits = [s for s in a.split(' ') if s] 199 if not splits[0] in valid: 200 raise SubstitutionException("Unknown substitution command [%s]. Valid commands are %s"%(a, valid)) 201 command = splits[0] 202 args = splits[1:] 203 if command == 'find': 204 resolved = _find(resolved, a, args, context) 205 elif command == 'env': 206 resolved = _env(resolved, a, args, context) 207 elif command == 'optenv': 208 resolved = _optenv(resolved, a, args, context) 209 elif command == 'anon' and resolve_anon: 210 resolved = _anon(resolved, a, args, context) 211 elif command == 'arg': 212 resolved = _arg(resolved, a, args, context) 213 214 return resolved
215 216 _OUT = 0 217 _DOLLAR = 1 218 _LP = 2 219 _IN = 3
220 -def _collect_args(arg_str):
221 """ 222 State-machine parser for resolve_args. Substitution args are of the form: 223 $(find rospy)/scripts/foo.py $(export some/attribute blar) non-relevant stuff 224 225 @param arg_str: argument string to parse args from 226 @type arg_str: str 227 @raise SubstitutionException: if args are invalidly specified 228 @return: list of arguments 229 @rtype: [str] 230 """ 231 buff = StringIO() 232 args = [] 233 state = _OUT 234 for c in arg_str: 235 # No escapes supported 236 if c == '$': 237 if state == _OUT: 238 state = _DOLLAR 239 elif state == _DOLLAR: 240 pass 241 else: 242 raise SubstitutionException("Dollar signs '$' cannot be inside of substitution args [%s]"%arg_str) 243 elif c == '(': 244 if state == _DOLLAR: 245 state = _LP 246 elif state != _OUT: 247 raise SubstitutionException("Invalid left parenthesis '(' in substitution args [%s]"%arg_str) 248 elif c == ')': 249 if state == _IN: 250 #save contents of collected buffer 251 args.append(buff.getvalue()) 252 buff.truncate(0) 253 buff.seek(0) 254 state = _OUT 255 else: 256 state = _OUT 257 elif state == _DOLLAR: 258 # left paren must immediately follow dollar sign to enter _IN state 259 state = _OUT 260 elif state == _LP: 261 state = _IN 262 263 if state == _IN: 264 buff.write(c) 265 return args
266