Source code for africanus.util.cmdline

#!/usr/bin/env python
# -*- coding: utf-8 -*-


import ast
import builtins

# builtin function whitelist
_BUILTIN_WHITELIST = frozenset(['slice'])
_missing = _BUILTIN_WHITELIST.difference(dir(builtins))
if len(_missing) > 0:
    raise ValueError("'%s' are not valid builtin functions.'" % list(_missing))


[docs]def parse_python_assigns(assign_str): """ Parses a string, containing assign statements into a dictionary. .. code-block:: python data = parse_python_assigns("beta=5.6; l=[2,3], s='hello, world'") assert data == { 'beta': 5.6, 'l': [2, 3], 's': 'hello, world' } Parameters ---------- assign_str: str Assignment string. Should only contain assignment statements assigning python literals or builtin function calls, to variable names. Multiple assignment statements should be separated by semi-colons. Returns ------- dict Dictionary { name: value } containing assignment results. """ if not assign_str: return {} def _eval_value(stmt_value): # If the statement value is a call to a builtin, try evaluate it if isinstance(stmt_value, ast.Call): func_name = stmt_value.func.id if func_name not in _BUILTIN_WHITELIST: raise ValueError("Function '%s' in '%s' is not builtin. " "Available builtins: '%s'" % (func_name, assign_str, list(_BUILTIN_WHITELIST))) # Recursively pass arguments through this same function if stmt_value.args is not None: args = tuple(_eval_value(a) for a in stmt_value.args) else: args = () # Recursively pass keyword arguments through this same function if stmt_value.keywords is not None: kwargs = {kw.arg: _eval_value(kw.value) for kw in stmt_value.keywords} else: kwargs = {} return getattr(builtins, func_name)(*args, **kwargs) # Try a literal eval else: return ast.literal_eval(stmt_value) # Variable dictionary variables = {} # Parse the assignment string stmts = ast.parse(assign_str, mode='single').body for i, stmt in enumerate(stmts): if not isinstance(stmt, ast.Assign): raise ValueError("Statement %d in '%s' is not a " "variable assignment." % (i, assign_str)) # Evaluate assignment lhs values = _eval_value(stmt.value) # "a = b = c" => targets 'a' and 'b' with 'c' as result for target in stmt.targets: # a = 2 if isinstance(target, ast.Name): variables[target.id] = values # Tuple/List unpacking case # (a, b) = 2 elif isinstance(target, (ast.Tuple, ast.List)): # Require all tuple/list elements to be variable names, # although anything else is probably a syntax error if not all(isinstance(e, ast.Name) for e in target.elts): raise ValueError("Tuple unpacking in assignment %d " "in expression '%s' failed as not all " "tuple contents are variable names.") # Promote for zip and length checking if not isinstance(values, (tuple, list)): elements = (values,) else: elements = values if not len(target.elts) == len(elements): raise ValueError("Unpacking '%s' into a tuple/list in " "assignment %d of expression '%s' " "failed. The number of tuple elements " "did not match the number of values." % (values, i, assign_str)) # Unpack for variable, value in zip(target.elts, elements): variables[variable.id] = value else: raise TypeError("'%s' types are not supported" "as assignment targets." % type(target)) return variables