ipy_profile_sh.py
187 lines
| 6.5 KiB
| text/x-python
|
PythonLexer
fperez
|
r284 | """Shell mode for IPython. | ||
vivainio
|
r187 | |||
Start ipython in shell mode by invoking "ipython -p sh" | ||||
(the old version, "ipython -p pysh" still works but this is the more "modern" | ||||
shell mode and is recommended for users who don't care about pysh-mode | ||||
compatibility) | ||||
""" | ||||
from IPython import ipapi | ||||
import os,textwrap | ||||
vivainio
|
r195 | # The import below effectively obsoletes your old-style ipythonrc[.ini], | ||
# so consider yourself warned! | ||||
fperez
|
r284 | import ipy_defaults | ||
vivainio
|
r195 | |||
vivainio
|
r543 | def main(): | ||
vivainio
|
r187 | ip = ipapi.get() | ||
fperez
|
r284 | o = ip.options | ||
vivainio
|
r187 | # autocall to "full" mode (smart mode is default, I like full mode) | ||
o.autocall = 2 | ||||
# Jason Orendorff's path class is handy to have in user namespace | ||||
# if you are doing shell-like stuff | ||||
try: | ||||
ip.ex("from path import path" ) | ||||
except ImportError: | ||||
pass | ||||
vivainio
|
r195 | |||
vivainio
|
r543 | # beefed up %env is handy in shell mode | ||
import envpersist | ||||
vivainio
|
r195 | ip.ex('import os') | ||
ip.ex("def up(): os.chdir('..')") | ||||
vivainio
|
r187 | |||
# Get pysh-like prompt for all profiles. | ||||
o.prompt_in1= '\C_LightBlue[\C_LightCyan\Y1\C_LightBlue]\C_Green|\#> ' | ||||
o.prompt_in2= '\C_Green|\C_LightGreen\D\C_Green> ' | ||||
o.prompt_out= '<\#> ' | ||||
from IPython import Release | ||||
import sys | ||||
# I like my banner minimal. | ||||
o.banner = "Py %s IPy %s\n" % (sys.version.split('\n')[0],Release.version) | ||||
# make 'd' an alias for ls -F | ||||
ip.magic('alias d ls -F --color=auto') | ||||
# Make available all system commands through "rehashing" immediately. | ||||
# You can comment these lines out to speed up startup on very slow | ||||
# machines, and to conserve a bit of memory. Note that pysh profile does this | ||||
# automatically | ||||
ip.IP.default_option('cd','-q') | ||||
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" | ||||
# now alias all syscommands | ||||
fperez
|
r284 | db = ip.db | ||
vivainio
|
r187 | |||
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") | ||||
for cmd in syscmds: | ||||
#print "al",cmd | ||||
noext, ext = os.path.splitext(cmd) | ||||
ip.IP.alias_table[noext] = (0,cmd) | ||||
vivainio
|
r328 | extend_shell_behavior(ip) | ||
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 | ||||
ip.IP.__sig__ = 0xa005 | ||||
# mark the IPSHELL with this signature | ||||
ip.IP.user_ns['__builtins__'].__dict__['__sig__'] = ip.IP.__sig__ | ||||
from IPython.Itpl import ItplNS | ||||
from IPython.genutils import shell | ||||
# utility to expand user variables via Itpl | ||||
vivainio
|
r425 | # xxx do something sensible with depth? | ||
ip.IP.var_expand = lambda cmd, lvars=None, depth=2: \ | ||||
vivainio
|
r328 | str(ItplNS(cmd.replace('#','\#'), ip.IP.user_ns, get_locals())) | ||
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 | ||||
sig = ip.IP.__sig__ | ||||
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). | ||||
ip.IP.resetbuffer() | ||||
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: | ||||
more = ip.IP.push(ip.IP.prefilter(command,more)) | ||||
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: | ||||
ip.IP.push('\n') | ||||
ip.IP.runlines = _runlines | ||||
vivainio
|
r187 | |||
main() | ||||