ipy_jot.py
312 lines
| 9.1 KiB
| text/x-python
|
PythonLexer
Ville M. Vainio
|
r1165 | # -*- coding: utf-8 -*- | ||
""" | ||||
%jot magic for lightweight persistence. | ||||
Stores variables in Struct with some notes in PicleShare database | ||||
""" | ||||
Bernardo B. Marques
|
r4872 | from datetime import datetime | ||
Brian Granger
|
r2027 | from IPython.core import ipapi | ||
ip = ipapi.get() | ||||
Ville M. Vainio
|
r1165 | |||
import pickleshare | ||||
import inspect,pickle,os,sys,textwrap | ||||
Brian Granger
|
r2020 | from IPython.core.fakemodule import FakeModule | ||
Bernardo B. Marques
|
r4872 | from IPython.utils.ipstruct import Struct | ||
Brian Granger
|
r2498 | from IPython.utils.warn import error | ||
Ville M. Vainio
|
r1165 | |||
def refresh_variables(ip, key=None): | ||||
db = ip.db | ||||
if key is None: | ||||
keys = db.keys('jot/*') | ||||
else: | ||||
keys = db.keys('jot/'+key) | ||||
for key in keys: | ||||
# strip autorestore | ||||
justkey = os.path.basename(key) | ||||
print "Restoring from", justkey, "..." | ||||
try: | ||||
obj = db[key] | ||||
except KeyError: | ||||
print "Unable to restore variable '%s', ignoring (use %%jot -d to forget!)" % justkey | ||||
print "The error was:",sys.exc_info()[0] | ||||
else: | ||||
#print "restored",justkey,"=",obj #dbg | ||||
try: | ||||
origname = obj.name | ||||
except: | ||||
ip.user_ns[justkey] = obj | ||||
print "Restored", justkey | ||||
else: | ||||
ip.user_ns[origname] = obj['val'] | ||||
Bernardo B. Marques
|
r4872 | print "Restored", origname | ||
Ville M. Vainio
|
r1165 | |||
def read_variables(ip, key=None): | ||||
db = ip.db | ||||
if key is None: | ||||
return None | ||||
else: | ||||
keys = db.keys('jot/'+key) | ||||
for key in keys: | ||||
# strip autorestore | ||||
justkey = os.path.basename(key) | ||||
Bernardo B. Marques
|
r4872 | print "restoring from ", justkey | ||
Ville M. Vainio
|
r1165 | try: | ||
obj = db[key] | ||||
except KeyError: | ||||
print "Unable to read variable '%s', ignoring (use %%jot -d to forget!)" % justkey | ||||
print "The error was:",sys.exc_info()[0] | ||||
else: | ||||
return obj | ||||
def detail_variables(ip, key=None): | ||||
db, get = ip.db, ip.db.get | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1165 | if key is None: | ||
keys = db.keys('jot/*') | ||||
else: | ||||
keys = db.keys('jot/'+key) | ||||
if keys: | ||||
size = max(map(len,keys)) | ||||
else: | ||||
size = 0 | ||||
fmthead = '%-'+str(size)+'s [%s]' | ||||
fmtbody = 'Comment:\n %s' | ||||
fmtdata = 'Data:\n %s, %s' | ||||
for key in keys: | ||||
v = get(key,'<unavailable>') | ||||
justkey = os.path.basename(key) | ||||
Bernardo B. Marques
|
r4872 | try: | ||
Ville M. Vainio
|
r1165 | print fmthead % (justkey, datetime.ctime(v.get('time','<unavailable>'))) | ||
print fmtbody % (v.get('comment','<unavailable>')) | ||||
d = v.get('val','unavailable') | ||||
print fmtdata % (repr(type(d)), '') | ||||
print repr(d)[0:200] | ||||
except AttributeError: | ||||
print fmt % (justkey, '<unavailable>', '<unavailable>', repr(v)[:50]) | ||||
def intm(n): | ||||
try: | ||||
return int(n) | ||||
except: | ||||
return 0 | ||||
def jot_obj(self, obj, name, comment=''): | ||||
""" | ||||
write obj data to the note database, with whatever that should be noted. | ||||
""" | ||||
had = self.db.keys('jot/'+name+'*') | ||||
Bernardo B. Marques
|
r4872 | # if it the same name but a later version, we stupidly add a number to the | ||
Ville M. Vainio
|
r1165 | # so the name doesn't collide. Any better idea? | ||
suffix = '' | ||||
if len(had)>0: | ||||
pre = os.path.commonprefix(had) | ||||
suf = [n.split(pre)[1] for n in had] | ||||
versions = map(intm, suf) | ||||
suffix = str(max(versions)+1) | ||||
uname = 'jot/'+name+suffix | ||||
Bernardo B. Marques
|
r4872 | # which one works better? | ||
Brian Granger
|
r2205 | #all = ip.shadowhist.all() | ||
Satrajit Ghosh
|
r3240 | all = ip.shell.history_manager.input_hist_parsed | ||
Ville M. Vainio
|
r1165 | |||
# We may actually want to make snapshot of files that are run-ned. | ||||
Bernardo B. Marques
|
r4872 | # get the comment | ||
Ville M. Vainio
|
r1165 | try: | ||
Brian Granger
|
r2205 | comment = ip.magic_edit('-x').strip() | ||
Ville M. Vainio
|
r1165 | except: | ||
print "No comment is recorded." | ||||
comment = '' | ||||
Bernardo B. Marques
|
r4872 | self.db[uname] = Struct({'val':obj, | ||
'time' : datetime.now(), | ||||
Ville M. Vainio
|
r1165 | 'hist' : all, | ||
'name' : name, | ||||
'comment' : comment,}) | ||||
print "Jotted down notes for '%s' (%s)" % (uname, obj.__class__.__name__) | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1165 | |||
def magic_jot(self, parameter_s=''): | ||||
"""Lightweight persistence for python variables. | ||||
Example: | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1165 | ville@badger[~]|1> A = ['hello',10,'world']\\ | ||
ville@badger[~]|2> %jot A\\ | ||||
ville@badger[~]|3> Exit | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1165 | (IPython session is closed and started again...) | ||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1165 | ville@badger:~$ ipython -p pysh\\ | ||
ville@badger[~]|1> print A | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1165 | ['hello', 10, 'world'] | ||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1165 | Usage: | ||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1165 | %jot - Show list of all variables and their current values\\ | ||
%jot -l - Show list of all variables and their current values in detail\\ | ||||
%jot -l <var> - Show one variable and its current values in detail\\ | ||||
%jot <var> - Store the *current* value of the variable to disk\\ | ||||
%jot -d <var> - Remove the variable and its value from storage\\ | ||||
%jot -z - Remove all variables from storage (disabled)\\ | ||||
%jot -r <var> - Refresh/Load variable from jot (delete current vals)\\ | ||||
%jot foo >a.txt - Store value of foo to new file a.txt\\ | ||||
Bernardo B. Marques
|
r4872 | %jot foo >>a.txt - Append value of foo to file a.txt\\ | ||
Ville M. Vainio
|
r1165 | It should be noted that if you change the value of a variable, you | ||
need to %note it again if you want to persist the new value. | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1165 | Note also that the variables will need to be pickleable; most basic | ||
python types can be safely %stored. | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1165 | """ | ||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1165 | opts,argsl = self.parse_options(parameter_s,'drzl',mode='string') | ||
args = argsl.split(None,1) | ||||
ip = self.getapi() | ||||
db = ip.db | ||||
# delete | ||||
if opts.has_key('d'): | ||||
try: | ||||
todel = args[0] | ||||
except IndexError: | ||||
error('You must provide the variable to forget') | ||||
else: | ||||
try: | ||||
del db['jot/' + todel] | ||||
except: | ||||
error("Can't delete variable '%s'" % todel) | ||||
# reset the whole database | ||||
elif opts.has_key('z'): | ||||
print "reseting the whole database has been disabled." | ||||
#for k in db.keys('autorestore/*'): | ||||
# del db[k] | ||||
elif opts.has_key('r'): | ||||
try: | ||||
toret = args[0] | ||||
except: | ||||
print "restoring all the variables jotted down..." | ||||
refresh_variables(ip) | ||||
else: | ||||
refresh_variables(ip, toret) | ||||
elif opts.has_key('l'): | ||||
try: | ||||
tolist = args[0] | ||||
except: | ||||
print "List details for all the items." | ||||
detail_variables(ip) | ||||
else: | ||||
print "Details for", tolist, ":" | ||||
detail_variables(ip, tolist) | ||||
Bernardo B. Marques
|
r4872 | |||
# run without arguments -> list noted variables & notes | ||||
Ville M. Vainio
|
r1165 | elif not args: | ||
vars = self.db.keys('jot/*') | ||||
Bernardo B. Marques
|
r4872 | vars.sort() | ||
Ville M. Vainio
|
r1165 | if vars: | ||
Bernardo B. Marques
|
r4872 | size = max(map(len,vars)) - 4 | ||
Ville M. Vainio
|
r1165 | else: | ||
size = 0 | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1165 | print 'Variables and their in-db values:' | ||
fmt = '%-'+str(size)+'s [%s] -> %s' | ||||
get = db.get | ||||
for var in vars: | ||||
justkey = os.path.basename(var) | ||||
v = get(var,'<unavailable>') | ||||
Bernardo B. Marques
|
r4872 | try: | ||
Ville M. Vainio
|
r1165 | print fmt % (justkey,\ | ||
datetime.ctime(v.get('time','<unavailable>')),\ | ||||
v.get('comment','<unavailable>')[:70].replace('\n',' '),) | ||||
except AttributeError: | ||||
print fmt % (justkey, '<unavailable>', '<unavailable>', repr(v)[:50]) | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1165 | # default action - store the variable | ||
else: | ||||
# %store foo >file.txt or >>file.txt | ||||
if len(args) > 1 and args[1].startswith('>'): | ||||
fnam = os.path.expanduser(args[1].lstrip('>').lstrip()) | ||||
if args[1].startswith('>>'): | ||||
fil = open(fnam,'a') | ||||
else: | ||||
fil = open(fnam,'w') | ||||
obj = ip.ev(args[0]) | ||||
print "Writing '%s' (%s) to file '%s'." % (args[0], | ||||
obj.__class__.__name__, fnam) | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1165 | if not isinstance (obj,basestring): | ||
from pprint import pprint | ||||
pprint(obj,fil) | ||||
else: | ||||
fil.write(obj) | ||||
if not obj.endswith('\n'): | ||||
fil.write('\n') | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1165 | fil.close() | ||
return | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1165 | # %note foo | ||
try: | ||||
obj = ip.user_ns[args[0]] | ||||
except KeyError: | ||||
# this should not be alias, for aliases, use %store | ||||
print "Error: %s doesn't exist." % args[0] | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1165 | print "Use %note -r <var> to retrieve variables. This should not be used " +\ | ||
Bernardo B. Marques
|
r4872 | "to store alias, for saving aliases, use %store" | ||
Ville M. Vainio
|
r1165 | return | ||
else: | ||||
if isinstance(inspect.getmodule(obj), FakeModule): | ||||
print textwrap.dedent("""\ | ||||
Bernardo B. Marques
|
r4872 | Warning:%s is %s | ||
Ville M. Vainio
|
r1165 | Proper storage of interactively declared classes (or instances | ||
of those classes) is not possible! Only instances | ||||
of classes in real modules on file system can be %%store'd. | ||||
Bernardo B. Marques
|
r4872 | """ % (args[0], obj) ) | ||
Ville M. Vainio
|
r1165 | return | ||
#pickled = pickle.dumps(obj) | ||||
#self.db[ 'jot/' + args[0] ] = obj | ||||
jot_obj(self, obj, args[0]) | ||||
def magic_read(self, parameter_s=''): | ||||
Bernardo B. Marques
|
r4872 | """ | ||
Ville M. Vainio
|
r1165 | %read <var> - Load variable from data that is jotted down.\\ | ||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1165 | """ | ||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1165 | opts,argsl = self.parse_options(parameter_s,'drzl',mode='string') | ||
args = argsl.split(None,1) | ||||
ip = self.getapi() | ||||
db = ip.db | ||||
#if opts.has_key('r'): | ||||
try: | ||||
toret = args[0] | ||||
except: | ||||
print "which record do you want to read out?" | ||||
return | ||||
else: | ||||
return read_variables(ip, toret) | ||||
Brian Granger
|
r2205 | ip.define_magic('jot',magic_jot) | ||
ip.define_magic('read',magic_read) | ||||