Skip to content

Instantly share code, notes, and snippets.

@obriencj
Last active January 4, 2016 10:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save obriencj/8610594 to your computer and use it in GitHub Desktop.
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.
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!
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-----"
print
tree = spexy.build_tree(StringIO(src))[0]
print "parses into tree\n-----\n", tree, "\n-----"
print
pycode = spexy.evaluate(spexy.ns_new(), tree)
print "processed into Python\n-----\n", pycode, "\n-----"
print
# 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
print "division(4, 2)"
result = division(4, 2)
print "result:", result
print
print "division(2, 0)"
result = division(2, 0)
print "result:", result
print
print "it works!"
@obriencj
Copy link
Author

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?

@obriencj
Copy link
Author

@obriencj
Copy link
Author

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

@obriencj
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment