This recipe allow you to write :
>>> a,b=5,6
>>> rstr("If you add #{a} and #{b}, you'll get #{a+b}.")
If you add 5 and 6, you'll get 11.
This is more readble, from my point of view, than :
"If you add {} and {}, you'll get {}.".format(a,b,a+b)
This recipe is inspired from the way Ruby evaluates strings :
irb> a,b=5,6
irb> "If you add #{a} and #{b}, you'll get #{a+b}."
==> If you add 5 and 6, you'll get 11.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | """
Module : rubstr
Author : Snarkturne
Version : 0.91
Evaluate expressions in strings
(the Ruby way : "Hello world number #{n+1}")
from rubstr import rstr
a,b=5,6
print(rstr("If you add #{a} and #{b}, you'll get #{a+b}."))
Note that the eval fonction is used. So, :
rstr("Your files are #{os.listdir()}")
will give the list of your files if module os have been imported.
(Using rstr can delete your files and do about everything that can be
done with Python, don't use rstr on unstrusted sources
v 0.91 :
Added the access to callee locals
"""
import re
def _split(stri) :
""" Split the initial string into normal strings and expressions
to evaluate (enclosed in #{...})
"""
def analyse(s) :
v=re.match("#{(.*)}$",s)
if v : return ("E",v.group(1))
return ("S",s)
lst=re.split("(#{[^}]*})",stri)
return [analyse(c) for c in lst]
def _eval(lstr,locals) :
""" Evaluate parts of the string. Normal parts ("S") are not modified.
Expression parts ("E") are evaluated (using eval).
"""
l=[]
for t,s in lstr :
if t=="S" : l.append(s)
if t=="E" : l.append(str(eval(s,None,locals)))
return l
def rstr(stri) :
""" Split the string, evaluates expressions, and reconstruct
the result.
"""
import inspect
f = inspect.currentframe()
locals=f.f_back.f_locals
return "".join(_eval(_split(stri),locals))
def testme() :
n=4
print(rstr("n=#{n}"))
if __name__=='__main__' :
a,b=5,6
print(rstr("If you add #{a} and #{b}, you'll get #{a+b}."))
testme()
|
locals are accessed using the inspect module.
Oups... Unfortunately, this is not useful as the environment (locals) used by eval is not the same as the one where rstr is called... Trying to correct this....
OK. This has been corrected in v 0.91. I dont know if using
inspects.currentframe().f_back.f_locals
is the best way to access the caller locals. Let me know if ther is a better/simpler/safer way to do this.