pymacs! :-)

François Pinard pinard at iro.umontreal.ca
Tue Sep 4 11:14:13 EDT 2001


Hello, my friends.

It was an holiday yesterday, and I dived into writing `pymacs'.  This tool
allows for using Python as if it were part of Emacs LISP.  I merely revisited
a good idea from Cedric Adjih, who published `pyemacs' about three years
ago, and spiced with a few simplification ideas on my own.  It seems to work!

The whole thing currently stands in three files:

   pymacs.el (176 lines)          the Emacs side
   pymacs.py (124 lines)          the Python side
   pymacs-services (11 lines)     a small bootstrap

Well, well, there also are 3 autloload lines in my ".emacs" file :-).
The thing being simpler, I removed the `e', so `pyemacs' became `pymacs'!

Here is a short example.  In Emacs "*scratch*", I can do:

---------------------------------------------------------------------->
(import-python "os")
nil
(os-listdir ".")
[".nnmail-cache" "wdiff" "emacs" "struct" "dmf" "suse" "perl2py" "po-mode" ".gdb_history" ".xsession-errors" "po-utils" ".kermit" ...]
----------------------------------------------------------------------<

So, I may import a module directly within Emacs, after which I can refer to
it using some prefix.  A second argument to `import-python' would allow
for choosing a specific prefix.  That `os-listdir' command returns an
Emacs LISP vector, which is consequently usable on the Emacs side.

In LISP, I also gave me some more direct tools:

   (exec-python "PYTHON-STATEMENTS")
   (eval-python "PYTHON-EXPRESSION")
   (apply-python FUNCTION ARGUMENTS)

>From the Python side, I may also call Emacs for help:

   eval_lisp("LISP-EXPRESSION")

This last function is only usable when the original call, or control of
initiative, comes from Emacs; it would not allow an autonomous Python
program to call Emacs as a sub-routine.  Here is a more hairy example:

---------------------------------------------------------------------->
(eval-python "eval_lisp('(eval-python \"`2L**111`\")')")
"2596148429267413814265248164610048L"
----------------------------------------------------------------------<

Here, I'm asking Emacs to ask Python to ask Emacs to ask Python for a simple
bignum computation!  Note that Emacs does not natively know how to handle
big integers, nor has an internal representation for them.  This is why
I use backticks, so Python returns a string representation of the result,
instead of the result itself.

The thing seems to be fast enough!  Here is a trace for the last example.
The `<' symbol flags a message going from Python to Emacs, the '>' from
Emacs to Python.  The small number gives the length of the message.

---------------------------------------------------------------------->
<9	(started)
>49	eval("eval_lisp('(eval-python \"`2L**111`\")')")
<25	(eval-python "`2L**111`")
>18	eval("`2L**111`")
<47	(reply . "2596148429267413814265248164610048L")
>45	reply("2596148429267413814265248164610048L")
<47	(reply . "2596148429267413814265248164610048L")
----------------------------------------------------------------------<

My real end goal is using Python instead of LISP for an extension language
for Emacs, as needed.  I'm jealous of "vim" users! :-)

Two things are missing before I'm happy.  First, this is all too fragile
to errors: a Python exception should properly be seen on the Emacs side,
and vice-versa.  Second, I'd like to find a way for any function or variable
which is not defined in Python to be resolved within Emacs if possible: the
idea is to use Emacs elegantly from Python, using Python syntax for function
calls, without explicitly resorting to `eval_lisp("LISP-EXPRESSION")'.

We could progressively build a cache about whether LISP symbols are `boundp'
or `fboundp' or not, and use that information afterwards.  (Underlines would
be translated to dashes, of course.)  For the rest, I wonder if I could not
replace __builtins__ with an instance of a class of my own.  Any other idea?

-- 
François Pinard   http://www.iro.umontreal.ca/~pinard




More information about the Python-list mailing list