| description | A bridge between Python and Lisp (FFI bindings, etc.) |
| last change | Fri, 20 Apr 2012 19:10:18 |
| url | http://repo.kepibu.org/burgled-batteries/ |
| project url | http://code.kepibu.org/burgled-batteries/ |
| mailing list url | http://lists.kepibu.org/listinfo/burgled-batteries |
burgled-batteries: A Common Lisp / Python Bridge
burgled-batteries provides a shim between Python (specifically, the CPython implementation of Python) and Common Lisp.
(asdf:load-system "burgled-batteries") (in-package #:burgled-batteries) (startup-python) (run "1+1") ; => 2 (import "feedparser") (defpyfun "feedparser.parse" (thing)) (documentation 'feedparser.parse 'function) ; => "Parse a feed from a URL, file, stream, or string" (feedparser.parse "http://pinterface.livejournal.com/data/atom") ; => #<HASH-TABLE> (shutdown-python)
CLPython is great when it works. However, if you’re using a low-resource computer—perhaps an underpowered VPS, or a Linux box salvaged from the 90s—, or need access to a Python library written in C, or there’s a bug and you can’t be bothered to narrow it down to a small test case, CLPython can’t help you. Two out of three of those are more your problem than CLPython’s, but hey, I’m not here to judge.
While a number of other Python-by-FFI options exist, burgled-batteries aims for a CLPython-esque level of integration. In other words, deep integration. You shouldn’t have to care that the library you’re using was written in Python—it should Just Work.
Certainly, b-b is not there yet. It may never be there completely. But we’ll try, dagnabbit.
Python objects are converted to a Lisp object where possible. Where a conversion is unknown, a pointer to the CPython object is returned (or, if inside a refcnt barrier, a wrapper around the pointer which will become invalid upon exiting the barrier). In general, this mapping follows the lead of CLPython.
| Python Type | Lisp Type |
|---|---|
| Boolean | (member T NIL) |
| Integer, Long | Integer |
| Float | Double-float |
| Dict | Hashtable |
| Unicode | String |
| List | Adjustable vector |
| Tuple | List |
| Complex | Complex |
| ByteArray | Octet vector |
| Exception | Condition |
| <Unknown> | <pointer> |
Anything dealing with the CPython API can be found in the PYTHON.CFFI package. See the docstring for that package for more information, as well as Python’s C API.
Not yet supported, but see ffi-callbacks.lisp for some experimentation and notes along those lines.
Because dealing with reference counts is Just No Fun and Not Lispy At All, as well as Inevitable—at some point there will be an object for which no translation is known—, b-b provides multiple options to avoid dealing with refcnts for those untranslatable pointers. See the macro CPYTHON:WITH-UNKNOWN-TRANSLATION-POLICY.
Note that this policy also also affects the EXCEPTION-* slots of PYTHON-CONDITION, and so they may or may not be available for inspection depending on the translation policy in effect and the manner of handling.
For example, under the default policy of :DISCARD, you would see something like:
(defun reveal-effect (c) (format t "~A~%" (slot-value c 'exception-type))) (handler-bind ((python-condition #'reveal-effect)) (burgled-batteries:run "1/0")) ; prints #.(SB-SYS:INT-SAP #X?????) (handler-case (burgled-batteries:run "1/0") (python-condition (c) (reveal-effect c))) ; prints NIL
If you’d like access to Python types without a known translation, :BARRIER or :FINALIZE are highly recommended over :PASS-THROUGH. They do, however, come with some caveats which you should be aware of.