Last active
January 4, 2016 10:39
-
-
Save obriencj/8610594 to your computer and use it in GitHub Desktop.
An attempt to create a Python expression that acts like the try/except/finally blocks.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
maybe:python-spexy siege$ PYTHONPATH=./ python tmp/spexy_try.py | |
The original spexy source is | |
----- | |
(defun division (x y) | |
(let ((result None)) | |
(try | |
(setf result (/ x y)) | |
((ZeroDivisionError) | |
(println "division by zero!" x "/" y) | |
False) | |
(:else | |
(println "result is" result) | |
result) | |
(:finally | |
(println "executing finally clause"))))) | |
----- | |
parses into tree | |
----- | |
['defun', 'division', ['x', 'y'], ['let', [['result', 'None']], ['try', ['setf', 'result', ['/', 'x', 'y']], [['ZeroDivisionError'], ['println', '"division by zero!"', 'x', '"/"', 'y'], 'False'], [':else', ['println', '"result is"', 'result'], 'result'], [':finally', ['println', '"executing finally clause"']]]]] | |
----- | |
processed into Python | |
----- | |
globals().__setitem__('division', (lambda x, y: ((lambda result: (lambda _lcls:(eval(compile('def try_block(try_clause,except_0,else_clause,finally_clause):\n\tfrom sys import exc_info\n\ttry:\n\t\ttry_clause()\n\texcept (ZeroDivisionError,):\n\t\treturn except_0(*exc_info())\n\telse:\n\t\treturn else_clause()\n\tfinally:\n\t\tfinally_clause()\n','<spexy>','single'),globals(),_lcls), _lcls)[-1])({})['try_block']((lambda : result.__setitem__(0, (x / y))),(lambda exc_type, exc_val, exc_tb: (__import__('sys').stdout.writelines(map(str, ("division by zero!", " ", x, " ", "/", " ", y, '\n'))), False,)[-1]), (lambda : (__import__('sys').stdout.writelines(map(str, ("result is", " ", result[0], '\n'))), result[0],)[-1]), (lambda : __import__('sys').stdout.writelines(map(str, ("executing finally clause", '\n'))))))([None])))) | |
----- | |
division(1, 2) | |
result is 0 | |
executing finally clause | |
result: 0 | |
division(4, 2) | |
result is 2 | |
executing finally clause | |
result: 2 | |
division(2, 0) | |
division by zero! 2 / 0 | |
executing finally clause | |
result: False | |
it works! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from cStringIO import StringIO | |
import spexy | |
src = r""" | |
(defun division (x y) | |
(let ((result None)) | |
(try | |
(setf result (/ x y)) | |
((ZeroDivisionError) | |
(println "division by zero!" x "/" y) | |
False) | |
(:else | |
(println "result is" result) | |
result) | |
(:finally | |
(println "executing finally clause"))))) | |
""".strip() | |
print "The original spexy source is\n-----\n", src, "\n-----" | |
tree = spexy.build_tree(StringIO(src))[0] | |
print "parses into tree\n-----\n", tree, "\n-----" | |
pycode = spexy.evaluate(spexy.ns_new(), tree) | |
print "processed into Python\n-----\n", pycode, "\n-----" | |
# since the spexy code uses defun, this will actually put that | |
# function in our globals | |
eval(pycode) | |
print "division(1, 2)" | |
result = division(1, 2) | |
print "result:", result | |
print "division(4, 2)" | |
result = division(4, 2) | |
print "result:", result | |
print "division(2, 0)" | |
result = division(2, 0) | |
print "result:", result | |
print "it works!" |
done in obriencj/python-spexy@377ad19
I need to update the example here, because it doesn't illustrate that the exception handlers automatically get a local binding of exc_type, exc_val, and exc_tb that they can use to work with the exception they're dealing with.
The trycall form is more explicit about this; it requires the handlers to be ternary callables
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
So I think what I'd really need to do is eval that with the normal globals() but with a locals that was an empty map; this way I could have the try_block be composed by spexy on a per-try-basis, with each of the possible exception types embedded there. This saves me from having to write my own dispatching-by-type.
I'd also make the various except_clause handler functions accept the triplet from sys.exc_info() rather than require them to mine it themselves.
I have to say that any place where I have to use compile/eval feels like cheating. I wish I could find something that worked like my _try function that already existed in base Python... maybe there is such and I simply haven't found it yet?