ipy_profile_sh.py
268 lines
| 9.0 KiB
| text/x-python
|
PythonLexer
ville
|
r988 | """Shell mode for IPython. | ||
Start ipython in shell mode by invoking "ipython -p sh" | ||||
Bernardo B. Marques
|
r4872 | (the old version, "ipython -p pysh" still works but this is the more "modern" | ||
ville
|
r988 | shell mode and is recommended for users who don't care about pysh-mode | ||
compatibility) | ||||
""" | ||||
Brian Granger
|
r2027 | from IPython.core import ipapi | ||
Brian Granger
|
r2205 | from IPython.core.error import TryNext | ||
Ville M. Vainio
|
r1742 | import os,re,textwrap | ||
ville
|
r988 | |||
# The import below effectively obsoletes your old-style ipythonrc[.ini], | ||||
# so consider yourself warned! | ||||
import ipy_defaults | ||||
Bernardo B. Marques
|
r4872 | def main(): | ||
ville
|
r988 | ip = ipapi.get() | ||
o = ip.options | ||||
# autocall to "full" mode (smart mode is default, I like full mode) | ||||
Bernardo B. Marques
|
r4872 | |||
ville
|
r988 | o.autocall = 2 | ||
Bernardo B. Marques
|
r4872 | |||
ville
|
r988 | # Jason Orendorff's path class is handy to have in user namespace | ||
# if you are doing shell-like stuff | ||||
try: | ||||
ip.ex("from IPython.external.path import path" ) | ||||
except ImportError: | ||||
pass | ||||
Bernardo B. Marques
|
r4872 | |||
ville
|
r988 | # beefed up %env is handy in shell mode | ||
import envpersist | ||||
Bernardo B. Marques
|
r4872 | |||
# To see where mycmd resides (in path/aliases), do %which mycmd | ||||
ville
|
r988 | import ipy_which | ||
Bernardo B. Marques
|
r4872 | |||
ville
|
r988 | # tab completers for hg, svn, ... | ||
import ipy_app_completers | ||||
Bernardo B. Marques
|
r4872 | |||
ville
|
r988 | # To make executables foo and bar in mybin usable without PATH change, do: | ||
# %rehashdir c:/mybin | ||||
# %store foo | ||||
# %store bar | ||||
import ipy_rehashdir | ||||
Ville M. Vainio
|
r1213 | |||
# does not work without subprocess module! | ||||
#import ipy_signals | ||||
Bernardo B. Marques
|
r4872 | |||
ville
|
r988 | ip.ex('import os') | ||
ip.ex("def up(): os.chdir('..')") | ||||
Bernardo B. Marques
|
r4872 | ip.user_ns['LA'] = LastArgFinder() | ||
# You can assign to _prompt_title variable | ||||
Ville M. Vainio
|
r1688 | # to provide some extra information for prompt | ||
# (e.g. the current mode, host/username...) | ||||
ip.user_ns['_prompt_title'] = '' | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1688 | # Nice prompt | ||
o.prompt_in1= r'\C_Green${_prompt_title}\C_LightBlue[\C_LightCyan\Y2\C_LightBlue]\C_Green|\#> ' | ||||
ville
|
r988 | o.prompt_in2= r'\C_Green|\C_LightGreen\D\C_Green> ' | ||
o.prompt_out= '<\#> ' | ||||
Bernardo B. Marques
|
r4872 | |||
Brian Granger
|
r2043 | from IPython.core import release | ||
ville
|
r988 | |||
import sys | ||||
# Non-chatty banner | ||||
Brian Granger
|
r2043 | o.banner = "IPython %s [on Py %s]\n" % (release.version,sys.version.split(None,1)[0]) | ||
Bernardo B. Marques
|
r4872 | |||
Brian Granger
|
r2205 | ip.default_option('cd','-q') | ||
ip.default_option('macro', '-r') | ||||
Bernardo B. Marques
|
r4872 | # If you only rarely want to execute the things you %edit... | ||
Brian Granger
|
r2205 | #ip.default_option('edit','-x') | ||
Bernardo B. Marques
|
r4872 | |||
ville
|
r988 | |||
o.prompts_pad_left="1" | ||||
# Remove all blank lines in between prompts, like a normal shell. | ||||
o.separate_in="0" | ||||
o.separate_out="0" | ||||
o.separate_out2="0" | ||||
Bernardo B. Marques
|
r4872 | |||
ville
|
r988 | # now alias all syscommands | ||
Bernardo B. Marques
|
r4872 | |||
ville
|
r988 | db = ip.db | ||
Bernardo B. Marques
|
r4872 | |||
ville
|
r988 | syscmds = db.get("syscmdlist",[] ) | ||
if not syscmds: | ||||
print textwrap.dedent(""" | ||||
System command list not initialized, probably the first run... | ||||
running %rehashx to refresh the command list. Run %rehashx | ||||
again to refresh command list (after installing new software etc.) | ||||
""") | ||||
ip.magic('rehashx') | ||||
syscmds = db.get("syscmdlist") | ||||
Bernardo B. Marques
|
r4872 | |||
ville
|
r988 | # lowcase aliases on win32 only | ||
if os.name == 'posix': | ||||
mapper = lambda s:s | ||||
else: | ||||
def mapper(s): return s.lower() | ||||
Bernardo B. Marques
|
r4872 | |||
ville
|
r988 | for cmd in syscmds: | ||
# print "sys",cmd #dbg | ||||
noext, ext = os.path.splitext(cmd) | ||||
Ville M. Vainio
|
r1687 | if ext.lower() == '.exe': | ||
cmd = noext | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1687 | key = mapper(cmd) | ||
Brian Granger
|
r2245 | if key not in ip.alias_manager.alias_table: | ||
Ville M. Vainio
|
r1736 | # Dots will be removed from alias names, since ipython | ||
# assumes names with dots to be python code | ||||
Bernardo B. Marques
|
r4872 | |||
Brian Granger
|
r2205 | ip.define_alias(key.replace('.',''), cmd) | ||
ville
|
r988 | |||
# win32 is crippled w/o cygwin, try to help it a little bit | ||||
if sys.platform == 'win32': | ||||
Bernardo B. Marques
|
r4872 | if 'cygwin' in os.environ['PATH'].lower(): | ||
ville
|
r988 | # use the colors of cygwin ls (recommended) | ||
Brian Granger
|
r2205 | ip.define_alias('d', 'ls -F --color=auto') | ||
ville
|
r988 | else: | ||
# get icp, imv, imkdir, igrep, irm,... | ||||
ip.load('ipy_fsops') | ||||
Bernardo B. Marques
|
r4872 | |||
ville
|
r988 | # and the next best thing to real 'ls -F' | ||
Brian Granger
|
r2205 | ip.define_alias('d','dir /w /og /on') | ||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1742 | ip.set_hook('input_prefilter', slash_prefilter_f) | ||
ville
|
r988 | extend_shell_behavior(ip) | ||
Ville M. Vainio
|
r1043 | class LastArgFinder: | ||
Ville M. Vainio
|
r1045 | """ Allow $LA to work as "last argument of previous command", like $! in bash | ||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1045 | To call this in normal IPython code, do LA() | ||
Ville M. Vainio
|
r1043 | """ | ||
Ville M. Vainio
|
r1047 | def __call__(self, hist_idx = None): | ||
ip = ipapi.get() | ||||
if hist_idx is None: | ||||
return str(self) | ||||
Brian Granger
|
r2205 | return ip.input_hist_raw[hist_idx].strip().split()[-1] | ||
Ville M. Vainio
|
r1043 | def __str__(self): | ||
Bernardo B. Marques
|
r4872 | ip = ipapi.get() | ||
Brian Granger
|
r2205 | for cmd in reversed(ip.input_hist_raw): | ||
Ville M. Vainio
|
r1043 | parts = cmd.strip().split() | ||
Ville M. Vainio
|
r1046 | if len(parts) < 2 or parts[-1] in ['$LA', 'LA()']: | ||
Ville M. Vainio
|
r1043 | continue | ||
return parts[-1] | ||||
return "" | ||||
Ville M. Vainio
|
r1742 | def slash_prefilter_f(self,line): | ||
""" ./foo, ~/foo and /bin/foo now run foo as system command | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1742 | Removes the need for doing !./foo, !~/foo or !/bin/foo | ||
Ville M. Vainio
|
r1285 | """ | ||
Brian Granger
|
r2023 | from IPython.utils import genutils | ||
Ville M. Vainio
|
r1742 | if re.match('(?:[.~]|/[a-zA-Z_0-9]+)/', line): | ||
Brian Granger
|
r2245 | return "get_ipython().system(" + genutils.make_quoted_expr(line)+")" | ||
Brian Granger
|
r2205 | raise TryNext | ||
Ville M. Vainio
|
r1043 | |||
ville
|
r988 | # XXX You do not need to understand the next function! | ||
# This should probably be moved out of profile | ||||
def extend_shell_behavior(ip): | ||||
# Instead of making signature a global variable tie it to IPSHELL. | ||||
# In future if it is required to distinguish between different | ||||
# shells we can assign a signature per shell basis | ||||
Brian Granger
|
r2205 | ip.__sig__ = 0xa005 | ||
ville
|
r988 | # mark the IPSHELL with this signature | ||
Brian Granger
|
r2205 | ip.user_ns['__builtins__'].__dict__['__sig__'] = ip.__sig__ | ||
ville
|
r988 | |||
Brian Granger
|
r2031 | from IPython.external.Itpl import ItplNS | ||
from IPython.utils.genutils import shell | ||||
ville
|
r988 | # utility to expand user variables via Itpl | ||
# xxx do something sensible with depth? | ||||
Brian Granger
|
r2205 | ip.var_expand = lambda cmd, lvars=None, depth=2: \ | ||
str(ItplNS(cmd, ip.user_ns, get_locals())) | ||||
ville
|
r988 | |||
def get_locals(): | ||||
""" Substituting a variable through Itpl deep inside the IPSHELL stack | ||||
requires the knowledge of all the variables in scope upto the last | ||||
IPSHELL frame. This routine simply merges all the local variables | ||||
on the IPSHELL stack without worrying about their scope rules | ||||
""" | ||||
import sys | ||||
# note lambda expression constitues a function call | ||||
# hence fno should be incremented by one | ||||
getsig = lambda fno: sys._getframe(fno+1).f_globals \ | ||||
['__builtins__'].__dict__['__sig__'] | ||||
getlvars = lambda fno: sys._getframe(fno+1).f_locals | ||||
# trackback until we enter the IPSHELL | ||||
frame_no = 1 | ||||
Brian Granger
|
r2205 | sig = ip.__sig__ | ||
ville
|
r988 | fsig = ~sig | ||
while fsig != sig : | ||||
try: | ||||
fsig = getsig(frame_no) | ||||
except (AttributeError, KeyError): | ||||
frame_no += 1 | ||||
except ValueError: | ||||
# stack is depleted | ||||
# call did not originate from IPSHELL | ||||
return {} | ||||
first_frame = frame_no | ||||
# walk further back until we exit from IPSHELL or deplete stack | ||||
try: | ||||
while(sig == getsig(frame_no+1)): | ||||
frame_no += 1 | ||||
except (AttributeError, KeyError, ValueError): | ||||
pass | ||||
# merge the locals from top down hence overriding | ||||
# any re-definitions of variables, functions etc. | ||||
lvars = {} | ||||
for fno in range(frame_no, first_frame-1, -1): | ||||
lvars.update(getlvars(fno)) | ||||
#print '\n'*5, first_frame, frame_no, '\n', lvars, '\n'*5 #dbg | ||||
return lvars | ||||
def _runlines(lines): | ||||
"""Run a string of one or more lines of source. | ||||
This method is capable of running a string containing multiple source | ||||
lines, as if they had been entered at the IPython prompt. Since it | ||||
exposes IPython's processing machinery, the given strings can contain | ||||
magic calls (%magic), special shell access (!cmd), etc.""" | ||||
# We must start with a clean buffer, in case this is run from an | ||||
# interactive IPython session (via a magic, for example). | ||||
Brian Granger
|
r2205 | ip.resetbuffer() | ||
ville
|
r988 | lines = lines.split('\n') | ||
more = 0 | ||||
command = '' | ||||
for line in lines: | ||||
# skip blank lines so we don't mess up the prompt counter, but do | ||||
# NOT skip even a blank line if we are in a code block (more is | ||||
# true) | ||||
# if command is not empty trim the line | ||||
if command != '' : | ||||
line = line.strip() | ||||
# add the broken line to the command | ||||
if line and line[-1] == '\\' : | ||||
command += line[0:-1] + ' ' | ||||
more = True | ||||
continue | ||||
else : | ||||
# add the last (current) line to the command | ||||
command += line | ||||
if command or more: | ||||
# push to raw history, so hist line numbers stay in sync | ||||
Brian Granger
|
r2205 | ip.input_hist_raw.append("# " + command + "\n") | ||
Bernardo B. Marques
|
r4872 | |||
Brian Granger
|
r2205 | more = ip.push_line(ip.prefilter(command,more)) | ||
ville
|
r988 | command = '' | ||
# IPython's runsource returns None if there was an error | ||||
# compiling the code. This allows us to stop processing right | ||||
# away, so the user gets the error message at the right place. | ||||
if more is None: | ||||
break | ||||
# final newline in case the input didn't have it, so that the code | ||||
# actually does get executed | ||||
if more: | ||||
Brian Granger
|
r2205 | ip.push_line('\n') | ||
ville
|
r988 | |||
Brian Granger
|
r2205 | ip.runlines = _runlines | ||
ville
|
r988 | |||
main() | ||||