pymacs! :-)

1 view
Skip to first unread message

François Pinard

unread,
Sep 4, 2001, 11:14:13 AM9/4/01
to pytho...@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!

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

Marcin 'Qrczak' Kowalczyk

unread,
Sep 4, 2001, 12:55:46 PM9/4/01
to
04 Sep 2001 11:14:13 -0400, François Pinard <pin...@iro.umontreal.ca> pisze:

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

I would use something like
lisp.variable_name
where lisp is an object with a suitable __getattr__.

--
__("< Marcin Kowalczyk * qrc...@knm.org.pl http://qrczak.ids.net.pl/
\__/
^^ SYGNATURA ZASTĘPCZA
QRCZAK

François Pinard

unread,
Sep 4, 2001, 2:25:19 PM9/4/01
to Marcin 'Qrczak' Kowalczyk, pytho...@python.org
> > 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")'.

[Marcin 'Qrczak' Kowalczyk]

> I would use something like
> lisp.variable_name
> where lisp is an object with a suitable __getattr__.

This traversed my mind. I wanted to avoid such `lisp.' prefix, a bit
because they might become cumbersome, but mostly because they force users
to well remember if a function is implemented in LISP or not, and I would
prefer that this be made a transparent implementation detail.

On the other hand, thinking again about it, I wonder why I hesitate using
`emacs.point()', when it does not bother me to write `string.join(...)'.
There is indeed some name-space cleanliness at sticking to such prefixes.
I'll think more on this. Thanks for the triggering!

Carel Fellinger

unread,
Sep 5, 2001, 2:02:57 PM9/5/01
to
François Pinard <pin...@iro.umontreal.ca> wrote:
...

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

Me too, I even tried to wrap my mind to dig the modes of vim, but in vain.

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

Like Marcin, I would prefer a lisp.fun notation. And as many lisp names use
funny characters like -, I suggest adding a [] interface as well, so we
easily can access those weird emacs names like in

count = lisp["1+"](count)

--
groetjes, carel

François Pinard

unread,
Sep 5, 2001, 6:44:12 PM9/5/01
to Carel Fellinger, pytho...@python.org
[Carel Fellinger]

> Like Marcin, I would prefer a lisp.fun notation. And as many lisp names
> use funny characters like -, I suggest adding a [] interface as well,
> so we easily can access those weird emacs names like in

> count = lisp["1+"](count)

Hello, Carel, and others. Let me thank you, as well as those who wrote
various suggestions to me. I tried to take good care of all ideas. :-)

Yes, I exactly added this (using square brackets) earlier today. Calling
functions as per above was already in place since one day or two. Also,
`lisp' itself can be used as a function to execute raw LISP code, like this:

value = lisp("""
...
POSSIBLY LONG LISP EXPRESSION
...
""")

There is another interesting feature I put in today. Consider:

def procedure(object):
lisp.setcdr(object, 17)

presuming that `object' has been given a cons-cell. I wanted the `setcdr'
to act on the original OBJECT, and not on a copy. So, `object' is a handle
to the LISP argument right in LISP space, whenever that argument is mutable.
A slight difficulty, easily solved, was to make sure those handles are
freed in LISP space in proper time, so they can be garbage collected.

On the debugging side, Python exceptions are properly reported as
tracebacks on the LISP side, while LISP errors occurring while Emacs is
serving Python are more summarily reported to Python and back to LISP.
This should not have adverse effects on later computations (I hope I did
it right). One may add `print' debugging statements in Python, and with
a minimum of care, these will correctly appear in the `*Pymacs*' window.

Some handles could usefully be seen as sequences, I'll explore this.
Also, given handles and symbols have a `.value()' method, I'm thinking
of adding a `.copy()' method for handles, that would return an expanded
copy of the list as usable as possible on the mere Python side, without
further communication. LISP proper lists could become Python tuples, LISP
vectors could become Python lists, and strings might loose their properties.

On summary, that project goes well, is nearly usable, and still small:
pymacs.el is 268 lines, pymacs.py 262 lines, and pymacs-services 11 lines.
The growth may be explained by the fact I added some documentation! :-)

Brian McErlean

unread,
Sep 6, 2001, 5:28:35 AM9/6/01
to
pin...@iro.umontreal.ca (=?iso-8859-1?q?Fran=E7ois?= Pinard) wrote in message news:<mailman.999616296...@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

Alex Martelli

unread,
Sep 6, 2001, 7:46:49 AM9/6/01
to
"Carel Fellinger" <cfel...@iae.nl> wrote in message
news:9n5pch$806$1...@animus.fel.iae.nl...

> François Pinard <pin...@iro.umontreal.ca> wrote:
> ...
> > My real end goal is using Python instead of LISP for an extension
language
> > for Emacs, as needed. I'm jealous of "vim" users! :-)
>
> Me too, I even tried to wrap my mind to dig the modes of vim, but in vain.

I think that's because the mind is not the part that learns the modes: it's
rather the fingers. If I stop to think out "what mode am I in" when
editing,
my performance drops to even lower than it is with non-vi-based editors --
if
I just let my fingers fly and keep my mind on more strategically oriented
issues, such as "what am I supposed to be writing about", then the speed
is excellent and my hands don't get tired as they do with other editors...

It's like walking -- if you think about exactly where and how you should
be moving and placing each foot, you'll walk slowly and haltingly; your
_feet_ are the parts of you that really knows about walking's low-level
issues, so, use your mind for strategic overview instead, don't let it
micro-manage the feet, and stroll happily along:-).


Alex

François Pinard

unread,
Sep 6, 2001, 11:39:18 AM9/6/01
to Brian McErlean, pytho...@python.org
[Brian McErlean]

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

That's why it is a good thing to announce what we are working at, if we
know we have any chance to get through, so to trigger collaboration! :-)

> > 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. [...] Actually,


> I think your way having seperate exec and eval may be a better idea -

So far, on my side, I found out that "python-exec" (I moved "python" as a
prefix rather than a suffix) is not really needed in practice. The most
useful is "python-apply", yet the user almost never use it directly: these
are only used indirectly through the LISP "defun" which get installed in the
LISP space through the action of "python-import". As for "python-eval",
I found it useful for writing quick tests, but I would not think it very
useful in real applications.

In practice on the LISP side, one does "(python-import MODULE)" and merely
use the imported functions, without paying much attention anymore if they
were written in LISP or Python. To make thins a bit more transparent,
I'm pondering some "(autoimport ...)" function to mimick "(autoload ...)",
unless -- this is unlikely -- I find a neat way to overload "(autoload ...)".

> > From the Python side, I may also call Emacs for help:
> > eval_lisp("LISP-EXPRESSION")

Since then, I changed that to the simpler:

from pymacs import lisp
...
value = lisp("LISP-EXPRESSION")

In fact, not much besides `lisp' is needed from the `pymacs' module,
and Python functions not needing Emacs services do not even need `lisp'.
The rest is fairly automatic.

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

I paid attention so opaque LISP objects could be made available on the
Python side, and found back automatically when transmitted back to the
LISP side from Python. You paid attention to the other way, and found
ways to represent non-simple Python objects on the LISP side. So it seems
we solved complementary problems, and this is enough for me to be quite
interested in your works! :-)

I confess that I did not tackle Python opaque representations in LISP,
because I do not see how to avoid memory leaks. If one creates a Python
object meant to be returned on the LISP side, one has to keep a reference
active to that object on the Python side, and later copies of corresponding
references could be made on the LISP side. But then, how do one gets a clue
about when the references on the LISP side still exists or not? Without
clues, I do not see other choices than keeping the Python object around
forever, and it might not be acceptable in long running Emacs sessions,
where a lot of Python objects could be progressively tied in this way.

On the other hand, I easily forgave myself for not doing so, as I was not
loosing so much. I aim writing LISP extensions in the Python language,
much more than give a Python flavour to my LISP writings. After all,
I'd rather write in Python than in LISP whenever I seek a Python flavour.
I think LISP objects like windows, buffers, markers, overlays, etc. should
be fully usable from the Python side, even if this implies a lot of automatic
synchronisation between Emacs and LISP. I guess I reached that goal.

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

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

Currently in my things, the Python backtrace gets displayed in the
mini-buffer (which is resized for the circumstance) *and* the LISP backtrace
is shown in the `*Backtrace*' window. Together with the `*Pymacs*'
communication window, that makes plenty for debugging. I made no kind of
effort to transmit the LISP backtrace on the Python side, as I do not see
a purpose for it: all debugging is done within Emacs windows anyway.

If cross-calls between LISP and Python nest deeply, an error will raise
successive exceptions alternatively on both size as things unstack, and
the diagnostic gets transmitted back and forth, slightly growing as we go.
I hope my logic is correct: these things are a bit complex to sort out
and I did not overly tested it yet.

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

As you might guess, I had to think on similar lines, but was happy to
use `__call__' to sort out if a LISP object is meant as a function or as
a value. Simplifying my code, I found out that the easiest avenue was to
merely shovel that problem on the LISP side, and I guess I did it cleanly.

> 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 `_'?

The automatic conversion is really useful, it is well worth, so I support
it fully on both sides. In my current Emacs here, there are only four
symbols having an underscore: `shift_jis', `shift_jis-dos', `shift_jis-mac',
and `shift_jis-unix', and they are only useful for their property lists.
Because "lisp.shift_jis" would really refer to `shift-jis', in such cases,
I would use "lisp['shift_jis']". Automatic conversion only occurs for
the attribute notation.

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

By all means! Deep down, these are the goal and result which attract me,
not really imposing my ideas. A single better tool has more chance growing
roots, than two competing and lesser packages. You understand that I would
like enjoying some ideas I had, when I dare considering them good ideas! :-)

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

Do you prefer that we post our respective versions and let these influence
our respective designs, or that we seek some integrated solution through
private communications, before posting? Or should we discuss publicly?
It depends a bit on if there are many Emacs users who have practical
interest in writing `.py'/`.pyc' files next to their `.el`/`.elc' files!

Andrei Kulakov

unread,
Sep 6, 2001, 2:24:25 PM9/6/01
to

I agree wholeheartedly, and the only thing I can add is that there is a
certain change of editing strategy that comes after you used a modal
editor like vim for a while: instead of writing and editing at the same
time you learn to split the process in two batches: first you type in a
large body of text, perhaps a paragraph or a page, then you jump into
command mode and see what you need to change and move around, if anything.

It's similar to writing perl in python or vice versa - if you use a modal
editor in a non-modal way you only get a small fraction of benefits, at
best.

- Andrei

--
Cymbaline: intelligent learning mp3 player - python, linux, console.
get it at: cy.silmarill.org

Paul Winkler

unread,
Sep 7, 2001, 5:22:12 PM9/7/01
to
On 06 Sep 2001 11:39:18 -0400, François Pinard <pin...@iro.umontreal.ca> wrote:
(snip)

>It depends a bit on if there are many Emacs users who have practical
>interest in writing `.py'/`.pyc' files next to their `.el`/`.elc' files!

Very much so! I love emacs, but there is a lot I can do in python that
I haven't a clue how to do in emacs lisp. (well, almost anything!)

I hope you'll excuse the lisp-newbie questions, but I think a simple
example would really help me understand how this is going to work.
For example, let's say I have a a module, let's call it manglers.py,
containing this simple python function:

def break_on_whitespace(some_string):
"""Yes, it's trivial."""
words = some_string.split()
return '\n'.join(words)

Now I want to tell Emacs about this function so that I can call it on
a region of text and replace the region with the result of the
call. And bind this action to a key, of course.

So what goes in my .emacs file?
First I import the module:
(import-python 'manglers')

Then I can refer to (manglers-break_on_whitespace), right?
And I think I can bind it to a key like so:
(global-set-key (quote [f7]) (quote manglers-break_on_whitespace))

But how do I pass and replace the current region? I was hoping this
project of yours might enable me to avoid learning any more lisp, but
it looks like I won't be so lucky. :)

--PW


Carel Fellinger

unread,
Sep 7, 2001, 5:13:48 PM9/7/01
to
Alex Martelli <al...@aleax.it> wrote:
> "Carel Fellinger" <cfel...@iae.nl> wrote in message
> news:9n5pch$806$1...@animus.fel.iae.nl...
...

>> Me too, I even tried to wrap my mind to dig the modes of vim, but in vain.

> I think that's because the mind is not the part that learns the modes: it's
> rather the fingers. If I stop to think out "what mode am I in" when

...more belitteling causery snipped

Please Alex, what you are telling here is so obvious that a man
wonders why you bother to tell it at all. And I wonder why I bother
to react.

Some 20 odd years ago I was a reasonable fast vi user, my fingers did
the edit-thinking. But even in those years I frequently stumbled upon
two problematic areas with vi (problematic to *me* that is). The first
being that in insert mode I often needed to edit the line above too. I
had to leave insert mode, go up a line and enter insert mode again. Oh
how I wished for functioning cursor keys. Last year, when my daugthers
had to learn a real editor, I set them off with vim, and discovered
that cursor keys do function in insert mode nowadays [completed with
a number of ugly control key bindings to do word/line/block movements
and edits, just like emacs:]

But this only enhanced my second problem, being that I regularly
forget to enter insert mode when I start inserting after a short pause
and give all kinds of commands instead. Or forget that I left insert
mode and start adding some spurious command letter(s) to my writings.

If I only had that problem now, I could believe that it was a matter
of getting used to. But I know from prior usage of vi that even years
of usage whould not free me from it. And surely after years of vi-ing
one might expect the fingers to take over, like the legs took over the
walking in a few weeks when I was but a baby.

Years of editing in mode-less editors on other platforms only deepened
that problem of keeping track of insert mode on/off. And I do regret
that, because apart from the above, I like vim a lot. I really dig the
command structure, and I love to be able to use python to edit my
sources.

Besides, studies in the UI design field showed that modes are
troublesome, some people can cope with them, but must just get lost.
--
groetjes, carel

François Pinard

unread,
Sep 7, 2001, 9:25:19 PM9/7/01
to Paul Winkler, pytho...@python.org
[Paul Winkler]

> Very much so! I love emacs, but there is a lot I can do in python that
> I haven't a clue how to do in emacs lisp. (well, almost anything!)

It goes without saying that Pymacs is not going to be useful, if it
requires its users to have a deep knowledge of both Emacs LISP and Python.
Brian McErlean and I consider that Python scripting as the main goal for
Pymacs. Hopefully, the Pymacs documentation will contain a few examples
meant for Python users having a limited experience with Emacs.

> For example, let's say I have a a module, let's call it manglers.py,
> containing this simple python function:

> def break_on_whitespace(some_string):
> """Yes, it's trivial."""
> words = some_string.split()
> return '\n'.join(words)

> Now I want to tell Emacs about this function so that I can call it on
> a region of text and replace the region with the result of the
> call. And bind this action to a key, of course.

This is an interesting exercise. Before diving into it, it is clear that
the Emacs buffer should be handled in some way. If this is not on the
lisp side, it has to be on the Python side, but we cannot escape handling
the buffer. So, there is an equilibrium in the work to do for the user,
that could be displaced towards LISP or towards Python.

Let's seek a solution which requires less LISP code, at the expense of
more Python code. This might be the easiest for one who is a Python user
before a LISP writer. For one, I would probably manage so the LISP side
transmit the region as passed as arguments to the Python function, but
let's not even do that, and rather discover the region from Python code.
It might look like this (untested):


from pymacs import lisp

def break_on_whitespace():
start = lisp.point()
end = lisp.mark()
if start > end:
start, end = end, start
text = lisp.buffer_substring(start, end)
words = text.split()
replacement = '\n'.join(words)
lisp.delete_region(start, end)
lisp.insert(replacement)


Note that if I was writing this for myself, for various stylistic reasons,
I might rather do:


from pymacs import lisp
from string import join

def break_on_whitespace():
start, end = lisp.point(), lisp.mark()
words = lisp.buffer_substring(start, end).split()
lisp.delete_region(start, end)
lisp.insert(join(words, '\n'))


relying on the fact that for the used LISP functions, `start' and `end'
may be given in any order.

> So what goes in my .emacs file? First I import the module:
> (import-python 'manglers')

Exactly. Yet, I might rename `import-python' into `python-load'.

> Then I can refer to (manglers-break_on_whitespace), right?

Almost. An automatic underline to dash translation occurs while the module is
imported into Emacs, so use `manglers-break-on-whitespace'.

> And I think I can bind it to a key like so:
> (global-set-key (quote [f7]) (quote manglers-break_on_whitespace))

Essentially, yes. The usual writing would be:

(global-set-key [f7] 'manglers-break-on-whitespace)

as vectors quote themselves, and the apostrophe is easier on the eye! :-)
It should be even possible to bind the key from the Python side, maybe
using something like this, at the left margin, near the end of the module:


q = lisp.quote
lisp.global_set_key([lisp.f7], q(lisp.manglers_break_on_whitespace))


There is one thing that I know is currently missing, and that would prevent
to above to work today. At `python-load' time, the function will not have
been marked "(interactive)", and I guess this is required for the function
to be usefully bound to a key. I hope I'll find a nice way for this,
with the enlightening suggestions of this forum, of course! :-)

François Pinard

unread,
Sep 8, 2001, 8:50:56 AM9/8/01
to Paul Winkler, pytho...@python.org
> [Paul Winkler]

> > For example, let's say I have a a module, let's call it manglers.py,
> > containing this simple python function:

> > def break_on_whitespace(some_string):
> > """Yes, it's trivial."""
> > words = some_string.split()
> > return '\n'.join(words)

> > Now I want to tell Emacs about this function so that I can call it on
> > a region of text and replace the region with the result of the
> > call. And bind this action to a key, of course.

Hello, Paul. I would like to add some further comments on your problem.
Yesterday, I wrote:

> q = lisp.quote
> lisp.global_set_key([lisp.f7], q(lisp.manglers_break_on_whitespace))

I might have been tired, or sleeping. That's useless complexity.
Just write:

lisp.global_set_key([lisp.f7], lisp.manglers_break_on_whitespace)

Since arguments have been evaluated the Python way on the Python side,
it would be conceptual overkill evaluating them again the LISP way on the
LISP side, so Pymacs already quotes arguments to defeat LISP evaluation.
The same logic applies, the other way around.

> There is one thing that I know is currently missing, and that would
> prevent to above to work today. At `python-load' time, the function will
> not have been marked "(interactive)", and I guess this is required for
> the function to be usefully bound to a key. I hope I'll find a nice way
> for this, with the enlightening suggestions of this forum, of course! :-)

Thinking a bit more about this, this is a difficult problem, as Emacs
functions have the concept of user interaction for completing the
specification of their arguments while being called. It would be very
unnatural to Python, trying to retrofit that facility on the Python side.
Best might be to write another trampoline, like this:


(pymacs-load "manglers")

(defun break-on-whitespace ()
(interactive)
(manglers-break-on-whitespace))

(global-set-key [f7] 'break-on-whitespace)


This is a bit more LISP to write, but I hope not too frightening...

Paul Winkler

unread,
Sep 8, 2001, 11:51:47 AM9/8/01
to François Pinard, pytho...@python.org
Thanks Francois, that gives me a good idea how this should work. I
expect to be using pymacs quite a lot!

On Sat, Sep 08, 2001 at 08:50:56AM -0400, François Pinard wrote:
> Thinking a bit more about this, this is a difficult problem, as Emacs
> functions have the concept of user interaction for completing the
> specification of their arguments while being called. It would be very
> unnatural to Python, trying to retrofit that facility on the Python side.
> Best might be to write another trampoline, like this:
>
>
> (pymacs-load "manglers")
>
> (defun break-on-whitespace ()
> (interactive)
> (manglers-break-on-whitespace))
>
> (global-set-key [f7] 'break-on-whitespace)
>
>
> This is a bit more LISP to write, but I hope not too frightening...

Not too bad... it's a small enough chunk that we can just parrot the
examples without worrying so much about what it all means.

One quibble if you do work out how to do this on the Python side. You
had written:

lisp.global_set_key([lisp.f7], lisp.manglers_break_on_whitespace)

So you want to put all the possible keys in the lisp namespace? Seems
messy. And what about key combinations? How would you say something
like 'C-x w'?

I would suggest simply writing them as strings, and the programmer
will have to know the elisp way of naming them. Example:

lisp.global_set_key(['C-x w'], lisp.manglers_break_on_whitespace)


--
................... paul winkler ....................
custom calendars & printing: http://www.calendargalaxy.com
A member of ARMS: http://www.reacharms.com
home page: http://www.slinkp.com

François Pinard

unread,
Sep 8, 2001, 12:40:09 PM9/8/01
to Paul Winkler, pytho...@python.org
[Paul Winkler]

> You had written:

> lisp.global_set_key([lisp.f7], lisp.manglers_break_on_whitespace)

> So you want to put all the possible keys in the lisp namespace? Seems
> messy.

My knowledge of English fails on me, here. I do not understand your comment.
Would you be kind enough to explain?

Maybe you are worried with `[lisp.f7]' ? There is nothing special with it.
[item] is a Python list, which yields a LISP vector. `lisp.f7' merely
translates to the LISP symbol `f7'. So, Python `[lisp.f7]' is LISP `[f7]'.

> And what about key combinations? How would you say something like 'C-x w'?
> I would suggest simply writing them as strings, and the programmer
> will have to know the elisp way of naming them. Example:

> lisp.global_set_key(['C-x w'], lisp.manglers_break_on_whitespace)

In LISP, this would be written:

(global-set-key "\C-xw" manglers-break-on-whitespace)

Triggering this from Python requires that you write the string the
Python way, and Python does not recognise the "\C-" escape. Knowing
that `C-x' is chr(0x18), one could write:

lisp.global_set_key('\x18w', lisp.manglers_break_on_whitespace)

Maybe it could be useful to have a service function to help writing more
LISP-looking strings in Python? `pymacs' does not have any service library
yet, but if the need arise, we can progressively build one. Maybe there
would not be much things in it, if any. Hard to know in advance.
I'd rather keep the API as bare as possible, if this is reasonable.

Paul Winkler

unread,
Sep 8, 2001, 4:31:55 PM9/8/01
to
On 08 Sep 2001 12:40:09 -0400, François Pinard <pin...@iro.umontreal.ca> wrote:
>[Paul Winkler]
>
>> You had written:
>
>> lisp.global_set_key([lisp.f7], lisp.manglers_break_on_whitespace)
>
>> So you want to put all the possible keys in the lisp namespace? Seems
>> messy.
>
>My knowledge of English fails on me, here. I do not understand your comment.
>Would you be kind enough to explain?
>Maybe you are worried with `[lisp.f7]' ? There is nothing special with it.
>[item] is a Python list, which yields a LISP vector. `lisp.f7' merely
>translates to the LISP symbol `f7'. So, Python `[lisp.f7]' is LISP `[f7]'.

How does that happen? Will you have definitions in lisp.py for every
possible key or key combination? That seems very impractical and
messy.

I have not seen your code, so I don't know what would happen on the
lisp side as a result of calling lisp.global_set_key(), but if it's
simply that we create a string of lisp code and give it back to emacs
for evaluation, then the solution is very easy, and I've already
suggested it. Instead of the first argument being [lisp.f7], it would
be ['f7']. The string would simply be placed in the generated lisp
code in the appropriate place. This way, we would use the same
keybinding syntax in Python as we use in emacs. So to take a real
example from my .emacs file:

(global-set-key [M-f2] 'redo)

... might become something like, on the python side,

lisp.global_set_key(['M-f2'], lisp.redo)


>> And what about key combinations? How would you say something like 'C-x w'?
>> I would suggest simply writing them as strings, and the programmer
>> will have to know the elisp way of naming them. Example:
>
>> lisp.global_set_key(['C-x w'], lisp.manglers_break_on_whitespace)
>
>In LISP, this would be written:
>
> (global-set-key "\C-xw" manglers-break-on-whitespace)
>
>Triggering this from Python requires that you write the string the
>Python way, and Python does not recognise the "\C-" escape. Knowing
>that `C-x' is chr(0x18), one could write:
>
> lisp.global_set_key('\x18w', lisp.manglers_break_on_whitespace)

IMHO this is a heavy burden to place on the person trying to write
python functions. I'm no dummy, I've been using linux and emacs for 5
years, writing python for 2 years, and I have no idea how I would find
out that C-x is equivalent to chr(0x18). It's not listed as such in
the ascii man page. Furthermore, there must be many combinations of
control, meta, and other keys that have no single-character
equivalent, so this is not a general solution.

>Maybe it could be useful to have a service function to help writing more
>LISP-looking strings in Python?

Yes, that's exactly what I'm suggesting.

--PW

François Pinard

unread,
Sep 8, 2001, 7:04:26 PM9/8/01
to Paul Winkler, pytho...@python.org
[Paul Winkler]

> > [item] is a Python list, which yields a LISP vector. `lisp.f7' merely
> > translates to the LISP symbol `f7'. So, Python `[lisp.f7]' is LISP
> > `[f7]'.

> How does that happen? Will you have definitions in lisp.py for every
> possible key or key combination?

Oh! God no! :-)

`lisp' is not a module, it is a special object having some Python magic
built in it. I may explain how it works if you are curious, but one should
be able to use it a bit blindly, or at least, I hope so. :-)

`lisp' is an instance of the `pymacs.Lisp' class. Whenever you write
`lisp.SYMBOL', you are calling the `__getattr__' method of that class.
That metho checks if the wanted symbol has already been created, in which
case it returns it, otherwise, it creates a new one and returns it. A symbol
is an instance of the `pymacs.Symbol' class (which also has its own magic!).
Whenever such a symbol gets transmitted to the Emacs side, the communication
protocol takes care that it produces the proper corresponding LISP symbol.

> I have not seen your code

If you feel audacious, it is available on request. I started integrating
some of Brian McErlean's good ideas (he really gave me wonderful clues),
and plan to start using it in production for myself in a few hours.
There is not much left to do now. Until users complain, of course! :-)

> Instead of the first argument being [lisp.f7], it would be ['f7'].

There is a deep difference between a symbol and a string, in Emacs, like
in Python, but you know this. :-) Python `lisp.f7' corresponds to the LISP
symbol `f7', Python `"f7"' corresponds to the LISP string `"f7"'.

> So to take a real example from my .emacs file:
> (global-set-key [M-f2] 'redo)
> ... might become something like, on the python side,
> lisp.global_set_key(['M-f2'], lisp.redo)

I'm not utterly familiar with how `[SYMBOL]' works on the Emacs side to
represent key-bindings, but I would guess Emacs developers made a few
stunts for it to look good. For what I guess these stunts are, I would
write on the Python side:

lisp.global_set_key([lisp['M-f2']], lisp.redo)

or even more tricky:

lisp.global_set_key([lisp.M_f2], lisp.redo)

> > (global-set-key "\C-xw" manglers-break-on-whitespace)
> > [to become]
> > lisp.global_set_key('\x18w', lisp.manglers_break_on_whitespace)

> IMHO this is a heavy burden to place on the person trying to write
> python functions.

Really? We'll have to do something about it, then.

> I have no idea how I would find out that C-x is equivalent to chr(0x18).

Oh! `C-x' stands for Control-X. In an ASCII table, there are control
characters from 0x00 to 0x20 (excluded). 0x01 is Control-A, 0x02 is
Control-B, etc. Knowing this, Control-X is: chr(ord('X') - ord('A') + 1).
Give the above to an interactive Python session, you will get: '\x18'.

> Furthermore, there must be many combinations of control, meta, and

> other keys that have no single-character equivalent [...]

Control is the equivalent of subtracting 0x40 from the upper case letter.
Meta is the equivalent of adding 0x80 to the rest. However, so people can
use non-ASCII charsets, Meta is given another representation in key maps,
and this is to prefix the character to "meta" in the string with `ESC',
which is 0x1b. That's all, I think, for when users specify strings.
For the rest, Emacs uses the vector notation, with no strings.

> >Maybe it could be useful to have a service function to help writing more
> >LISP-looking strings in Python?

> Yes, that's exactly what I'm suggesting.

Then this routine -- let's tentatively call it `pymacs.key()' -- and the
convention for writing its argument, should be precisely documented.

Andrei Kulakov

unread,
Sep 8, 2001, 8:27:48 PM9/8/01
to
On 7 Sep 2001 23:13:48 +0200, Carel Fellinger <cfel...@iae.nl> wrote:
> Alex Martelli <al...@aleax.it> wrote:
>> "Carel Fellinger" <cfel...@iae.nl> wrote in message
>> news:9n5pch$806$1...@animus.fel.iae.nl...
> But this only enhanced my second problem, being that I regularly
> forget to enter insert mode when I start inserting after a short pause
> and give all kinds of commands instead. Or forget that I left insert
> mode and start adding some spurious command letter(s) to my writings.

This happens very rarely to me, not enough to really bother me but enough
to make me wonder why there isn't an option to make the status bar
different colors in different modes, for instance green in insert and blue
in command mode. That could be very helpful to newbies and pro's alike.

> Besides, studies in the UI design field showed that modes are
> troublesome, some people can cope with them, but must just get lost.

Modality is everywhere, the real issue imho is making it obvious which
mode the user is in, and make mistakes as harmless as possible. Vim is
fairly good on both counts, although as I mention above the former can be
more noticeable, but the latter.. I never lost anything because I thought
I'm in the other mode, the only thing that annoys me and should be fixed
imho is that when you hit caps lock accidentally, commands do unexpected
things. I got tripped up on that a few times.

Steffen Ries

unread,
Sep 9, 2001, 8:13:55 AM9/9/01
to
pin...@iro.umontreal.ca (François Pinard) writes:

> I'm not utterly familiar with how `[SYMBOL]' works on the Emacs side to
> represent key-bindings, but I would guess Emacs developers made a few
> stunts for it to look good. For what I guess these stunts are, I would
> write on the Python side:
>
> lisp.global_set_key([lisp['M-f2']], lisp.redo)
>
> or even more tricky:
>
> lisp.global_set_key([lisp.M_f2], lisp.redo)
>
> > > (global-set-key "\C-xw" manglers-break-on-whitespace)
> > > [to become]
> > > lisp.global_set_key('\x18w', lisp.manglers_break_on_whitespace)
>
> > IMHO this is a heavy burden to place on the person trying to write
> > python functions.
>
> Really? We'll have to do something about it, then.

I think with emacs-20 the macro `kbd' was introduced to translate
strings like 'M-<f2>' or 'C-x w' to key bindings. I haven't followed the
emacs news groups for a while so I don't know if anybody besides me is
actually using it :-)

The example above could be written as:

(global-set-key (kbd "C-x w") manglers-break-on-whitespace)

-->

lisp.global_set_key(lisp.kbd('C-x w'), lisp.manglers_break_on_whitespace)

/steffen
--
steffe...@sympatico.ca <> Gravity is a myth -- the Earth sucks!

François Pinard

unread,
Sep 9, 2001, 9:47:50 AM9/9/01
to Steffen Ries, pytho...@python.org
[Steffen Ries]

> I think with emacs-20 the macro `kbd' was introduced to translate
> strings like 'M-<f2>' or 'C-x w' to key bindings.

Thanks for this pointer. I'm adding it to the Pymacs documentation!

Paul Winkler

unread,
Sep 9, 2001, 4:30:52 PM9/9/01
to
On 08 Sep 2001 19:04:26 -0400, François Pinard <pin...@iro.umontreal.ca> wrote:
(snip)

>`lisp' is not a module, it is a special object having some Python magic
>built in it. I may explain how it works if you are curious, but one should
>be able to use it a bit blindly, or at least, I hope so. :-)

Ah, OK. I assumed it was a module. This is interesting, and much
better than what I thought you were doing.

>> I have not seen your code
>
>If you feel audacious, it is available on request.

I think I'll wait a bit longer - I have no especially urgent need to
extend emacs.

>There is a deep difference between a symbol and a string, in Emacs, like
>in Python, but you know this. :-) Python `lisp.f7' corresponds to the LISP
>symbol `f7', Python `"f7"' corresponds to the LISP string `"f7"'.

Yes, I see why this matters now. I thought you were writing python
functions in a lisp.py module to generate strings of lisp code; I
hadn't realized you were providing much more direct access to emacs
and just translating the names appropriately.

>I'm not utterly familiar with how `[SYMBOL]' works on the Emacs side to
>represent key-bindings, but I would guess Emacs developers made a few
>stunts for it to look good. For what I guess these stunts are, I would
>write on the Python side:
>
> lisp.global_set_key([lisp['M-f2']], lisp.redo)
>
>or even more tricky:
>
> lisp.global_set_key([lisp.M_f2], lisp.redo)

I think either way would be fine.


>> > (global-set-key "\C-xw" manglers-break-on-whitespace)
>> > [to become]
>> > lisp.global_set_key('\x18w', lisp.manglers_break_on_whitespace)
>
>> IMHO this is a heavy burden to place on the person trying to write
>> python functions.
>
>Really? We'll have to do something about it, then.

Yes... see below.

(explanation of control and meta codes snipped)

That's all good information, but is it really common knowledge? (I'm
serious; that's not a rhetorical question.)

My point is that emacs does not require you to know any of that to
assign keybindings. You just use the same notation that's used
throughout the emacs documentation. This makes it easy to learn, easy
to read. When I look at .el files, I know what keys they're talking
about even if I don't understand much else. It stands to reason
(to me) that pymacs should not require you to use a different
notation.

>> >Maybe it could be useful to have a service function to help writing more
>> >LISP-looking strings in Python?
>
>> Yes, that's exactly what I'm suggesting.
>
>Then this routine -- let's tentatively call it `pymacs.key()' -- and the
>convention for writing its argument, should be precisely documented.

Of course.

--PW


François Pinard

unread,
Sep 9, 2001, 6:21:39 PM9/9/01
to Paul Winkler, pytho...@python.org
[Paul Winkler]

> (explanation of control and meta codes snipped)

> That's all good information, but is it really common knowledge? (I'm
> serious; that's not a rhetorical question.)

I thought it was rather common knowledge. On the other hand, I also
thought it should not be, because it is anachronic (in my opinion) that
people still have a need, nowadays, to understand the internal structure
and properties of charsets.

In particular, I'm more than tired with octal and hexadecimal notations.
They surely have an historical interest, I would not deny this. There was
a time people were writing in assembler or punching wires in BCD machines,
and these people needed to understand bits structures and electron flows.
But this is all past, so why still burden people with these? Decimal numbers
are just fine: almost everything is table driven anyway (or should be).

So, I struggle in my things and around to fully avoid such notations, and
convinced many people around me. Not Richard Stallman, however, who stiffly
wants `C-q' to be followed by octal notations -- to take a simple example.
Sadly enough, I really lost my cause with Unicode people, where nobody was
willing to listen to my arguments. As things stand by now, we cannot really
work with Unicode without having to suffer hexadecimal all over the place.

Finally, I gave up, and my `recode' program which once was perfectly clean
in that respect, is now full of hexadecimal notations to support Unicode.
I guess we need a dozen more years, or so, before hoping any improvement.
In the meantime, let's continue eating bits with a small spoon :-(.

> >Then this routine -- let's tentatively call it `pymacs.key()' -- and the
> >convention for writing its argument, should be precisely documented.

> Of course.

As Steffen Ries pointed out, we can nicely use `lisp.kbd()' for that.

Reply all
Reply to author
Forward
0 new messages