def parse(obj): """ Bool operations in non-if statements The _if.py parser handles a boolean operation in an if statement e.g., if var1 and var2: So if we're here it means we're in some other expression e.g., chmod +x file || echo "No file found" """ return " {} ".format(parse(obj.op)).join( [parse(val) for val in obj.values])
def parse(obj): if len(obj.ops) != 1: raise Unparseable( "Compare operations must one and only have one operation") if len(obj.comparators) != 1: raise Unparseable( "Compare operations must one and only have one comparator") numeric_compare = False if isinstance(obj.left, Num) or isinstance(obj.comparators[0], Num): numeric_compare = True return "{} {} {}".format( parse(obj.left), parse(obj.ops[0], numeric=numeric_compare), parse(obj.comparators[0]), )
def envvar(call_args, call_kwargs, parseable=None, **kwargs): default = get_kwarg(call_kwargs, 'default', default=None) var_name = parse(call_args[0], raw=True) parseable._known_vars.add(var_name) if default is not None: return "{var}=${{{var}:-{default}}}".format(var=var_name, default=default)
def parse(cls, obj): cls._known_funcs.add(obj.name) return "\n".join([ "{}() {{".format(obj.name), parse(obj.args), parse_body(obj.body), "}", ])
def _color(color_code, args): from shellshock.convert import ConvertContext ConvertContext.colors_used = True return ("\"${{SHELLSHOCK_COLOR_{color_code}}}" "{text}" "${{SHELLSHOCK_COLOR_EC}}\"".format( color_code=color_code, text=parse(args[0], raw=True), ))
def parse_conditional(cls, obj_test): if isinstance(obj_test, Name): # They are testing a boolean variable, compare to true return "[ {test} = true ]".format(test=parse(obj_test)) elif isinstance(obj_test, UnaryOp) and \ isinstance(obj_test.op, Not) and \ isinstance(obj_test.operand, Name): # They are testing NOT a boolean variable, still assume true return "[ {test} = true ]".format(test=parse(obj_test)) elif isinstance(obj_test, Call): return "{test}".format(test=parse(obj_test)) elif isinstance(obj_test, UnaryOp) and \ isinstance(obj_test.op, Not) and \ isinstance(obj_test.operand, Call): # They are testing NOT a call, prefix the ! and parse the call return "{test}".format(test=parse(obj_test)) else: # It's a more complicated test, parse it return "[ {test} ]".format(test=parse(obj_test))
def get_kwarg(call_kwargs, kwarg_name, default=__get_kwarg_default, **kwargs): """ Return the value of a kwarg in a converter call """ for kwarg in call_kwargs: if kwarg.arg == kwarg_name: default = parse(kwarg.value, **kwargs) break if default is __get_kwarg_default: raise KeyError( "Required kwarg '{}' was not provided".format(kwarg_name)) return default
def _call_mock(cls, mock_id, mock_args): """ Call a unit test mock instead of the real method. This will spawn a subshell that does two things: 1. Records the mock call with arguments for assertion in the test 2. Calls a stub function that returns the mock side effect """ return '$(__record_mock_call {mock} {args}; __mock_{mock})'.format( mock=mock_id, args=" ".join([parse(arg) for arg in mock_args]), )
def parse(cls, obj): assign_target = obj.targets[0].id cls._known_vars.add(assign_target) if isinstance(obj.value, Call) and obj.value.func.attr == 'input': # If we're assigning from user input, adjust accordingly return cls._read_input_into_var(assign_target, obj) else: return "{}={}".format( assign_target, parse(obj.value), )
def parse(cls, obj, **kwargs): func_name = parse(obj.func) # Process keyword args to see if this is a func call ID to mock for kwarg in obj.keywords: if kwarg.arg == '__id__': mock_id = parse(kwarg.value, raw=True) if mock_id in cls._known_mocks: return cls._call_mock(mock_id, obj.args) if func_name in cls._known_funcs: return "{} {}".format( func_name, " ".join([parse(arg) for arg in obj.args]), ) else: return run_converter( func=func_name, call_ref=obj, parseable=cls, context=ConvertContext, **kwargs, )
def _read_input_into_var(cls, var, obj): """ Read user input into variable At this point we assume obj.value is a Call object pointing to ss.input and obj.value.args[0] is the input prompt. We know the user is trying to set this input to a new variable, but we must make a temp variable in between. The variable name will be based off the hash of the prompt for repeatability's sake. """ prompt_hash = sha256() prompt_hash.update(obj.value.args[0].s.encode()) temp_var = "INPUT_VAR_{}".format(prompt_hash.hexdigest()[:8]) return [ parse(obj.value, assign_to_var=temp_var), "{new_var}=\"${temp_var}\"".format(new_var=var, temp_var=temp_var), ]
def parse(cls, obj): arg_list = [] num_args = len(obj.args) num_defaults = len(obj.defaults) for arg_index, arg in enumerate(obj.args): cls._known_vars.add(arg.arg) default = None default_index = arg_index + num_defaults - num_args if default_index >= 0: default = obj.defaults[default_index] arg_list.append( parse( arg, arg_num=(arg_index + 1), arg_default=default, )) return "\n".join(arg_list)
def input(call_args, call_kwargs, parseable=None, assign_to_var=None, **kwargs): if assign_to_var is not None: # See if we are assigning this to a new variable first target_var = assign_to_var else: # If we're not assigning, they better specify the 'target' kwarg target_var = get_kwarg(call_kwargs, 'target', raw=True) parseable._known_vars.add(target_var) return "read -p {prompt} {var} </dev/tty".format( prompt=parse(call_args[0]), var=target_var, )
def parse(cls, obj): out_lines = [] if isinstance(obj.test, BoolOp): conds = " {} ".format(parse(obj.test.op)).join( [cls.parse_conditional(val) for val in obj.test.values]) else: conds = cls.parse_conditional(obj.test) out_lines.append("if {conds}; then".format(conds=conds)) out_lines.append( "{body}".format(body=parse_body(obj.body)) ) if obj.orelse: out_lines.append("else") out_lines.append( "{body}".format(body=parse_body(obj.orelse)) ) out_lines.append("fi") return out_lines
def parse(obj): """ Returns a stringified version of the attr. """ return "{}.{}".format(parse(obj.value), obj.attr)
def shell(call_args, **kwargs): arg = call_args[0] return parse(arg, raw=True).strip()
def print(call_args, **kwargs): return "echo -e {}".format(" ".join([parse(arg) for arg in call_args]))
def exit(call_args, **kwargs): return "exit {}".format(parse(call_args[0]))
def parse(obj): return "{} {}".format( parse(obj.op), parse(obj.operand), )
def my_custom_converter(args): return "echo 'I came from a custom converter' args -> {}".format(" ".join( [parse(arg) for arg in args]))
def parse(obj, arg_num, arg_default): trailing = "" if arg_default: trailing = ":-{}".format(parse(arg_default)) return " {}=${{{}{}}}".format(obj.arg, arg_num, trailing)
def parse(obj): return parse(obj.value)
def _file_check(test, call_args): return "[ {} {} ]".format(test, parse(call_args[0]))
def numeric_bin_op(cls, obj): return "$(({left} {op} {right}))".format(left=parse(obj.left), op=parse(obj.op), right=parse(obj.right))
def subshell(call_args, **kwargs): """ Run the argument in a subshell, return the result """ return "\"$({})\"".format(parse(call_args[0], raw=True))
def string_bin_op(cls, obj): if not isinstance(obj.op, Add): raise Unparseable("Binary op {} not supported on strings".format( type(obj.op))) return "{}{}".format(parse(obj.left), parse(obj.right))
def shebang(call_args, context=None, **kwargs): context.shebang = parse(call_args[0], raw=True)
def parse(obj): return "echo {}".format(" ".join( [parse(value) for value in obj.values]))