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.Attribute
object?
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.