pymacs! :-)

Brian McErlean brianmce at crosswinds.net
Thu Sep 6 05:28:35 EDT 2001


pinard at iro.umontreal.ca (=?iso-8859-1?q?Fran=E7ois?= Pinard) wrote in message news:<mailman.999616296.17057.python-list at python.org>...
> 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!
 
Oh dear - oddly enough I'm currently writing exactly the same thing - 
I'd even decided to call it pymacs :-)  Rather worryingly, it looks 
like I've even implemented it in a similar way.  

> ---------------------------------------------------------------------->
> (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)

I just have one function, (pymacs-eval
"python-statement-or-expression").
This evaluates either, but returns nil for a statement.  Functions are
handled as normal lisp functions:

(funcall (pymacs-eval "lambda x:x+1") 4)   ;; returns 5
or
(pymacs-eval "emacs.defun('inc',lambda x:x+1)")
(inc 4)  ;; returns 5

Actually, I think your way having seperate exec and eval may be a
better
idea - my way tries eval, and then exec on a SyntaxError, which is 
probably less efficient.

> 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:

Pretty much the same here:
(pymacs-eval "emacs.eval('(pymacs-eval \'2+1\')')")

And similarly, you can't do anything like call to emacs from another
thread - emacs does the "driving", and python only responds, though 
possibly with another call.

> ---------------------------------------------------------------------->
> (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")
> ----------------------------------------------------------------------<

I think you've the edge on me here - my traces tend to look a bit more
messy, since there are several functions doing the work, and values
can
get passed all over the place before they are sent.  
Sometimes doing seemingly simple things can mean a lot of work - eg.

(funcall (pymacs-eval "map") (pymacs-eval "lambda x:x+1") (1 2 3 4 5))

works, but causes a lot of traffic between emacs and python 
(several calls between the two for every item in the list).


> 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

My error handling is fairly simple really - I have a special character
I
return to python as the response to a call - ">" indicates emacs is 
makeing another call to python, "&" indicates a response to pythons
call,
and "!" indicates an error, after which I just throw a LispError() - 
which will in turn be passed to emacs using an error code.  It would
be nice to provide some kind of backtrace up both emacs and python
calls
though.

> 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")'.

I have several functions on the emacs class:

emacs.get_func(name)  - looks up (symbol-function 'name) and returns
it, or None
emacs.get_value(name) - just calls emacs.eval(name)
and __getitem__       - return get_func(name) or get_value(name)

This lets you do:

emacs["mapcar"](lambda x:x+1, emacs.eval("(1 2 3 4)"))
 
> 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?

I considered using automatic _ to - and having syntax like
emacs.add_hook,
using __getattr__ but I didn't like the automatic conversion - what do
you
do for functions that do contain "_"?

Your implementation does sound very interesting though - maybe we
should
collaborate and take the best of our respective designs?

Currently, I had planned on releasing my version sometime next week.  
I don't have the source with me at work, but I'll see about maybe
posting a preliminary release sometime soon.

-- 
Brian McErlean



More information about the Python-list mailing list