Welcome, guest | Sign In | My Account | Store | Cart

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.
Python, 62 lines
 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.

2 comments

SnarkTurne (author) 11 years, 1 month ago  # | flag

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....

SnarkTurne (author) 11 years, 1 month ago  # | flag

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.

Created by SnarkTurne on Sat, 9 Mar 2013 (MIT)
Python recipes (4591)
SnarkTurne's recipes (2)

Required Modules

Other Information and Tasks