hooks.py
209 lines
| 6.6 KiB
| text/x-python
|
PythonLexer
Thomas Kluyver
|
r9244 | """Hooks for IPython. | ||
Ville M. Vainio
|
r1032 | |||
In Python, it is possible to overwrite any method of any object if you really | ||||
Thomas Kluyver
|
r9244 | want to. But IPython exposes a few 'hooks', methods which are *designed* to | ||
Ville M. Vainio
|
r1032 | be overwritten by users for customization purposes. This module defines the | ||
default versions of all such hooks, which get used by IPython if not | ||||
overridden by the user. | ||||
Thomas Kluyver
|
r9244 | Hooks are simple functions, but they should be declared with ``self`` as their | ||
Ville M. Vainio
|
r1032 | first argument, because when activated they are registered into IPython as | ||
Thomas Kluyver
|
r9244 | instance methods. The self argument will be the IPython running instance | ||
Ville M. Vainio
|
r1032 | itself, so hooks have full access to the entire IPython object. | ||
Thomas Kluyver
|
r9244 | If you wish to define a new hook and activate it, you can make an :doc:`extension | ||
</config/extensions/index>` or a :ref:`startup script <startup_files>`. For | ||||
example, you could use a startup file like this:: | ||||
Ville M. Vainio
|
r1032 | |||
Thomas Kluyver
|
r9244 | import os | ||
Ville M. Vainio
|
r1032 | |||
Thomas Kluyver
|
r9244 | def calljed(self,filename, linenum): | ||
"My editor hook calls the jed editor directly." | ||||
print "Calling my own editor, jed ..." | ||||
if os.system('jed +%d %s' % (linenum,filename)) != 0: | ||||
raise TryNext() | ||||
Ville M. Vainio
|
r1032 | |||
Thomas Kluyver
|
r9244 | def load_ipython_extension(ip): | ||
ip.set_hook('editor', calljed) | ||||
Ville M. Vainio
|
r1032 | |||
Fernando Perez
|
r1853 | """ | ||
Ville M. Vainio
|
r1032 | |||
#***************************************************************************** | ||||
# Copyright (C) 2005 Fernando Perez. <fperez@colorado.edu> | ||||
# | ||||
# Distributed under the terms of the BSD License. The full license is in | ||||
# the file COPYING, distributed as part of this software. | ||||
#***************************************************************************** | ||||
Thomas Kluyver
|
r7515 | import os | ||
Takafumi Arakaki
|
r6634 | import subprocess | ||
Robert Kern
|
r1841 | import sys | ||
Brian Granger
|
r2498 | |||
Brian Granger
|
r2781 | from IPython.core.error import TryNext | ||
Brian Granger
|
r2498 | |||
Ville M. Vainio
|
r1032 | # List here all the default hooks. For now it's just the editor functions | ||
# but over time we'll move here all the public API for user-accessible things. | ||||
Brian Granger
|
r2226 | |||
Brian Granger
|
r2781 | __all__ = ['editor', 'fix_error_editor', 'synchronize_with_editor', | ||
Thomas Kluyver
|
r13894 | 'shutdown_hook', 'late_startup_hook', | ||
Thomas Kluyver
|
r5500 | 'show_in_pager','pre_prompt_hook', | ||
Fernando Perez
|
r3096 | 'pre_run_code_hook', 'clipboard_get'] | ||
Ville M. Vainio
|
r1032 | |||
Takafumi Arakaki
|
r6634 | def editor(self, filename, linenum=None, wait=True): | ||
Ville M. Vainio
|
r1032 | """Open the default editor at the given filename and linenumber. | ||
This is IPython's default editor hook, you can use it as an example to | ||||
write your own modified one. To set your own editor function as the | ||||
new editor hook, call ip.set_hook('editor',yourfunc).""" | ||||
# IPython configures a default editor at startup by reading $EDITOR from | ||||
# the environment, and falling back on vi (unix) or notepad (win32). | ||||
Brian Granger
|
r2202 | editor = self.editor | ||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1032 | # marker for at which line to open the file (for existing objects) | ||
if linenum is None or editor=='notepad': | ||||
linemark = '' | ||||
else: | ||||
linemark = '+%d' % int(linenum) | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1032 | # Enclose in quotes if necessary and legal | ||
if ' ' in editor and os.path.isfile(editor) and editor[0] != '"': | ||||
editor = '"%s"' % editor | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1032 | # Call the actual editor | ||
Takafumi Arakaki
|
r6634 | proc = subprocess.Popen('%s %s %s' % (editor, linemark, filename), | ||
shell=True) | ||||
if wait and proc.wait() != 0: | ||||
Brian Granger
|
r2205 | raise TryNext() | ||
Ville M. Vainio
|
r1032 | |||
import tempfile | ||||
def fix_error_editor(self,filename,linenum,column,msg): | ||||
Bernardo B. Marques
|
r4872 | """Open the editor at the given filename, linenumber, column and | ||
Ville M. Vainio
|
r1032 | show an error message. This is used for correcting syntax errors. | ||
The current implementation only has special support for the VIM editor, | ||||
and falls back on the 'editor' hook if VIM is not used. | ||||
Call ip.set_hook('fix_error_editor',youfunc) to use your own function, | ||||
""" | ||||
def vim_quickfix_file(): | ||||
t = tempfile.NamedTemporaryFile() | ||||
t.write('%s:%d:%d:%s\n' % (filename,linenum,column,msg)) | ||||
t.flush() | ||||
return t | ||||
Brian Granger
|
r2202 | if os.path.basename(self.editor) != 'vim': | ||
Ville M. Vainio
|
r1032 | self.hooks.editor(filename,linenum) | ||
return | ||||
t = vim_quickfix_file() | ||||
try: | ||||
Ville M. Vainio
|
r1744 | if os.system('vim --cmd "set errorformat=%f:%l:%c:%m" -q ' + t.name): | ||
Brian Granger
|
r2205 | raise TryNext() | ||
Ville M. Vainio
|
r1032 | finally: | ||
t.close() | ||||
Brian Granger
|
r2226 | |||
vds
|
r1241 | def synchronize_with_editor(self, filename, linenum, column): | ||
Fernando Perez
|
r2116 | pass | ||
Brian Granger
|
r2226 | |||
Ville M. Vainio
|
r1032 | |||
class CommandChainDispatcher: | ||||
""" Dispatch calls to a chain of commands until some func can handle it | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1032 | Usage: instantiate, execute "add" to add commands (with optional | ||
priority), execute normally via f() calling mechanism. | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1032 | """ | ||
def __init__(self,commands=None): | ||||
if commands is None: | ||||
self.chain = [] | ||||
else: | ||||
self.chain = commands | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1032 | def __call__(self,*args, **kw): | ||
Bernardo B. Marques
|
r4872 | """ Command chain is called just like normal func. | ||
Fernando Perez
|
r6951 | This will call all funcs in chain with the same args as were given to | ||
this function, and return the result of first func that didn't raise | ||||
TryNext""" | ||||
Bradley M. Froehle
|
r7334 | last_exc = TryNext() | ||
Ville M. Vainio
|
r1032 | for prio,cmd in self.chain: | ||
#print "prio",prio,"cmd",cmd #dbg | ||||
try: | ||||
Fernando Perez
|
r2370 | return cmd(*args, **kw) | ||
Bradley M. Froehle
|
r7334 | except TryNext as exc: | ||
last_exc = exc | ||||
Ville M. Vainio
|
r1032 | # if no function will accept it, raise TryNext up to the caller | ||
Bradley M. Froehle
|
r7334 | raise last_exc | ||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1032 | def __str__(self): | ||
return str(self.chain) | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1032 | def add(self, func, priority=0): | ||
""" Add a func to the cmd chain with given priority """ | ||||
Thomas Kluyver
|
r7515 | self.chain.append((priority, func)) | ||
Thomas Kluyver
|
r7519 | self.chain.sort(key=lambda x: x[0]) | ||
Ville M. Vainio
|
r1032 | |||
def __iter__(self): | ||||
""" Return all objects in chain. | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1032 | Handy if the objects are not callable. | ||
""" | ||||
return iter(self.chain) | ||||
Brian Granger
|
r2226 | |||
Ville M. Vainio
|
r1032 | def shutdown_hook(self): | ||
""" default shutdown hook | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1032 | Typically, shotdown hooks should raise TryNext so all shutdown ops are done | ||
""" | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1032 | #print "default shutdown hook ok" # dbg | ||
return | ||||
Brian Granger
|
r2226 | |||
Ville M. Vainio
|
r1032 | def late_startup_hook(self): | ||
Bernardo B. Marques
|
r4872 | """ Executed after ipython has been constructed and configured | ||
Ville M. Vainio
|
r1032 | """ | ||
#print "default startup hook ok" # dbg | ||||
Brian Granger
|
r2226 | |||
Ville M. Vainio
|
r1032 | def show_in_pager(self,s): | ||
""" Run a string through pager """ | ||||
# raising TryNext here will use the default paging functionality | ||||
Brian Granger
|
r2205 | raise TryNext | ||
Ville M. Vainio
|
r1032 | |||
Brian Granger
|
r2226 | |||
Ville M. Vainio
|
r1032 | def pre_prompt_hook(self): | ||
""" Run before displaying the next prompt | ||||
Bernardo B. Marques
|
r4872 | |||
Use this e.g. to display output from asynchronous operations (in order | ||||
to not mess up text entry) | ||||
Ville M. Vainio
|
r1032 | """ | ||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1032 | return None | ||
Brian Granger
|
r2226 | |||
Fernando Perez
|
r3096 | def pre_run_code_hook(self): | ||
vivainio2
|
r1036 | """ Executed before running the (prefiltered) code in IPython """ | ||
return None | ||||
Ville M. Vainio
|
r1032 | |||
Brian Granger
|
r2226 | |||
Robert Kern
|
r1841 | def clipboard_get(self): | ||
""" Get text from the clipboard. | ||||
""" | ||||
Brian Granger
|
r2128 | from IPython.lib.clipboard import ( | ||
Bernardo B. Marques
|
r4872 | osx_clipboard_get, tkinter_clipboard_get, | ||
Brian Granger
|
r2128 | win32_clipboard_get | ||
) | ||||
Robert Kern
|
r1841 | if sys.platform == 'win32': | ||
chain = [win32_clipboard_get, tkinter_clipboard_get] | ||||
elif sys.platform == 'darwin': | ||||
chain = [osx_clipboard_get, tkinter_clipboard_get] | ||||
else: | ||||
chain = [tkinter_clipboard_get] | ||||
dispatcher = CommandChainDispatcher() | ||||
for func in chain: | ||||
dispatcher.add(func) | ||||
text = dispatcher() | ||||
return text | ||||