code_junkie

extracting a parenthesized Python expression from a string

Nov 11th, 2011
79
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 3.07 KB | None | 0 0
  1. #!/usr/bin/env python
  2.  
  3. import sys
  4. import re
  5.  
  6. FILE = sys.argv[1]
  7.  
  8. handle = open(FILE)
  9. fcontent = handle.read()
  10. handle.close()
  11.  
  12. for myexpr in re.finditer(r'${([^}]+)}', fcontent, re.M|re.S):
  13. text = myexpr.group(1)
  14. try:
  15. exec text
  16. except SyntaxError:
  17. print "ERROR: unable to compile expression '%s'" % (text)
  18.  
  19. This is some random text, with embedded python like
  20. ${print "foo"} and some bogus python like
  21.  
  22. ${any:thing}.
  23.  
  24. And a multiline statement, just for kicks:
  25.  
  26. ${
  27. def multiline_stmt(foo):
  28. print foo
  29.  
  30. multiline_stmt("ahem")
  31. }
  32.  
  33. More text here.
  34.  
  35. [user@host]$ ./exec_embedded_python.py test.txt
  36. foo
  37. ERROR: unable to compile expression 'any:thing'
  38. ahem
  39.  
  40. def findExpr(s, i0=0, begin='${', end='}', compArgs=('<string>', 'eval')):
  41. assert 'n' not in s, 'line numbers not implemented'
  42. i0 = s.index(begin, i0) + len(begin)
  43. i1 = s.index(end, i0)
  44. code = errMsg = None
  45. while code is None and errMsg is None:
  46. expr = s[i0:i1]
  47. try: code = compile(expr, *compArgs)
  48. except SyntaxError, e:
  49. i1 = s.find(end, i1 + 1)
  50. if i1 < 0: errMsg, i1 = e.msg, i0 + e.offset
  51. return i0, i1, code, errMsg
  52.  
  53. '''
  54. Search s for a (possibly invalid) Python expression bracketed by begin
  55. and end, which default to '${' and '}'. Return a 4-tuple.
  56.  
  57. >>> s = 'foo ${a*b + c*d} bar'
  58. >>> i0, i1, code, errMsg = findExpr(s)
  59. >>> i0, i1, s[i0:i1], errMsg
  60. (6, 15, 'a*b + c*d', None)
  61. >>> ' '.join('%02x' % ord(byte) for byte in code.co_code)
  62. '65 00 00 65 01 00 14 65 02 00 65 03 00 14 17 53'
  63. >>> code.co_names
  64. ('a', 'b', 'c', 'd')
  65. >>> eval(code, {'a': 1, 'b': 2, 'c': 3, 'd': 4})
  66. 14
  67. >>> eval(code, {'a': 'a', 'b': 2, 'c': 'c', 'd': 4})
  68. 'aacccc'
  69. >>> eval(code, {'a': None})
  70. Traceback (most recent call last):
  71. ...
  72. NameError: name 'b' is not defined
  73.  
  74. Expressions containing start and/or end are allowed.
  75.  
  76. >>> s = '{foo ${{"}": "${"}["}"]} bar}'
  77. >>> i0, i1, code, errMsg = findExpr(s)
  78. >>> i0, i1, s[i0:i1], errMsg
  79. (7, 23, '{"}": "${"}["}"]', None)
  80.  
  81. If the first match is syntactically invalid Python, i0 points to the
  82. start of the match, i1 points to the parse error, code is None and
  83. errMsg contains a message from the compiler.
  84.  
  85. >>> s = '{foo ${qwerty asdf zxcvbnm!!!} ${7} bar}'
  86. >>> i0, i1, code, errMsg = findExpr(s)
  87. >>> i0, i1, s[i0:i1], errMsg
  88. (7, 18, 'qwerty asdf', 'invalid syntax')
  89. >>> print code
  90. None
  91.  
  92. If a second argument is given, start searching there.
  93.  
  94. >>> i0, i1, code, errMsg = findExpr(s, i1)
  95. >>> i0, i1, s[i0:i1], errMsg
  96. (33, 34, '7', None)
  97.  
  98. Raise ValueError if there are no further matches.
  99.  
  100. >>> i0, i1, code, errMsg = findExpr(s, i1)
  101. Traceback (most recent call last):
  102. ...
  103. ValueError: substring not found
  104.  
  105. In ambiguous cases, match the shortest valid expression. This is not
  106. always ideal behavior.
  107.  
  108. >>> s = '{foo ${x or {} # return {} instead of None} bar}'
  109. >>> i0, i1, code, errMsg = findExpr(s)
  110. >>> i0, i1, s[i0:i1], errMsg
  111. (7, 25, 'x or {} # return {', None)
  112.  
  113. This implementation must not be used with multi-line strings. It does
  114. not adjust line number information in the returned code object, and it
  115. does not take the line number into account when computing the offset
  116. of a parse error.
  117.  
  118. '''
Add Comment
Please, Sign In to add comment