Show More
@@ -0,0 +1,311 b'' | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | """ | |||
|
3 | %jot magic for lightweight persistence. | |||
|
4 | ||||
|
5 | Stores variables in Struct with some notes in PicleShare database | |||
|
6 | ||||
|
7 | ||||
|
8 | """ | |||
|
9 | ||||
|
10 | from datetime import datetime | |||
|
11 | import IPython.ipapi | |||
|
12 | ip = IPython.ipapi.get() | |||
|
13 | ||||
|
14 | import pickleshare | |||
|
15 | ||||
|
16 | import inspect,pickle,os,sys,textwrap | |||
|
17 | from IPython.FakeModule import FakeModule | |||
|
18 | from IPython.ipstruct import Struct | |||
|
19 | ||||
|
20 | ||||
|
21 | def refresh_variables(ip, key=None): | |||
|
22 | db = ip.db | |||
|
23 | if key is None: | |||
|
24 | keys = db.keys('jot/*') | |||
|
25 | else: | |||
|
26 | keys = db.keys('jot/'+key) | |||
|
27 | for key in keys: | |||
|
28 | # strip autorestore | |||
|
29 | justkey = os.path.basename(key) | |||
|
30 | print "Restoring from", justkey, "..." | |||
|
31 | try: | |||
|
32 | obj = db[key] | |||
|
33 | except KeyError: | |||
|
34 | print "Unable to restore variable '%s', ignoring (use %%jot -d to forget!)" % justkey | |||
|
35 | print "The error was:",sys.exc_info()[0] | |||
|
36 | else: | |||
|
37 | #print "restored",justkey,"=",obj #dbg | |||
|
38 | try: | |||
|
39 | origname = obj.name | |||
|
40 | except: | |||
|
41 | ip.user_ns[justkey] = obj | |||
|
42 | print "Restored", justkey | |||
|
43 | else: | |||
|
44 | ip.user_ns[origname] = obj['val'] | |||
|
45 | print "Restored", origname | |||
|
46 | ||||
|
47 | def read_variables(ip, key=None): | |||
|
48 | db = ip.db | |||
|
49 | if key is None: | |||
|
50 | return None | |||
|
51 | else: | |||
|
52 | keys = db.keys('jot/'+key) | |||
|
53 | for key in keys: | |||
|
54 | # strip autorestore | |||
|
55 | justkey = os.path.basename(key) | |||
|
56 | print "restoring from ", justkey | |||
|
57 | try: | |||
|
58 | obj = db[key] | |||
|
59 | except KeyError: | |||
|
60 | print "Unable to read variable '%s', ignoring (use %%jot -d to forget!)" % justkey | |||
|
61 | print "The error was:",sys.exc_info()[0] | |||
|
62 | else: | |||
|
63 | return obj | |||
|
64 | ||||
|
65 | ||||
|
66 | def detail_variables(ip, key=None): | |||
|
67 | db, get = ip.db, ip.db.get | |||
|
68 | ||||
|
69 | if key is None: | |||
|
70 | keys = db.keys('jot/*') | |||
|
71 | else: | |||
|
72 | keys = db.keys('jot/'+key) | |||
|
73 | if keys: | |||
|
74 | size = max(map(len,keys)) | |||
|
75 | else: | |||
|
76 | size = 0 | |||
|
77 | ||||
|
78 | fmthead = '%-'+str(size)+'s [%s]' | |||
|
79 | fmtbody = 'Comment:\n %s' | |||
|
80 | fmtdata = 'Data:\n %s, %s' | |||
|
81 | for key in keys: | |||
|
82 | v = get(key,'<unavailable>') | |||
|
83 | justkey = os.path.basename(key) | |||
|
84 | try: | |||
|
85 | print fmthead % (justkey, datetime.ctime(v.get('time','<unavailable>'))) | |||
|
86 | print fmtbody % (v.get('comment','<unavailable>')) | |||
|
87 | d = v.get('val','unavailable') | |||
|
88 | print fmtdata % (repr(type(d)), '') | |||
|
89 | print repr(d)[0:200] | |||
|
90 | ||||
|
91 | ||||
|
92 | except AttributeError: | |||
|
93 | print fmt % (justkey, '<unavailable>', '<unavailable>', repr(v)[:50]) | |||
|
94 | ||||
|
95 | ||||
|
96 | def intm(n): | |||
|
97 | try: | |||
|
98 | return int(n) | |||
|
99 | except: | |||
|
100 | return 0 | |||
|
101 | ||||
|
102 | def jot_obj(self, obj, name, comment=''): | |||
|
103 | """ | |||
|
104 | write obj data to the note database, with whatever that should be noted. | |||
|
105 | """ | |||
|
106 | had = self.db.keys('jot/'+name+'*') | |||
|
107 | # if it the same name but a later version, we stupidly add a number to the | |||
|
108 | # so the name doesn't collide. Any better idea? | |||
|
109 | suffix = '' | |||
|
110 | if len(had)>0: | |||
|
111 | pre = os.path.commonprefix(had) | |||
|
112 | suf = [n.split(pre)[1] for n in had] | |||
|
113 | versions = map(intm, suf) | |||
|
114 | suffix = str(max(versions)+1) | |||
|
115 | ||||
|
116 | uname = 'jot/'+name+suffix | |||
|
117 | ||||
|
118 | # which one works better? | |||
|
119 | #all = ip.IP.shadowhist.all() | |||
|
120 | all = ip.IP.shell.input_hist | |||
|
121 | ||||
|
122 | # We may actually want to make snapshot of files that are run-ned. | |||
|
123 | ||||
|
124 | # get the comment | |||
|
125 | try: | |||
|
126 | comment = ip.IP.magic_edit('-x').strip() | |||
|
127 | except: | |||
|
128 | print "No comment is recorded." | |||
|
129 | comment = '' | |||
|
130 | ||||
|
131 | self.db[uname] = Struct({'val':obj, | |||
|
132 | 'time' : datetime.now(), | |||
|
133 | 'hist' : all, | |||
|
134 | 'name' : name, | |||
|
135 | 'comment' : comment,}) | |||
|
136 | ||||
|
137 | print "Jotted down notes for '%s' (%s)" % (uname, obj.__class__.__name__) | |||
|
138 | ||||
|
139 | ||||
|
140 | ||||
|
141 | def magic_jot(self, parameter_s=''): | |||
|
142 | """Lightweight persistence for python variables. | |||
|
143 | ||||
|
144 | Example: | |||
|
145 | ||||
|
146 | ville@badger[~]|1> A = ['hello',10,'world']\\ | |||
|
147 | ville@badger[~]|2> %jot A\\ | |||
|
148 | ville@badger[~]|3> Exit | |||
|
149 | ||||
|
150 | (IPython session is closed and started again...) | |||
|
151 | ||||
|
152 | ville@badger:~$ ipython -p pysh\\ | |||
|
153 | ville@badger[~]|1> print A | |||
|
154 | ||||
|
155 | ['hello', 10, 'world'] | |||
|
156 | ||||
|
157 | Usage: | |||
|
158 | ||||
|
159 | %jot - Show list of all variables and their current values\\ | |||
|
160 | %jot -l - Show list of all variables and their current values in detail\\ | |||
|
161 | %jot -l <var> - Show one variable and its current values in detail\\ | |||
|
162 | %jot <var> - Store the *current* value of the variable to disk\\ | |||
|
163 | %jot -d <var> - Remove the variable and its value from storage\\ | |||
|
164 | %jot -z - Remove all variables from storage (disabled)\\ | |||
|
165 | %jot -r <var> - Refresh/Load variable from jot (delete current vals)\\ | |||
|
166 | %jot foo >a.txt - Store value of foo to new file a.txt\\ | |||
|
167 | %jot foo >>a.txt - Append value of foo to file a.txt\\ | |||
|
168 | ||||
|
169 | It should be noted that if you change the value of a variable, you | |||
|
170 | need to %note it again if you want to persist the new value. | |||
|
171 | ||||
|
172 | Note also that the variables will need to be pickleable; most basic | |||
|
173 | python types can be safely %stored. | |||
|
174 | ||||
|
175 | """ | |||
|
176 | ||||
|
177 | opts,argsl = self.parse_options(parameter_s,'drzl',mode='string') | |||
|
178 | args = argsl.split(None,1) | |||
|
179 | ip = self.getapi() | |||
|
180 | db = ip.db | |||
|
181 | # delete | |||
|
182 | if opts.has_key('d'): | |||
|
183 | try: | |||
|
184 | todel = args[0] | |||
|
185 | except IndexError: | |||
|
186 | error('You must provide the variable to forget') | |||
|
187 | else: | |||
|
188 | try: | |||
|
189 | del db['jot/' + todel] | |||
|
190 | except: | |||
|
191 | error("Can't delete variable '%s'" % todel) | |||
|
192 | # reset the whole database | |||
|
193 | elif opts.has_key('z'): | |||
|
194 | print "reseting the whole database has been disabled." | |||
|
195 | #for k in db.keys('autorestore/*'): | |||
|
196 | # del db[k] | |||
|
197 | ||||
|
198 | elif opts.has_key('r'): | |||
|
199 | try: | |||
|
200 | toret = args[0] | |||
|
201 | except: | |||
|
202 | print "restoring all the variables jotted down..." | |||
|
203 | refresh_variables(ip) | |||
|
204 | else: | |||
|
205 | refresh_variables(ip, toret) | |||
|
206 | ||||
|
207 | elif opts.has_key('l'): | |||
|
208 | try: | |||
|
209 | tolist = args[0] | |||
|
210 | except: | |||
|
211 | print "List details for all the items." | |||
|
212 | detail_variables(ip) | |||
|
213 | else: | |||
|
214 | print "Details for", tolist, ":" | |||
|
215 | detail_variables(ip, tolist) | |||
|
216 | ||||
|
217 | # run without arguments -> list noted variables & notes | |||
|
218 | elif not args: | |||
|
219 | vars = self.db.keys('jot/*') | |||
|
220 | vars.sort() | |||
|
221 | if vars: | |||
|
222 | size = max(map(len,vars)) - 4 | |||
|
223 | else: | |||
|
224 | size = 0 | |||
|
225 | ||||
|
226 | print 'Variables and their in-db values:' | |||
|
227 | fmt = '%-'+str(size)+'s [%s] -> %s' | |||
|
228 | get = db.get | |||
|
229 | for var in vars: | |||
|
230 | justkey = os.path.basename(var) | |||
|
231 | v = get(var,'<unavailable>') | |||
|
232 | try: | |||
|
233 | print fmt % (justkey,\ | |||
|
234 | datetime.ctime(v.get('time','<unavailable>')),\ | |||
|
235 | v.get('comment','<unavailable>')[:70].replace('\n',' '),) | |||
|
236 | except AttributeError: | |||
|
237 | print fmt % (justkey, '<unavailable>', '<unavailable>', repr(v)[:50]) | |||
|
238 | ||||
|
239 | ||||
|
240 | # default action - store the variable | |||
|
241 | else: | |||
|
242 | # %store foo >file.txt or >>file.txt | |||
|
243 | if len(args) > 1 and args[1].startswith('>'): | |||
|
244 | fnam = os.path.expanduser(args[1].lstrip('>').lstrip()) | |||
|
245 | if args[1].startswith('>>'): | |||
|
246 | fil = open(fnam,'a') | |||
|
247 | else: | |||
|
248 | fil = open(fnam,'w') | |||
|
249 | obj = ip.ev(args[0]) | |||
|
250 | print "Writing '%s' (%s) to file '%s'." % (args[0], | |||
|
251 | obj.__class__.__name__, fnam) | |||
|
252 | ||||
|
253 | ||||
|
254 | if not isinstance (obj,basestring): | |||
|
255 | from pprint import pprint | |||
|
256 | pprint(obj,fil) | |||
|
257 | else: | |||
|
258 | fil.write(obj) | |||
|
259 | if not obj.endswith('\n'): | |||
|
260 | fil.write('\n') | |||
|
261 | ||||
|
262 | fil.close() | |||
|
263 | return | |||
|
264 | ||||
|
265 | # %note foo | |||
|
266 | try: | |||
|
267 | obj = ip.user_ns[args[0]] | |||
|
268 | except KeyError: | |||
|
269 | # this should not be alias, for aliases, use %store | |||
|
270 | ||||
|
271 | print "Error: %s doesn't exist." % args[0] | |||
|
272 | ||||
|
273 | print "Use %note -r <var> to retrieve variables. This should not be used " +\ | |||
|
274 | "to store alias, for saving aliases, use %store" | |||
|
275 | return | |||
|
276 | else: | |||
|
277 | if isinstance(inspect.getmodule(obj), FakeModule): | |||
|
278 | print textwrap.dedent("""\ | |||
|
279 | Warning:%s is %s | |||
|
280 | Proper storage of interactively declared classes (or instances | |||
|
281 | of those classes) is not possible! Only instances | |||
|
282 | of classes in real modules on file system can be %%store'd. | |||
|
283 | """ % (args[0], obj) ) | |||
|
284 | return | |||
|
285 | #pickled = pickle.dumps(obj) | |||
|
286 | #self.db[ 'jot/' + args[0] ] = obj | |||
|
287 | jot_obj(self, obj, args[0]) | |||
|
288 | ||||
|
289 | ||||
|
290 | def magic_read(self, parameter_s=''): | |||
|
291 | """ | |||
|
292 | %read <var> - Load variable from data that is jotted down.\\ | |||
|
293 | ||||
|
294 | """ | |||
|
295 | ||||
|
296 | opts,argsl = self.parse_options(parameter_s,'drzl',mode='string') | |||
|
297 | args = argsl.split(None,1) | |||
|
298 | ip = self.getapi() | |||
|
299 | db = ip.db | |||
|
300 | #if opts.has_key('r'): | |||
|
301 | try: | |||
|
302 | toret = args[0] | |||
|
303 | except: | |||
|
304 | print "which record do you want to read out?" | |||
|
305 | return | |||
|
306 | else: | |||
|
307 | return read_variables(ip, toret) | |||
|
308 | ||||
|
309 | ||||
|
310 | ip.expose_magic('jot',magic_jot) | |||
|
311 | ip.expose_magic('read',magic_read) |
@@ -3,6 +3,11 b'' | |||||
3 | * Extensions/ipy_lookfor.py: add %lookfor magic command |
|
3 | * Extensions/ipy_lookfor.py: add %lookfor magic command | |
4 | (search docstrings for words) by Pauli Virtanen. Close #245. |
|
4 | (search docstrings for words) by Pauli Virtanen. Close #245. | |
5 |
|
5 | |||
|
6 | * Extension/ipy_jot.py: %jot and %read magics, analogous | |||
|
7 | to %store but you can specify some notes. Not read | |||
|
8 | in automatically on startup, you need %read. | |||
|
9 | Contributed by Yichun Wei. | |||
|
10 | ||||
6 | 2008-04-18 Fernando Perez <Fernando.Perez@berkeley.edu> |
|
11 | 2008-04-18 Fernando Perez <Fernando.Perez@berkeley.edu> | |
7 |
|
12 | |||
8 | * IPython/genutils.py (page): apply workaround to curses bug that |
|
13 | * IPython/genutils.py (page): apply workaround to curses bug that |
General Comments 0
You need to be logged in to leave comments.
Login now