PageRenderTime 50ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/Pymacs/pymacs.py

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