Suppose I have a string like this
"Style.BRIGHT + 'BRIGHT' + '\\n' + Style.DIM + 'DIM' + '\\n' + Style.NORMAL + 'NORMAL'"
I try to print it directly
from colorama import Style
s = "Style.BRIGHT + 'BRIGHT' + '\\n' + Style.DIM + 'DIM' + '\\n' + Style.NORMAL + 'NORMAL'"
print(s)
Output:
Style.BRIGHT + 'BRIGHT' + '\n' + Style.DIM + 'DIM' + '\n' + Style.NORMAL + 'NORMAL'
This is not the result I want, I want it to display the result in the terminal like this.
So I tried eval and it worked.
from colorama import Style
s = "Style.BRIGHT + 'BRIGHT' + '\\n' + Style.DIM + 'DIM' + '\\n' + Style.NORMAL + 'NORMAL'"
print(eval(s))
Output:
BRIGHT
DIM
NORMAL
Although it succeeded, it seems to be unsafe, I also tried adding the global variable {"Style": Style}, so it might be relatively safe.
But maybe I'll extract properties in other variables, like this x.y, where x and y can be any value. So it feels like eval doesn't apply.
The answer of @jfs in this question seems to be what I want, and I modified the program.
from colorama import Style
import ast
import operator as op
# supported operators
operators = {ast.Add: op.add}
def eval_expr(expr):
return eval_(ast.parse(expr, mode='eval').body)
def eval_(node):
if isinstance(node, ast.Constant): # <number>
return node.n
elif isinstance(node, ast.BinOp): # <left> <operator> <right>
return operators[type(node.op)](eval_(node.left), eval_(node.right))
elif isinstance(node, ast.Attribute):
return ast.unparse(node)
else:
raise TypeError(node)
s = "Style.BRIGHT + 'BRIGHT' + '\\n' + Style.DIM + 'DIM' + '\\n' + Style.NORMAL + 'NORMAL'"
print(eval_expr(s))
Output:
Style.BRIGHTBRIGHT
Style.DIMDIM
Style.NORMALNORMAL
In the program the string Style.BRIGHT is converted into an ast.Attribute object, I don't know how to get its value('\x1b[1m').
In [1]: from colorama import Style
In [2]: Style.BRIGHT
Out[2]: '\x1b[1m'
If I use eval(ast.unparse(node)) it succeeds, but if so why not just use eval directly...
So my question is:
- How can I extract the primitive value corresponding to the
ast.Attributeobject?
Or is there any other way to interpret the string.
The demo above is just one case, it can be applied to other cases. For example, I want to define a macro, which is a string that adds and sums some properties of a class, which will be applied to other programs or processes. Demo:
class Base:
a = 1
b = 2
def get(cls):
s = []
for k, v in cls.__dict__.items():
if k.startswith("_"):
continue
s.append(f"{cls.__name__}.{k}")
return " + ".join(s)
s = get(Base)
print(s)
# Base.a + Base.b
Maybe my case is a bit unsatisfactory, but this problem may appear in a future scenario, maybe it's just me wanting to know how to deal with it.

NB. It would be hundred times better to use the correct object and not a string in the first place, starting with correct input is almost always more explicit and efficient than trying to fix a broken input.
If you have a string with arbitrarily complex operations, you will need to understand its grammar, which is exactly what
astis doing, or blindly (and potentially unsafely) evaluate it witheval.That said, if you want to simplify your process in the particular case you showed, you need to do 3 things. 1- replace the
Stylerdefinitions with the string formatting codes they represent, 2- remove the quotes and +, 3- unescape the escaped newlines.2 and 3 are quite easy to achieve, so I'll focus only on interpreting the codes here. You can use a regex to find the Styler definitions and
re.subto replace them safely with the actual formatting code.Output: