2
# -*- coding: utf-8 -*-
3
# Copyright © 2001, 2002, 2003 Progiciels Bourbeau-Pinard inc.
4
# François Pinard <pinard@iro.umontreal.ca>, 2001.
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2, or (at your option)
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
# GNU General Public License for more details.
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software Foundation,
18
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
21
Interface between Emacs Lisp and Python - Python part.
23
Emacs may launch this module as a stand-alone program, in which case it
24
acts as a server of Python facilities for that Emacs session, reading
25
requests from standard input and writing replies on standard output.
26
When used in this way, the program is called "the Pymacs helper".
28
This module may also be usefully imported by those other Python modules.
29
See the Pymacs documentation (in `README') for more information.
35
old_style_exception = not isinstance(Exception, type)
37
## Python services for Emacs applications.
43
def main(self, *arguments):
45
Execute Python services for Emacs, and Emacs services for Python.
46
This program is meant to be called from Emacs, using `pymacs.el'.
49
-d FILE Debug the protocol to FILE.
50
-s FILE Trace received signals to FILE.
52
Arguments are added to the search path for Python modules.
55
arguments = (os.environ.get('PYMACS_OPTIONS', '').split()
58
options, arguments = getopt.getopt(arguments, 'd:s:')
59
for option, value in options:
61
self.debug_file = value
63
self.signal_file = value
65
for argument in arguments:
66
if os.path.isdir(argument):
67
sys.path.insert(0, argument)
70
self.original_handler = signal.signal(
71
signal.SIGINT, self.interrupt_handler)
72
for counter in range(1, signal.NSIG):
73
if counter == signal.SIGINT:
74
self.original_handler = signal.signal(counter,
75
self.interrupt_handler)
77
# The following few lines of code are reported to create IO
78
# problems within the Pymacs helper itself, so I merely comment
79
# them for now, until we know better.
83
# signal.signal(counter, self.generic_handler)
84
# except RuntimeError:
86
self.inhibit_quit = True
87
# Start protocol and services.
88
from Pymacs import __version__
89
lisp._protocol.send('version', '"%s"' % __version__)
92
def generic_handler(self, number, frame):
94
file(self.signal_file, 'a').write('%d\n' % number)
96
def interrupt_handler(self, number, frame):
98
star = (' *', '')[self.inhibit_quit]
99
file(self.signal_file, 'a').write('%d%s\n' % (number, star))
100
if not self.inhibit_quit:
101
self.original_handler(number, frame)
106
if old_style_exception:
107
ProtocolError = 'ProtocolError'
108
ZombieError = 'ZombieError'
110
class error(Exception): pass
111
class ProtocolError(error): pass
112
class ZombieError(error): pass
116
# All exec's and eval's triggered from the Emacs side are all executed
117
# within the "loop" method below, so all user context is kept as
118
# local variables within this single routine. Different instances
119
# of this Protocol class would yield independant evaluation contexts.
120
# But in the usual case, there is only one such instance kept within a
121
# Lisp_Interface instance, and the "lisp" global variable within this
122
# module holds such a Lisp_Interface instance.
128
# The server loop repeatedly receives a request from Emacs and
129
# returns a response, which is either the value of the received
130
# Python expression, or the Python traceback if an error occurs
131
# while evaluating the expression.
133
# The server loop may also be executed, as a recursive invocation,
134
# in the context of Emacs serving a Python request. In which
135
# case, we might also receive a notification from Emacs telling
136
# that the reply has been transmitted, or that an error occurred.
137
# A reply notification from Emacs interrupts the loop: the result
138
# of this function is then the value returned from Emacs.
142
action, text = self.receive()
146
run.inhibit_quit = False
149
run.inhibit_quit = True
150
elif action == 'exec':
154
run.inhibit_quit = False
157
run.inhibit_quit = True
158
elif action == 'return':
161
run.inhibit_quit = False
164
run.inhibit_quit = True
165
elif action == 'raise':
167
value = 'Emacs: ' + text
169
if old_style_exception:
170
raise ProtocolError, "Unknown action %r" % action
171
raise ProtocolError("Unknown action %r" % action)
172
except KeyboardInterrupt:
176
value = '*Interrupted*'
177
except ProtocolError, exception:
178
sys.exit("Protocol error: %s\n" % exception)
180
import StringIO, traceback
181
buffer = StringIO.StringIO()
182
traceback.print_exc(file=buffer)
184
value = buffer.getvalue()
187
print_lisp(value, fragments.append, True)
188
self.send(action, ''.join(fragments))
192
# Receive a Python expression from Emacs, return (ACTION, TEXT).
193
prefix = sys.stdin.read(3)
194
if not prefix or prefix[0] != '>':
195
if old_style_exception:
196
raise ProtocolError, "`>' expected."
197
raise ProtocolError("`>' expected.")
198
while prefix[-1] != '\t':
199
character = sys.stdin.read(1)
201
if old_style_exception:
202
raise ProtocolError, "Empty stdin read."
203
raise ProtocolError("Empty stdin read.")
205
text = sys.stdin.read(int(prefix[1:-1]))
206
if run.debug_file is not None:
207
file(run.debug_file, 'a').write(prefix + text)
208
return text.split(None, 1)
210
def send(self, action, text):
211
# Send ACTION and its TEXT argument to Emacs.
213
# All delayed Lisp cleanup is piggied back on the transmission.
214
text = ('(free (%s) %s %s)\n'
215
% (' '.join(map(str, self.freed)), action, text))
218
text = '(%s %s)\n' % (action, text)
219
prefix = '<%d\t' % len(text)
220
if run.debug_file is not None:
221
file(run.debug_file, 'a').write(prefix + text)
222
sys.stdout.write(prefix + text)
225
def pymacs_load_helper(file_without_extension, prefix):
226
# This function imports a Python module, then returns a Lisp expression
227
# which, when later evaluated, will install trampoline definitions in
228
# Emacs for accessing the Python module facilities. MODULE may be a
229
# full path, yet without the `.py' or `.pyc' extension, in which case
230
# the directory is temporarily added to the Python search path for
231
# the sole duration of that import. All defined symbols on the Lisp
232
# side have have PREFIX prepended, and have Python underlines in Python
233
# turned into dashes. If PREFIX is None, it then defaults to the base
234
# name of MODULE with underlines turned to dashes, followed by a dash.
235
directory, module_name = os.path.split(file_without_extension)
236
module_components = module_name.split('.')
238
prefix = module_components[-1].replace('_', '-') + '-'
240
object = sys.modules.get(module_name)
246
sys.path.insert(0, directory)
247
object = __import__(module_name)
251
# Whenever MODULE_NAME is of the form [PACKAGE.]...MODULE,
252
# __import__ returns the outer PACKAGE, not the module.
253
for component in module_components[1:]:
254
object = getattr(object, component)
257
load_hook = object.__dict__.get('pymacs_load_hook')
260
interactions = object.__dict__.get('interactions', {})
261
if not isinstance(interactions, dict):
264
for name, value in object.__dict__.items():
265
if callable(value) and value is not lisp:
266
arguments.append(allocate_python(value))
267
arguments.append(lisp[prefix + name.replace('_', '-')])
269
interaction = value.interaction
270
except AttributeError:
271
interaction = interactions.get(value)
272
if callable(interaction):
273
arguments.append(allocate_python(interaction))
275
arguments.append(interaction)
278
[lisp.pymacs_defuns, [lisp.quote, arguments]],
280
return [lisp.quote, object]
282
def doc_string(object):
283
if hasattr(object, '__doc__'):
284
return object.__doc__
286
## Garbage collection matters.
288
# Many Python types do not have direct Lisp equivalents, and may not be
289
# directly returned to Lisp for this reason. They are rather allocated in
290
# a list of handles, below, and a handle index is used for communication
291
# instead of the Python value. Whenever such a handle is freed from the
292
# Lisp side, its index is added of a freed list for later reuse.
297
def allocate_python(value):
298
assert not isinstance(value, str), (type(value), repr(value))
299
# Allocate some handle to hold VALUE, return its index.
301
index = freed_list[-1]
303
python[index] = value
309
def free_python(*indices):
310
# Return many handles to the pool.
311
for index in indices:
313
freed_list.append(index)
315
def zombie_python(*indices):
316
# Ensure that some handles are _not_ in the pool.
317
for index in indices:
318
while index >= len(python):
319
freed_list.append(len(python))
321
python[index] = zombie
322
freed_list.remove(index)
323
# Merely to make `*Pymacs*' a bit more readable.
326
def zombie(*arguments):
327
# This catch-all function is set as the value for any function which
328
# disappeared with a previous Pymacs helper process, so calling
329
# such a function from Emacs will trigger a decipherable diagnostic.
330
diagnostic = "Object vanished when the Pymacs helper was killed"
331
if lisp.pymacs_dreadful_zombies.value():
332
if old_style_exception:
333
raise ZombieError, diagnostic
334
raise ZombieError(diagnostic)
335
lisp.message(diagnostic)
337
## Emacs services for Python applications.
341
def __init__(self, **keywords):
342
# The stack holds (METHOD, DATA) pairs, where METHOD is the expected
343
# unbound pop_* method, and DATA holds information to be restored.
344
# METHOD may not be bound to the instance, as this would induce
345
# reference cycles, and then, __del__ would not be called timely.
347
self.push(**keywords)
351
self.stack[-1][0](self)
353
def __nonzero__(self):
354
# So stylistic `if let:' executes faster.
357
def push(self, **keywords):
359
for name, value in keywords.items():
360
data.append((name, getattr(lisp, name).value()))
361
setattr(lisp, name, value)
362
self.stack.append((Let.pop, data))
366
method, data = self.stack.pop()
367
assert method == Let.pop, (method, data)
368
for name, value in data:
369
setattr(lisp, name, value)
371
def push_excursion(self):
372
self.stack.append((Let.pop_excursion, (lisp.current_buffer(),
374
lisp.mark_marker())))
377
def pop_excursion(self):
378
method, data = self.stack.pop()
379
assert method == Let.pop_excursion, (method, data)
380
buffer, point_marker, mark_marker = data
381
lisp.set_buffer(buffer)
382
lisp.goto_char(point_marker)
383
lisp.set_mark(mark_marker)
384
lisp.set_marker(point_marker, None)
385
lisp.set_marker(mark_marker, None)
387
def push_match_data(self):
388
self.stack.append((Let.pop_match_data, lisp.match_data()))
391
def pop_match_data(self):
392
method, data = self.stack.pop()
393
assert method == Let.pop_match_data, (method, data)
394
lisp.set_match_data(data)
396
def push_restriction(self):
397
self.stack.append((Let.pop_restriction, (lisp.point_min_marker(),
398
lisp.point_max_marker())))
401
def pop_restriction(self):
402
method, data = self.stack.pop()
403
assert method == Let.pop_restriction, (method, data)
404
point_min_marker, point_max_marker = data
405
lisp.narrow_to_region(point_min_marker, point_max_marker)
406
lisp.set_marker(point_min_marker, None)
407
lisp.set_marker(point_max_marker, None)
409
def push_selected_window(self):
410
self.stack.append((Let.pop_selected_window, lisp.selected_window()))
413
def pop_selected_window(self):
414
method, data = self.stack.pop()
415
assert method == Let.pop_selected_window, (method, data)
416
lisp.select_window(data)
418
def push_window_excursion(self):
419
self.stack.append((Let.pop_window_excursion,
420
lisp.current_window_configuration()))
423
def pop_window_excursion(self):
424
method, data = self.stack.pop()
425
assert method == Let.pop_window_excursion, (method, data)
426
lisp.set_window_configuration(data)
430
def __init__(self, text):
434
return 'lisp[%s]' % repr(self.text)
437
return '\'' + self.text
440
return lisp._eval(self.text)
443
return lisp._expand(self.text)
445
def set(self, value):
447
lisp._eval('(setq %s nil)' % self.text)
450
write = fragments.append
451
write('(progn (setq %s ' % self.text)
452
print_lisp(value, write, True)
454
lisp._eval(''.join(fragments))
456
def __call__(self, *arguments):
458
write = fragments.append
459
write('(%s' % self.text)
460
for argument in arguments:
462
print_lisp(argument, write, True)
464
return lisp._eval(''.join(fragments))
468
def __init__(self, index):
472
lisp._protocol.freed.append(self.index)
475
return ('lisp(%s)' % repr(lisp('(prin1-to-string %s)' % self)))
478
return '(aref pymacs-lisp %d)' % self.index
484
return lisp._expand(str(self))
490
# # So you could do things like
491
# # print >>lisp.current_buffer(), "Hello World"
492
# lisp.insert(text, self)
495
# return lisp.point(self)
499
def __call__(self, *arguments):
501
write = fragments.append
503
for argument in arguments:
505
print_lisp(argument, write, True)
507
return lisp._eval(''.join(fragments))
510
return lisp._eval('(length %s)' % self)
512
def __getitem__(self, key):
513
value = lisp._eval('(nth %d %s)' % (key, self))
514
if value is None and key >= len(self):
515
if old_style_exception:
516
raise IndexError, key
517
raise IndexError(key)
520
def __setitem__(self, key, value):
522
write = fragments.append
523
write('(setcar (nthcdr %d %s) ' % (key, self))
524
print_lisp(value, write, True)
526
lisp._eval(''.join(fragments))
530
def __getitem__(self, key):
532
write = fragments.append
534
print_lisp(key, write, True)
536
return lisp._eval(''.join(fragments))
538
def __setitem__(self, key, value):
540
write = fragments.append
542
print_lisp(key, write, True)
544
print_lisp(value, write, True)
546
lisp._eval(''.join(fragments))
551
return lisp._eval('(length %s)' % self)
553
def __getitem__(self, key):
554
return lisp._eval('(aref %s %d)' % (self, key))
556
def __setitem__(self, key, value):
558
write = fragments.append
559
write('(aset %s %d ' % (self, key))
560
print_lisp(value, write, True)
562
lisp._eval(''.join(fragments))
564
class Lisp_Interface:
567
self.__dict__['_cache'] = {'nil': None}
568
self.__dict__['_protocol'] = Protocol()
570
def __call__(self, text):
571
return self._eval('(progn %s)' % text)
573
def _eval(self, text):
574
self._protocol.send('eval', text)
575
return self._protocol.loop()
577
def _expand(self, text):
578
self._protocol.send('expand', text)
579
return self._protocol.loop()
581
def __getattr__(self, name):
583
if old_style_exception:
584
raise AttributeError, name
585
raise AttributeError(name)
586
return self[name.replace('_', '-')]
588
def __setattr__(self, name, value):
590
if old_style_exception:
591
raise AttributeError, name
592
raise AttributeError(name)
593
self[name.replace('_', '-')] = value
595
def __getitem__(self, name):
597
return self._cache[name]
599
symbol = self._cache[name] = Symbol(name)
602
def __setitem__(self, name, value):
604
symbol = self._cache[name]
606
symbol = self._cache[name] = Symbol(name)
609
lisp = Lisp_Interface()
611
print_lisp_quoted_specials = {
612
'"': '\\"', '\\': '\\\\', '\b': '\\b', '\f': '\\f',
613
'\n': '\\n', '\r': '\\r', '\t': '\\t'}
615
def print_lisp(value, write, quoted):
618
elif isinstance(value, bool):
619
write(('nil', 't')[value])
620
elif isinstance(value, int):
622
elif isinstance(value, float):
624
elif isinstance(value, basestring):
626
if isinstance(value, unicode):
628
value = value.encode('ASCII')
629
except UnicodeEncodeError:
630
value = value.encode('UTF-8')
633
write('(decode-coding-string ')
635
for character in value:
636
special = print_lisp_quoted_specials.get(character)
637
if special is not None:
639
elif 32 <= ord(character) < 127:
642
write('\\%.3o' % ord(character))
646
elif isinstance(value, list):
651
elif len(value) == 2 and value[0] == lisp.quote:
653
print_lisp(value[1], write, False)
656
print_lisp(value[0], write, False)
657
for sub_value in value[1:]:
659
print_lisp(sub_value, write, False)
661
elif isinstance(value, tuple):
664
print_lisp(value[0], write, False)
665
for sub_value in value[1:]:
667
print_lisp(sub_value, write, False)
669
elif isinstance(value, Lisp):
671
elif isinstance(value, Symbol):
675
elif callable(value):
676
write('(pymacs-defun %d nil)' % allocate_python(value))
678
write('(pymacs-python %d)' % allocate_python(value))
680
if __name__ == '__main__':