@@ -31,15 +31,13 @@ class ArgumentResolver:
31
31
files : dockerblade .FileSystem
32
32
context : Dict [str , Any ] = attr .ib (default = None )
33
33
34
- def _resolve_arg (self , s : str ) -> str :
34
+ def _resolve_substitution_arg (self , s : str ) -> str :
35
35
"""
36
36
Raises
37
37
------
38
38
EnvNotFoundError
39
39
if a given environment variable is not found.
40
40
"""
41
- shell = self .shell
42
- context = self .context
43
41
logger .debug (f"resolving substitution argument: { s } " )
44
42
s = s [2 :- 1 ]
45
43
logger .debug (f"stripped delimiters: { s } " )
@@ -49,31 +47,49 @@ def _resolve_arg(self, s: str) -> str:
49
47
# we deal with find in a later stage
50
48
if kind == 'find' :
51
49
return f'$({ s } )'
52
- if kind == 'env' :
53
- return shell .environ (params [0 ])
54
- if kind == 'optenv' :
55
- try :
56
- return shell .environ (params [0 ])
57
- except dockerblade .exceptions .EnvNotFoundError :
58
- return ' ' .join (params [1 :])
59
- if kind == 'dirname' :
60
- try :
61
- dirname = os .path .dirname (context ['filename' ])
62
- except KeyError :
63
- m = 'filename is not provided by the launch context'
64
- raise SubstitutionError (m )
65
- dirname = os .path .normpath (dirname )
66
- return dirname
67
- if kind == 'arg' :
50
+ elif kind == 'env' :
51
+ var = params [0 ]
52
+ return self ._resolve_env (var )
53
+ elif kind == 'optenv' :
54
+ var = params [0 ]
55
+ default = ' ' .join (params [1 :])
56
+ return self ._resolve_optenv (var , default )
57
+ elif kind == 'dirname' :
58
+ return self ._resolve_dirname ()
59
+ elif kind == 'arg' :
68
60
arg_name = params [0 ]
69
- if 'arg' not in context or arg_name not in context ['arg' ]:
70
- m = f'arg not supplied to launch context [{ arg_name } ]'
71
- raise SubstitutionError (m )
72
- return context ['arg' ][arg_name ]
73
-
74
- # TODO $(anon name)
61
+ return self ._resolve_arg (arg_name )
62
+ elif kind == 'anon' :
63
+ return self ._resolve_anon (params [0 ])
75
64
return s
76
65
66
+ def _resolve_dirname (self ) -> str :
67
+ try :
68
+ dirname = os .path .dirname (self .context ['filename' ])
69
+ except KeyError :
70
+ m = 'filename is not provided by the launch context'
71
+ raise SubstitutionError (m )
72
+ return os .path .normpath (dirname )
73
+
74
+ def _resolve_anon (self , name : str ) -> str :
75
+ raise NotImplementedError
76
+
77
+ def _resolve_env (self , var : str ) -> str :
78
+ return self .shell .environ (var )
79
+
80
+ def _resolve_optenv (self , var : str , default : str ) -> str :
81
+ try :
82
+ return self .shell .environ (var )
83
+ except dockerblade .exceptions .EnvNotFoundError :
84
+ return default
85
+
86
+ def _resolve_arg (self , arg_name : str ) -> str :
87
+ context = self .context
88
+ if 'arg' not in context or arg_name not in context ['arg' ]:
89
+ m = f'arg not supplied to launch context [{ arg_name } ]'
90
+ raise SubstitutionError (m )
91
+ return context ['arg' ][arg_name ]
92
+
77
93
def _find_package_path (self , package : str ) -> str :
78
94
cmd = f'rospack find { shlex .quote (package )} '
79
95
try :
@@ -125,7 +141,7 @@ def _find_resource(self, package: str, path: str) -> str:
125
141
raise SubstitutionError (m )
126
142
return path_in_package
127
143
128
- def _resolve_find (self , package : str , path : str ) -> str :
144
+ def _resolve_find (self , package : str , path : str = '' ) -> str :
129
145
logger .debug (f'resolving find: { package } ' )
130
146
path_original = path
131
147
@@ -153,12 +169,42 @@ def _resolve_find(self, package: str, path: str) -> str:
153
169
resolved_path = self ._find_package_path (package ) + path_original
154
170
return resolved_path
155
171
172
+ def _resolve_eval (self , attribute_string : str ) -> str :
173
+ logger .debug (f'resolving eval: { attribute_string } ' )
174
+ assert attribute_string .startswith ('$(eval ' )
175
+ assert attribute_string [- 1 ] == ')'
176
+ eval_string = attribute_string [7 :- 1 ]
177
+
178
+ if '__' in attribute_string :
179
+ m = ("$(eval ...): refusing to evaluate potentially dangerous "
180
+ "expression -- must not contain double underscores" )
181
+ raise SubstitutionError (m )
182
+
183
+ _builtins = {x : __builtins__ [x ] # type: ignore
184
+ for x in ('dict' , 'float' , 'int' , 'list' , 'map' )}
185
+ _locals = {
186
+ 'true' : True ,
187
+ 'True' : True ,
188
+ 'false' : False ,
189
+ 'False' : False ,
190
+ '__builtins__' : _builtins ,
191
+ 'arg' : self ._resolve_arg ,
192
+ 'anon' : self ._resolve_anon ,
193
+ 'dirname' : self ._resolve_dirname ,
194
+ 'env' : self ._resolve_env ,
195
+ 'find' : self ._resolve_find ,
196
+ 'optenv' : self ._resolve_optenv
197
+ }
198
+
199
+ result = str (eval (eval_string , {}, _locals ))
200
+ logger .debug (f'resolved eval [{ attribute_string } ]: { result } ' )
201
+ return result
202
+
156
203
def resolve (self , s : str ) -> str :
157
204
"""Resolves a given argument string."""
158
- # TODO $(eval ...)
159
205
if s .startswith ('$(eval ' ) and s [- 1 ] == ')' :
160
- raise NotImplementedError
161
- s = R_ARG .sub (lambda m : self ._resolve_arg (m .group (0 )), s )
206
+ return self . _resolve_eval ( s )
207
+ s = R_ARG .sub (lambda m : self ._resolve_substitution_arg (m .group (0 )), s )
162
208
163
209
def process_find_arg (match : Match [str ]) -> str :
164
210
# split tag and optional trailing path
0 commit comments