# python2 / python3 compatibility try: # python 2 _basestr = basestring except NameError: # python 3 _basestr = str unicode = str def get_boolean_value(value): """ Return a boolean value that corresponds to the given value. Values "true", "1" and "1.0" are supposed to be True. Values "false", "0" and "0.0" are supposed to be False. All other values raise an exception. """ if isinstance(value, _basestr): if value == 'true' or value == 'True': return True elif value == 'false' or value == 'False': return False else: return bool(int(value)) else: return bool(value) class MyDict(object): def __init__(self, parent=None): self.parent = parent self.dict = {} self.depth = 0 # for nice indentation of debugging messages @staticmethod def _eval_literal(value): if isinstance(value, _basestr): # remove single quotes from escaped string if len(value) >= 2 and value[0] == "'" and value[-1] == "'": return value[1:-1] # Try to evaluate as number literal or boolean. # This is needed to handle numbers in property definitions as numbers, not strings. # python3 ignores/drops underscores in number literals (due to PEP515). # Here, we want to handle literals with underscores as plain strings. if '_' in value: return value # order of types is important! for f in [int, float, lambda x: get_boolean_value(x)]: try: return f(value) except Exception: pass return value def _resolve_(self, key): print("{indent}resolve({key})".format(key=key, indent=' ' * 2*self.depth)) self.depth += 1 value = self._eval_literal(eval_text(self.dict[key], self)) self.depth -= 1 return value def __getitem__(self, key): print("{indent}getitem({})".format(key, indent=' ' * 2*self.depth)) if key in self.dict: self.depth += 1 value = self._resolve_(key) self.depth -= 1 return value else: raise KeyError(key) def __setitem__(self, key, value): print("Set {key}: {value}".format(key=key, value=value)) self.depth += 1 value = self._eval_literal(value) self.depth -= 1 self.dict[key] = value def __contains__(self, key): return key in self.dict def __str__(self): return unicode(self.dict) # restrict set of global symbols global_symbols = {} def eval_text(s, symbols): print("{indent}eval_text('{}') with symbols: {}".format(s, symbols.dict.keys(), indent=' ' * 2*symbols.depth)) symbols.depth += 1 value = eval(s, global_symbols, symbols) symbols.depth -= 1 return value symbols = MyDict() symbols['abc'] = '[1, 2, 3]' print('*** This one works ***') symbols['xyz'] = 'abc + abc' print(eval_text('xyz', symbols)) print('*** This one fails ***') symbols['xyz'] = '[abc[i]*abc[i] for i in [0, 1, 2]]' print(eval_text('xyz', symbols))