PageRenderTime 49ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/Pymacs/pymacs.py

https://bitbucket.org/vvangelovski/vasil-emacs
Python | 715 lines | 690 code | 2 blank | 23 comment | 0 complexity | b0af93bb9a5986c181fcd1ed2f026665 MD5 | raw file
Possible License(s): GPL-3.0, GPL-2.0
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # Copyright Š 2001, 2002, 2003 Progiciels Bourbeau-Pinard inc.
  4. # Fran?§ois Pinard <pinard@iro.umontreal.ca>, 2001.
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2, or (at your option)
  8. # any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software Foundation,
  17. # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
  18. """\
  19. Interface between Emacs Lisp and Python - Python part.
  20. Emacs may launch this module as a stand-alone program, in which case it
  21. acts as a server of Python facilities for that Emacs session, reading
  22. requests from standard input and writing replies on standard output.
  23. When used in this way, the program is called "the Pymacs helper".
  24. This module may also be usefully imported by those other Python modules.
  25. See the Pymacs documentation (in `README') for more information.
  26. """
  27. __metaclass__ = type
  28. import os, sys
  29. def fixup_icanon():
  30. # otherwise sys.stdin.read hangs for large inputs in emacs 24
  31. # see comment in emacs source code sysdep.c
  32. import termios
  33. a = termios.tcgetattr(1)
  34. a[3] &= ~termios.ICANON
  35. termios.tcsetattr(1, termios.TCSANOW, a)
  36. try:
  37. import signal
  38. except ImportError:
  39. # Jython does not have signal.
  40. signal = None
  41. old_style_exception = not isinstance(Exception, type)
  42. ## Python services for Emacs applications.
  43. class Main:
  44. debug_file = None
  45. signal_file = None
  46. def main(self, *arguments):
  47. """\
  48. Execute Python services for Emacs, and Emacs services for Python.
  49. This program is meant to be called from Emacs, using `pymacs.el'.
  50. Debugging options:
  51. -d FILE Debug the protocol to FILE.
  52. -s FILE Trace received signals to FILE.
  53. Arguments are added to the search path for Python modules.
  54. """
  55. # Decode options.
  56. arguments = (os.environ.get('PYMACS_OPTIONS', '').split()
  57. + list(arguments))
  58. import getopt
  59. options, arguments = getopt.getopt(arguments, 'fd:s:')
  60. for option, value in options:
  61. if option == '-d':
  62. self.debug_file = value
  63. elif option == '-s':
  64. self.signal_file = value
  65. elif option == '-f':
  66. try:
  67. fixup_icanon()
  68. except:
  69. pass
  70. arguments.reverse()
  71. for argument in arguments:
  72. if os.path.isdir(argument):
  73. sys.path.insert(0, argument)
  74. # Inhibit signals.
  75. if signal is not None:
  76. self.original_handler = signal.signal(
  77. signal.SIGINT, self.interrupt_handler)
  78. for counter in range(1, signal.NSIG):
  79. if counter == signal.SIGINT:
  80. self.original_handler = signal.signal(
  81. counter, self.interrupt_handler)
  82. # The following few lines of code are reported to create IO
  83. # problems within the Pymacs helper itself, so I merely comment
  84. # them for now, until we know better.
  85. #else:
  86. # try:
  87. # signal.signal(counter, self.generic_handler)
  88. # except RuntimeError:
  89. # pass
  90. self.inhibit_quit = True
  91. # Start protocol and services.
  92. from Pymacs import __version__
  93. lisp._protocol.send('version', '"%s"' % __version__)
  94. lisp._protocol.loop()
  95. def generic_handler(self, number, frame):
  96. if self.signal_file:
  97. handle = file(self.signal_file, 'a')
  98. handle.write('%d\n' % number)
  99. handle.close()
  100. def interrupt_handler(self, number, frame):
  101. if self.signal_file:
  102. star = (' *', '')[self.inhibit_quit]
  103. handle = file(self.signal_file, 'a')
  104. handle.write('%d%s\n' % (number, star))
  105. handle.close()
  106. if not self.inhibit_quit:
  107. self.original_handler(number, frame)
  108. run = Main()
  109. main = run.main
  110. if old_style_exception:
  111. ProtocolError = 'ProtocolError'
  112. ZombieError = 'ZombieError'
  113. else:
  114. class error(Exception): pass
  115. class ProtocolError(error): pass
  116. class ZombieError(error): pass
  117. class Protocol:
  118. # All exec's and eval's triggered from the Emacs side are all executed
  119. # within the "loop" method below, so all user context is kept as
  120. # local variables within this single routine. Different instances
  121. # of this Protocol class would yield independant evaluation contexts.
  122. # But in the usual case, there is only one such instance kept within a
  123. # Lisp_Interface instance, and the "lisp" global variable within this
  124. # module holds such a Lisp_Interface instance.
  125. def __init__(self):
  126. self.freed = []
  127. def loop(self):
  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.
  132. # The server loop may also be executed, as a recursive invocation,
  133. # in the context of Emacs serving a Python request. In which
  134. # case, we might also receive a notification from Emacs telling
  135. # that the reply has been transmitted, or that an error occurred.
  136. # A reply notification from Emacs interrupts the loop: the result
  137. # of this function is then the value returned from Emacs.
  138. done = False
  139. while not done:
  140. try:
  141. action, text = self.receive()
  142. if action == 'eval':
  143. action = 'return'
  144. try:
  145. run.inhibit_quit = False
  146. value = eval(text)
  147. finally:
  148. run.inhibit_quit = True
  149. elif action == 'exec':
  150. action = 'return'
  151. value = None
  152. try:
  153. run.inhibit_quit = False
  154. exec text
  155. finally:
  156. run.inhibit_quit = True
  157. elif action == 'return':
  158. done = True
  159. try:
  160. run.inhibit_quit = False
  161. value = eval(text)
  162. finally:
  163. run.inhibit_quit = True
  164. elif action == 'raise':
  165. action = 'raise'
  166. value = 'Emacs: ' + text
  167. else:
  168. if old_style_exception:
  169. raise ProtocolError, "Unknown action %r" % action
  170. raise ProtocolError("Unknown action %r" % action)
  171. except KeyboardInterrupt:
  172. if done:
  173. raise
  174. action = 'raise'
  175. value = '*Interrupted*'
  176. except ProtocolError, exception:
  177. sys.exit("Protocol error: %s\n" % exception)
  178. except:
  179. import StringIO, traceback
  180. buffer = StringIO.StringIO()
  181. traceback.print_exc(file=buffer)
  182. action = 'raise'
  183. value = buffer.getvalue()
  184. if not done:
  185. fragments = []
  186. print_lisp(value, fragments.append, True)
  187. self.send(action, ''.join(fragments))
  188. return value
  189. def receive(self):
  190. # Receive a Python expression from Emacs, return (ACTION, TEXT).
  191. prefix = sys.stdin.read(3)
  192. if not prefix or prefix[0] != '>':
  193. if old_style_exception:
  194. raise ProtocolError, "`>' expected."
  195. raise ProtocolError("`>' expected.")
  196. while prefix[-1] != '\t':
  197. character = sys.stdin.read(1)
  198. if not character:
  199. if old_style_exception:
  200. raise ProtocolError, "Empty stdin read."
  201. raise ProtocolError("Empty stdin read.")
  202. prefix += character
  203. text = sys.stdin.read(int(prefix[1:-1]))
  204. if run.debug_file is not None:
  205. handle = file(run.debug_file, 'a')
  206. handle.write(prefix + text)
  207. handle.close()
  208. return text.split(None, 1)
  209. def send(self, action, text):
  210. # Send ACTION and its TEXT argument to Emacs.
  211. if self.freed:
  212. # All delayed Lisp cleanup is piggied back on the transmission.
  213. text = ('(free (%s) %s %s)\n'
  214. % (' '.join(map(str, self.freed)), action, text))
  215. self.freed = []
  216. else:
  217. text = '(%s %s)\n' % (action, text)
  218. prefix = '<%d\t' % len(text)
  219. if run.debug_file is not None:
  220. handle = file(run.debug_file, 'a')
  221. handle.write(prefix + text)
  222. handle.close()
  223. sys.stdout.write(prefix + text)
  224. sys.stdout.flush()
  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
  228. # in Emacs for accessing the Python module facilities. Module, given
  229. # through FILE_WITHOUT_EXTENSION, may be a full path, yet without the
  230. # `.py' or `.pyc' suffix, in which case the directory is temporarily
  231. # added to the Python search path for the sole duration of that import.
  232. # All defined symbols on the Lisp side have have PREFIX prepended,
  233. # and have Python underlines in Python turned into dashes. If PREFIX
  234. # is None, it then defaults to the base name of MODULE with underlines
  235. # turned to dashes, followed by a dash.
  236. directory, module_name = os.path.split(file_without_extension)
  237. module_components = module_name.split('.')
  238. if prefix is None:
  239. prefix = module_components[-1].replace('_', '-') + '-'
  240. try:
  241. object = sys.modules.get(module_name)
  242. if object:
  243. reload(object)
  244. else:
  245. try:
  246. if directory:
  247. sys.path.insert(0, directory)
  248. object = __import__(module_name)
  249. finally:
  250. if directory:
  251. del sys.path[0]
  252. # Whenever MODULE_NAME is of the form [PACKAGE.]...MODULE,
  253. # __import__ returns the outer PACKAGE, not the module.
  254. for component in module_components[1:]:
  255. object = getattr(object, component)
  256. except ImportError:
  257. return None
  258. load_hook = object.__dict__.get('pymacs_load_hook')
  259. if load_hook:
  260. load_hook()
  261. interactions = object.__dict__.get('interactions', {})
  262. if not isinstance(interactions, dict):
  263. interactions = {}
  264. arguments = []
  265. for name, value in object.__dict__.items():
  266. if callable(value) and value is not lisp:
  267. arguments.append(allocate_python(value))
  268. arguments.append(lisp[prefix + name.replace('_', '-')])
  269. try:
  270. interaction = value.interaction
  271. except AttributeError:
  272. interaction = interactions.get(value)
  273. if callable(interaction):
  274. arguments.append(allocate_python(interaction))
  275. else:
  276. arguments.append(interaction)
  277. if arguments:
  278. return [lisp.progn,
  279. [lisp.pymacs_defuns, [lisp.quote, arguments]],
  280. object]
  281. return [lisp.quote, object]
  282. def doc_string(object):
  283. if hasattr(object, '__doc__'):
  284. return object.__doc__
  285. ## Garbage collection matters.
  286. # Many Python types do not have direct Lisp equivalents, and may not be
  287. # directly returned to Lisp for this reason. They are rather allocated in
  288. # a list of handles, below, and a handle index is used for communication
  289. # instead of the Python value. Whenever such a handle is freed from the
  290. # Lisp side, its index is added of a freed list for later reuse.
  291. python = []
  292. freed_list = []
  293. def allocate_python(value):
  294. assert not isinstance(value, str), (type(value), repr(value))
  295. # Allocate some handle to hold VALUE, return its index.
  296. if freed_list:
  297. index = freed_list[-1]
  298. del freed_list[-1]
  299. python[index] = value
  300. else:
  301. index = len(python)
  302. python.append(value)
  303. return index
  304. def free_python(*indices):
  305. # Return many handles to the pool.
  306. for index in indices:
  307. python[index] = None
  308. freed_list.append(index)
  309. def zombie_python(*indices):
  310. # Ensure that some handles are _not_ in the pool.
  311. for index in indices:
  312. while index >= len(python):
  313. freed_list.append(len(python))
  314. python.append(None)
  315. python[index] = zombie
  316. freed_list.remove(index)
  317. # Merely to make `*Pymacs*' a bit more readable.
  318. freed_list.sort()
  319. def zombie(*arguments):
  320. # This catch-all function is set as the value for any function which
  321. # disappeared with a previous Pymacs helper process, so calling
  322. # such a function from Emacs will trigger a decipherable diagnostic.
  323. diagnostic = "Object vanished when the Pymacs helper was killed"
  324. if lisp.pymacs_dreadful_zombies.value():
  325. if old_style_exception:
  326. raise ZombieError, diagnostic
  327. raise ZombieError(diagnostic)
  328. lisp.message(diagnostic)
  329. ## Emacs services for Python applications.
  330. class Let:
  331. def __init__(self, **keywords):
  332. # The stack holds (METHOD, DATA) pairs, where METHOD is the expected
  333. # unbound pop_* method, and DATA holds information to be restored.
  334. # METHOD may not be bound to the instance, as this would induce
  335. # reference cycles, and then, __del__ would not be called timely.
  336. self.stack = []
  337. if keywords:
  338. self.push(**keywords)
  339. def __del__(self):
  340. self.pops()
  341. def __nonzero__(self):
  342. # So stylistic `if let:' executes faster.
  343. return True
  344. def pops(self):
  345. while self.stack:
  346. self.stack[-1][0](self)
  347. def push(self, **keywords):
  348. data = []
  349. for name, value in keywords.items():
  350. data.append((name, getattr(lisp, name).value()))
  351. setattr(lisp, name, value)
  352. self.stack.append((Let.pop, data))
  353. return self
  354. def pop(self):
  355. method, data = self.stack.pop()
  356. assert method == Let.pop, (method, data)
  357. for name, value in data:
  358. setattr(lisp, name, value)
  359. def push_excursion(self):
  360. self.stack.append((Let.pop_excursion, (lisp.current_buffer(),
  361. lisp.point_marker(),
  362. lisp.mark_marker())))
  363. return self
  364. def pop_excursion(self):
  365. method, data = self.stack.pop()
  366. assert method == Let.pop_excursion, (method, data)
  367. buffer, point_marker, mark_marker = data
  368. lisp.set_buffer(buffer)
  369. lisp.goto_char(point_marker)
  370. lisp.set_mark(mark_marker)
  371. lisp.set_marker(point_marker, None)
  372. lisp.set_marker(mark_marker, None)
  373. def push_match_data(self):
  374. self.stack.append((Let.pop_match_data, lisp.match_data()))
  375. return self
  376. def pop_match_data(self):
  377. method, data = self.stack.pop()
  378. assert method == Let.pop_match_data, (method, data)
  379. lisp.set_match_data(data)
  380. def push_restriction(self):
  381. self.stack.append((Let.pop_restriction, (lisp.point_min_marker(),
  382. lisp.point_max_marker())))
  383. return self
  384. def pop_restriction(self):
  385. method, data = self.stack.pop()
  386. assert method == Let.pop_restriction, (method, data)
  387. point_min_marker, point_max_marker = data
  388. lisp.narrow_to_region(point_min_marker, point_max_marker)
  389. lisp.set_marker(point_min_marker, None)
  390. lisp.set_marker(point_max_marker, None)
  391. def push_selected_window(self):
  392. self.stack.append((Let.pop_selected_window, lisp.selected_window()))
  393. return self
  394. def pop_selected_window(self):
  395. method, data = self.stack.pop()
  396. assert method == Let.pop_selected_window, (method, data)
  397. lisp.select_window(data)
  398. def push_window_excursion(self):
  399. self.stack.append((Let.pop_window_excursion,
  400. lisp.current_window_configuration()))
  401. return self
  402. def pop_window_excursion(self):
  403. method, data = self.stack.pop()
  404. assert method == Let.pop_window_excursion, (method, data)
  405. lisp.set_window_configuration(data)
  406. class Symbol:
  407. def __init__(self, text):
  408. self.text = text
  409. def __repr__(self):
  410. return 'lisp[%s]' % repr(self.text)
  411. def __str__(self):
  412. return '\'' + self.text
  413. def value(self):
  414. return lisp._eval(self.text)
  415. def copy(self):
  416. return lisp._expand(self.text)
  417. def set(self, value):
  418. if value is None:
  419. lisp._eval('(setq %s nil)' % self.text)
  420. else:
  421. fragments = []
  422. write = fragments.append
  423. write('(progn (setq %s ' % self.text)
  424. print_lisp(value, write, True)
  425. write(') nil)')
  426. lisp._eval(''.join(fragments))
  427. def __call__(self, *arguments):
  428. fragments = []
  429. write = fragments.append
  430. write('(%s' % self.text)
  431. for argument in arguments:
  432. write(' ')
  433. print_lisp(argument, write, True)
  434. write(')')
  435. return lisp._eval(''.join(fragments))
  436. class Lisp:
  437. def __init__(self, index):
  438. self.index = index
  439. def __del__(self):
  440. lisp._protocol.freed.append(self.index)
  441. def __repr__(self):
  442. return ('lisp(%s)' % repr(lisp('(prin1-to-string %s)' % self)))
  443. def __str__(self):
  444. return '(aref pymacs-lisp %d)' % self.index
  445. def value(self):
  446. return self
  447. def copy(self):
  448. return lisp._expand(str(self))
  449. class Buffer(Lisp):
  450. pass
  451. #def write(text):
  452. # # So you could do things like
  453. # # print >>lisp.current_buffer(), "Hello World"
  454. # lisp.insert(text, self)
  455. #def point(self):
  456. # return lisp.point(self)
  457. class List(Lisp):
  458. def __call__(self, *arguments):
  459. fragments = []
  460. write = fragments.append
  461. write('(%s' % self)
  462. for argument in arguments:
  463. write(' ')
  464. print_lisp(argument, write, True)
  465. write(')')
  466. return lisp._eval(''.join(fragments))
  467. def __len__(self):
  468. return lisp._eval('(length %s)' % self)
  469. def __getitem__(self, key):
  470. value = lisp._eval('(nth %d %s)' % (key, self))
  471. if value is None and key >= len(self):
  472. if old_style_exception:
  473. raise IndexError, key
  474. raise IndexError(key)
  475. return value
  476. def __setitem__(self, key, value):
  477. fragments = []
  478. write = fragments.append
  479. write('(setcar (nthcdr %d %s) ' % (key, self))
  480. print_lisp(value, write, True)
  481. write(')')
  482. lisp._eval(''.join(fragments))
  483. class Table(Lisp):
  484. def __getitem__(self, key):
  485. fragments = []
  486. write = fragments.append
  487. write('(gethash ')
  488. print_lisp(key, write, True)
  489. write(' %s)' % self)
  490. return lisp._eval(''.join(fragments))
  491. def __setitem__(self, key, value):
  492. fragments = []
  493. write = fragments.append
  494. write('(puthash ')
  495. print_lisp(key, write, True)
  496. write(' ')
  497. print_lisp(value, write, True)
  498. write(' %s)' % self)
  499. lisp._eval(''.join(fragments))
  500. class Vector(Lisp):
  501. def __len__(self):
  502. return lisp._eval('(length %s)' % self)
  503. def __getitem__(self, key):
  504. return lisp._eval('(aref %s %d)' % (self, key))
  505. def __setitem__(self, key, value):
  506. fragments = []
  507. write = fragments.append
  508. write('(aset %s %d ' % (self, key))
  509. print_lisp(value, write, True)
  510. write(')')
  511. lisp._eval(''.join(fragments))
  512. class Lisp_Interface:
  513. def __init__(self):
  514. self.__dict__['_cache'] = {'nil': None}
  515. self.__dict__['_protocol'] = Protocol()
  516. def __call__(self, text):
  517. return self._eval('(progn %s)' % text)
  518. def _eval(self, text):
  519. self._protocol.send('eval', text)
  520. return self._protocol.loop()
  521. def _expand(self, text):
  522. self._protocol.send('expand', text)
  523. return self._protocol.loop()
  524. def __getattr__(self, name):
  525. if name[0] == '_':
  526. if old_style_exception:
  527. raise AttributeError, name
  528. raise AttributeError(name)
  529. return self[name.replace('_', '-')]
  530. def __setattr__(self, name, value):
  531. if name[0] == '_':
  532. if old_style_exception:
  533. raise AttributeError, name
  534. raise AttributeError(name)
  535. self[name.replace('_', '-')] = value
  536. def __getitem__(self, name):
  537. try:
  538. return self._cache[name]
  539. except KeyError:
  540. symbol = self._cache[name] = Symbol(name)
  541. return symbol
  542. def __setitem__(self, name, value):
  543. try:
  544. symbol = self._cache[name]
  545. except KeyError:
  546. symbol = self._cache[name] = Symbol(name)
  547. symbol.set(value)
  548. lisp = Lisp_Interface()
  549. print_lisp_quoted_specials = {
  550. '"': '\\"', '\\': '\\\\', '\b': '\\b', '\f': '\\f',
  551. '\n': '\\n', '\r': '\\r', '\t': '\\t'}
  552. def print_lisp(value, write, quoted):
  553. if value is None:
  554. write('nil')
  555. elif isinstance(bool, type) and isinstance(value, bool):
  556. write(('nil', 't')[value])
  557. elif isinstance(value, int):
  558. write(repr(value))
  559. elif isinstance(value, float):
  560. write(repr(value))
  561. elif isinstance(value, basestring):
  562. multibyte = False
  563. if isinstance(value, unicode):
  564. try:
  565. value = value.encode('ASCII')
  566. except UnicodeError:
  567. value = value.encode('UTF-8')
  568. multibyte = True
  569. if multibyte:
  570. write('(decode-coding-string ')
  571. write('"')
  572. for character in value:
  573. special = print_lisp_quoted_specials.get(character)
  574. if special is not None:
  575. write(special)
  576. elif 32 <= ord(character) < 127:
  577. write(character)
  578. else:
  579. write('\\%.3o' % ord(character))
  580. write('"')
  581. if multibyte:
  582. write(' \'utf-8)')
  583. elif isinstance(value, list):
  584. if quoted:
  585. write("'")
  586. if len(value) == 0:
  587. write('nil')
  588. elif len(value) == 2 and value[0] == lisp.quote:
  589. write("'")
  590. print_lisp(value[1], write, False)
  591. else:
  592. write('(')
  593. print_lisp(value[0], write, False)
  594. for sub_value in value[1:]:
  595. write(' ')
  596. print_lisp(sub_value, write, False)
  597. write(')')
  598. elif isinstance(value, tuple):
  599. write('[')
  600. if len(value) > 0:
  601. print_lisp(value[0], write, False)
  602. for sub_value in value[1:]:
  603. write(' ')
  604. print_lisp(sub_value, write, False)
  605. write(']')
  606. elif isinstance(value, Lisp):
  607. write(str(value))
  608. elif isinstance(value, Symbol):
  609. if quoted:
  610. write("'")
  611. write(value.text)
  612. elif callable(value):
  613. write('(pymacs-defun %d nil)' % allocate_python(value))
  614. else:
  615. write('(pymacs-python %d)' % allocate_python(value))
  616. if __name__ == '__main__':
  617. main(*sys.argv[1:])