Show More
@@ -0,0 +1,267 b'' | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | ||||
|
3 | """ PickleShare - a small 'shelve' like datastore with concurrency support | |||
|
4 | ||||
|
5 | Like shelve, a PickleShareDB object acts like a normal dictionary. Unlike | |||
|
6 | shelve, many processes can access the database simultaneously. Changing a | |||
|
7 | value in database is immediately visible to other processes accessing the | |||
|
8 | same database. | |||
|
9 | ||||
|
10 | Concurrency is possible because the values are stored in separate files. Hence | |||
|
11 | the "database" is a directory where *all* files are governed by PickleShare. | |||
|
12 | ||||
|
13 | Example usage:: | |||
|
14 | ||||
|
15 | from pickleshare import * | |||
|
16 | db = PickleShareDB('~/testpickleshare') | |||
|
17 | db.clear() | |||
|
18 | print "Should be empty:",db.items() | |||
|
19 | db['hello'] = 15 | |||
|
20 | db['aku ankka'] = [1,2,313] | |||
|
21 | db['paths/are/ok/key'] = [1,(5,46)] | |||
|
22 | print db.keys() | |||
|
23 | del db['aku ankka'] | |||
|
24 | ||||
|
25 | This module is certainly not ZODB, but can be used for low-load | |||
|
26 | (non-mission-critical) situations where tiny code size trumps the | |||
|
27 | advanced features of a "real" object database. | |||
|
28 | ||||
|
29 | Installation guide: easy_install pickleshare | |||
|
30 | ||||
|
31 | Author: Ville Vainio <vivainio@gmail.com> | |||
|
32 | License: MIT open source license. | |||
|
33 | ||||
|
34 | """ | |||
|
35 | ||||
|
36 | from path import path as Path | |||
|
37 | import os,stat,time | |||
|
38 | import cPickle as pickle | |||
|
39 | import UserDict | |||
|
40 | import warnings | |||
|
41 | import glob | |||
|
42 | ||||
|
43 | class PickleShareDB(UserDict.DictMixin): | |||
|
44 | """ The main 'connection' object for PickleShare database """ | |||
|
45 | def __init__(self,root): | |||
|
46 | """ Return a db object that will manage the specied directory""" | |||
|
47 | self.root = Path(root).expanduser().abspath() | |||
|
48 | if not self.root.isdir(): | |||
|
49 | self.root.makedirs() | |||
|
50 | # cache has { 'key' : (obj, orig_mod_time) } | |||
|
51 | self.cache = {} | |||
|
52 | ||||
|
53 | def __getitem__(self,key): | |||
|
54 | """ db['key'] reading """ | |||
|
55 | fil = self.root / key | |||
|
56 | try: | |||
|
57 | mtime = (fil.stat()[stat.ST_MTIME]) | |||
|
58 | except OSError: | |||
|
59 | raise KeyError(key) | |||
|
60 | ||||
|
61 | if fil in self.cache and mtime == self.cache[fil][1]: | |||
|
62 | return self.cache[fil][0] | |||
|
63 | try: | |||
|
64 | # The cached item has expired, need to read | |||
|
65 | obj = pickle.load(fil.open()) | |||
|
66 | except: | |||
|
67 | raise KeyError(key) | |||
|
68 | ||||
|
69 | self.cache[fil] = (obj,mtime) | |||
|
70 | return obj | |||
|
71 | ||||
|
72 | def __setitem__(self,key,value): | |||
|
73 | """ db['key'] = 5 """ | |||
|
74 | fil = self.root / key | |||
|
75 | parent = fil.parent | |||
|
76 | if parent and not parent.isdir(): | |||
|
77 | parent.makedirs() | |||
|
78 | pickled = pickle.dump(value,fil.open('w')) | |||
|
79 | try: | |||
|
80 | self.cache[fil] = (value,fil.mtime) | |||
|
81 | except OSError,e: | |||
|
82 | if e.errno != 2: | |||
|
83 | raise | |||
|
84 | ||||
|
85 | def __delitem__(self,key): | |||
|
86 | """ del db["key"] """ | |||
|
87 | fil = self.root / key | |||
|
88 | self.cache.pop(fil,None) | |||
|
89 | try: | |||
|
90 | fil.remove() | |||
|
91 | except OSError: | |||
|
92 | # notfound and permission denied are ok - we | |||
|
93 | # lost, the other process wins the conflict | |||
|
94 | pass | |||
|
95 | ||||
|
96 | def _normalized(self, p): | |||
|
97 | """ Make a key suitable for user's eyes """ | |||
|
98 | return str(self.root.relpathto(p)).replace('\\','/') | |||
|
99 | ||||
|
100 | def keys(self, globpat = None): | |||
|
101 | """ All keys in DB, or all keys matching a glob""" | |||
|
102 | ||||
|
103 | if globpat is None: | |||
|
104 | files = self.root.walkfiles() | |||
|
105 | else: | |||
|
106 | files = [Path(p) for p in glob.glob(self.root/globpat)] | |||
|
107 | return [self._normalized(p) for p in files if p.isfile()] | |||
|
108 | ||||
|
109 | def uncache(self,*items): | |||
|
110 | """ Removes all, or specified items from cache | |||
|
111 | ||||
|
112 | Use this after reading a large amount of large objects | |||
|
113 | to free up memory, when you won't be needing the objects | |||
|
114 | for a while. | |||
|
115 | ||||
|
116 | """ | |||
|
117 | if not items: | |||
|
118 | self.cache = {} | |||
|
119 | for it in items: | |||
|
120 | self.cache.pop(it,None) | |||
|
121 | ||||
|
122 | def waitget(self,key, maxwaittime = 60 ): | |||
|
123 | """ Wait (poll) for a key to get a value | |||
|
124 | ||||
|
125 | Will wait for `maxwaittime` seconds before raising a KeyError. | |||
|
126 | The call exits normally if the `key` field in db gets a value | |||
|
127 | within the timeout period. | |||
|
128 | ||||
|
129 | Use this for synchronizing different processes or for ensuring | |||
|
130 | that an unfortunately timed "db['key'] = newvalue" operation | |||
|
131 | in another process (which causes all 'get' operation to cause a | |||
|
132 | KeyError for the duration of pickling) won't screw up your program | |||
|
133 | logic. | |||
|
134 | """ | |||
|
135 | ||||
|
136 | wtimes = [0.2] * 3 + [0.5] * 2 + [1] | |||
|
137 | tries = 0 | |||
|
138 | waited = 0 | |||
|
139 | while 1: | |||
|
140 | try: | |||
|
141 | val = self[key] | |||
|
142 | return val | |||
|
143 | except KeyError: | |||
|
144 | pass | |||
|
145 | ||||
|
146 | if waited > maxwaittime: | |||
|
147 | raise KeyError(key) | |||
|
148 | ||||
|
149 | time.sleep(wtimes[tries]) | |||
|
150 | waited+=wtimes[tries] | |||
|
151 | if tries < len(wtimes) -1: | |||
|
152 | tries+=1 | |||
|
153 | ||||
|
154 | def getlink(self,folder): | |||
|
155 | """ Get a convenient link for accessing items """ | |||
|
156 | return PickleShareLink(self, folder) | |||
|
157 | ||||
|
158 | def __repr__(self): | |||
|
159 | return "PickleShareDB('%s')" % self.root | |||
|
160 | ||||
|
161 | ||||
|
162 | ||||
|
163 | class PickleShareLink: | |||
|
164 | """ A shortdand for accessing nested PickleShare data conveniently. | |||
|
165 | ||||
|
166 | Created through PickleShareDB.getlink(), example:: | |||
|
167 | ||||
|
168 | lnk = db.getlink('myobjects/test') | |||
|
169 | lnk.foo = 2 | |||
|
170 | lnk.bar = lnk.foo + 5 | |||
|
171 | ||||
|
172 | """ | |||
|
173 | def __init__(self, db, keydir ): | |||
|
174 | self.__dict__.update(locals()) | |||
|
175 | ||||
|
176 | def __getattr__(self,key): | |||
|
177 | return self.__dict__['db'][self.__dict__['keydir']+'/' + key] | |||
|
178 | def __setattr__(self,key,val): | |||
|
179 | self.db[self.keydir+'/' + key] = val | |||
|
180 | def __repr__(self): | |||
|
181 | db = self.__dict__['db'] | |||
|
182 | keys = db.keys( self.__dict__['keydir'] +"/*") | |||
|
183 | return "<PickleShareLink '%s': %s>" % ( | |||
|
184 | self.__dict__['keydir'], | |||
|
185 | ";".join([Path(k).basename() for k in keys])) | |||
|
186 | ||||
|
187 | ||||
|
188 | def test(): | |||
|
189 | db = PickleShareDB('~/testpickleshare') | |||
|
190 | db.clear() | |||
|
191 | print "Should be empty:",db.items() | |||
|
192 | db['hello'] = 15 | |||
|
193 | db['aku ankka'] = [1,2,313] | |||
|
194 | db['paths/nest/ok/keyname'] = [1,(5,46)] | |||
|
195 | print db.keys() | |||
|
196 | print db.keys('paths/nest/ok/k*') | |||
|
197 | print dict(db) # snapsot of whole db | |||
|
198 | db.uncache() # frees memory, causes re-reads later | |||
|
199 | ||||
|
200 | # shorthand for accessing deeply nested files | |||
|
201 | lnk = db.getlink('myobjects/test') | |||
|
202 | lnk.foo = 2 | |||
|
203 | lnk.bar = lnk.foo + 5 | |||
|
204 | print lnk.bar # 7 | |||
|
205 | ||||
|
206 | def stress(): | |||
|
207 | db = PickleShareDB('~/fsdbtest') | |||
|
208 | import time,sys | |||
|
209 | for i in range(1000): | |||
|
210 | for j in range(300): | |||
|
211 | if i % 15 == 0 and i < 200: | |||
|
212 | if str(j) in db: | |||
|
213 | del db[str(j)] | |||
|
214 | continue | |||
|
215 | ||||
|
216 | if j%33 == 0: | |||
|
217 | time.sleep(0.02) | |||
|
218 | ||||
|
219 | db[str(j)] = db.get(str(j), []) + [(i,j,"proc %d" % os.getpid())] | |||
|
220 | print i, | |||
|
221 | sys.stdout.flush() | |||
|
222 | if i % 10 == 0: | |||
|
223 | db.uncache() | |||
|
224 | ||||
|
225 | def main(): | |||
|
226 | import textwrap | |||
|
227 | usage = textwrap.dedent("""\ | |||
|
228 | pickleshare - manage PickleShare databases | |||
|
229 | ||||
|
230 | Usage: | |||
|
231 | ||||
|
232 | pickleshare dump /path/to/db > dump.txt | |||
|
233 | pickleshare load /path/to/db < dump.txt | |||
|
234 | pickleshare test /path/to/db | |||
|
235 | """) | |||
|
236 | DB = PickleShareDB | |||
|
237 | import sys | |||
|
238 | if len(sys.argv) < 2: | |||
|
239 | print usage | |||
|
240 | return | |||
|
241 | ||||
|
242 | cmd = sys.argv[1] | |||
|
243 | args = sys.argv[2:] | |||
|
244 | if cmd == 'dump': | |||
|
245 | if not args: args= ['.'] | |||
|
246 | db = DB(args[0]) | |||
|
247 | import pprint | |||
|
248 | pprint.pprint(db.items()) | |||
|
249 | elif cmd == 'load': | |||
|
250 | cont = sys.stdin.read() | |||
|
251 | db = DB(args[0]) | |||
|
252 | data = eval(cont) | |||
|
253 | db.clear() | |||
|
254 | for k,v in db.items(): | |||
|
255 | db[k] = v | |||
|
256 | elif cmd == 'testwait': | |||
|
257 | db = DB(args[0]) | |||
|
258 | db.clear() | |||
|
259 | print db.waitget('250') | |||
|
260 | elif cmd == 'test': | |||
|
261 | test() | |||
|
262 | stress() | |||
|
263 | ||||
|
264 | if __name__== "__main__": | |||
|
265 | main() | |||
|
266 | ||||
|
267 | No newline at end of file |
@@ -0,0 +1,149 b'' | |||||
|
1 | import IPython.ipapi | |||
|
2 | ip = IPython.ipapi.get() | |||
|
3 | ||||
|
4 | import pickleshare | |||
|
5 | ||||
|
6 | import inspect,pickle,os,textwrap | |||
|
7 | from IPython.FakeModule import FakeModule | |||
|
8 | ||||
|
9 | def refresh_variables(ip): | |||
|
10 | db = ip.getdb() | |||
|
11 | for key in db.keys('autorestore/*'): | |||
|
12 | # strip autorestore | |||
|
13 | justkey = os.path.basename(key) | |||
|
14 | try: | |||
|
15 | obj = db[key] | |||
|
16 | except KeyError: | |||
|
17 | print "Unable to restore variable '%s', ignoring (use %%store -d to forget!)" % justkey | |||
|
18 | print "The error was:",sys.exc_info()[0] | |||
|
19 | else: | |||
|
20 | #print "restored",justkey,"=",obj #dbg | |||
|
21 | ip.user_ns()[justkey] = obj | |||
|
22 | ||||
|
23 | ||||
|
24 | ||||
|
25 | def restore_data(self): | |||
|
26 | #o = ip.options() | |||
|
27 | #self.db = pickleshare.PickleShareDB(o.ipythondir + "/db") | |||
|
28 | #print "restoring ps data" # dbg | |||
|
29 | ||||
|
30 | ip = self.getapi() | |||
|
31 | refresh_variables(ip) | |||
|
32 | raise IPython.ipapi.TryNext | |||
|
33 | ||||
|
34 | ||||
|
35 | ip.set_hook('late_startup_hook', restore_data) | |||
|
36 | ||||
|
37 | def magic_store(self, parameter_s=''): | |||
|
38 | """Lightweight persistence for python variables. | |||
|
39 | ||||
|
40 | Example: | |||
|
41 | ||||
|
42 | ville@badger[~]|1> A = ['hello',10,'world']\\ | |||
|
43 | ville@badger[~]|2> %store A\\ | |||
|
44 | ville@badger[~]|3> Exit | |||
|
45 | ||||
|
46 | (IPython session is closed and started again...) | |||
|
47 | ||||
|
48 | ville@badger:~$ ipython -p pysh\\ | |||
|
49 | ville@badger[~]|1> print A | |||
|
50 | ||||
|
51 | ['hello', 10, 'world'] | |||
|
52 | ||||
|
53 | Usage: | |||
|
54 | ||||
|
55 | %store - Show list of all variables and their current values\\ | |||
|
56 | %store <var> - Store the *current* value of the variable to disk\\ | |||
|
57 | %store -d <var> - Remove the variable and its value from storage\\ | |||
|
58 | %store -z - Remove all variables from storage\\ | |||
|
59 | %store -r - Refresh all variables from store (delete current vals)\\ | |||
|
60 | %store foo >a.txt - Store value of foo to new file a.txt\\ | |||
|
61 | %store foo >>a.txt - Append value of foo to file a.txt\\ | |||
|
62 | ||||
|
63 | It should be noted that if you change the value of a variable, you | |||
|
64 | need to %store it again if you want to persist the new value. | |||
|
65 | ||||
|
66 | Note also that the variables will need to be pickleable; most basic | |||
|
67 | python types can be safely %stored. | |||
|
68 | """ | |||
|
69 | ||||
|
70 | opts,argsl = self.parse_options(parameter_s,'drz',mode='string') | |||
|
71 | args = argsl.split(None,1) | |||
|
72 | ip = self.getapi() | |||
|
73 | # delete | |||
|
74 | if opts.has_key('d'): | |||
|
75 | try: | |||
|
76 | todel = args[0] | |||
|
77 | except IndexError: | |||
|
78 | error('You must provide the variable to forget') | |||
|
79 | else: | |||
|
80 | try: | |||
|
81 | del self.db['autorestore/' + todel] | |||
|
82 | except: | |||
|
83 | error("Can't delete variable '%s'" % todel) | |||
|
84 | # reset | |||
|
85 | elif opts.has_key('z'): | |||
|
86 | for k in self.db.keys('autorestore/*'): | |||
|
87 | del self.db[k] | |||
|
88 | ||||
|
89 | elif opts.has_key('r'): | |||
|
90 | refresh_variables(ip) | |||
|
91 | ||||
|
92 | ||||
|
93 | # run without arguments -> list variables & values | |||
|
94 | elif not args: | |||
|
95 | vars = self.db.keys('autorestore/*') | |||
|
96 | vars.sort() | |||
|
97 | if vars: | |||
|
98 | size = max(map(len,vars)) | |||
|
99 | else: | |||
|
100 | size = 0 | |||
|
101 | ||||
|
102 | print 'Stored variables and their in-db values:' | |||
|
103 | fmt = '%-'+str(size)+'s -> %s' | |||
|
104 | get = self.db.get | |||
|
105 | for var in vars: | |||
|
106 | justkey = os.path.basename(var) | |||
|
107 | # print 30 first characters from every var | |||
|
108 | print fmt % (justkey,repr(get(var,'<unavailable>'))[:50]) | |||
|
109 | ||||
|
110 | # default action - store the variable | |||
|
111 | else: | |||
|
112 | # %store foo >file.txt or >>file.txt | |||
|
113 | if len(args) > 1 and args[1].startswith('>'): | |||
|
114 | fnam = os.path.expanduser(args[1].lstrip('>').lstrip()) | |||
|
115 | if args[1].startswith('>>'): | |||
|
116 | fil = open(fnam,'a') | |||
|
117 | else: | |||
|
118 | fil = open(fnam,'w') | |||
|
119 | obj = ip.ev(args[0]) | |||
|
120 | print "Writing '%s' (%s) to file '%s'." % (args[0], | |||
|
121 | obj.__class__.__name__, fnam) | |||
|
122 | ||||
|
123 | ||||
|
124 | if not isinstance (obj,basestring): | |||
|
125 | pprint(obj,fil) | |||
|
126 | else: | |||
|
127 | fil.write(obj) | |||
|
128 | if not obj.endswith('\n'): | |||
|
129 | fil.write('\n') | |||
|
130 | ||||
|
131 | fil.close() | |||
|
132 | return | |||
|
133 | ||||
|
134 | # %store foo | |||
|
135 | obj = ip.ev(args[0]) | |||
|
136 | if isinstance(inspect.getmodule(obj), FakeModule): | |||
|
137 | print textwrap.dedent("""\ | |||
|
138 | Warning:%s is %s | |||
|
139 | Proper storage of interactively declared classes (or instances | |||
|
140 | of those classes) is not possible! Only instances | |||
|
141 | of classes in real modules on file system can be %%store'd. | |||
|
142 | """ % (args[0], obj) ) | |||
|
143 | return | |||
|
144 | #pickled = pickle.dumps(obj) | |||
|
145 | self.db[ 'autorestore/' + args[0] ] = obj | |||
|
146 | print "Stored '%s' (%s)" % (args[0], obj.__class__.__name__) | |||
|
147 | ||||
|
148 | ip.expose_magic('store',magic_store) | |||
|
149 | No newline at end of file |
@@ -16,3 +16,4 b' import sys' | |||||
16 |
|
16 | |||
17 | import ext_rehashdir # %rehashdir magic |
|
17 | import ext_rehashdir # %rehashdir magic | |
18 | import ext_rescapture # var = !ls and var = %magic |
|
18 | import ext_rescapture # var = !ls and var = %magic | |
|
19 | import pspersistence # %store magic No newline at end of file |
@@ -1,7 +1,7 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """Magic functions for InteractiveShell. |
|
2 | """Magic functions for InteractiveShell. | |
3 |
|
3 | |||
4 |
$Id: Magic.py 1 |
|
4 | $Id: Magic.py 1107 2006-01-30 19:02:20Z vivainio $""" | |
5 |
|
5 | |||
6 | #***************************************************************************** |
|
6 | #***************************************************************************** | |
7 | # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> and |
|
7 | # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> and | |
@@ -2301,7 +2301,7 b' Defaulting color scheme to \'NoColor\'"""' | |||||
2301 | !command runs is immediately discarded after executing 'command'.""" |
|
2301 | !command runs is immediately discarded after executing 'command'.""" | |
2302 |
|
2302 | |||
2303 | parameter_s = parameter_s.strip() |
|
2303 | parameter_s = parameter_s.strip() | |
2304 | bkms = self.shell.persist.get("bookmarks",{}) |
|
2304 | #bkms = self.shell.persist.get("bookmarks",{}) | |
2305 |
|
2305 | |||
2306 | numcd = re.match(r'(-)(\d+)$',parameter_s) |
|
2306 | numcd = re.match(r'(-)(\d+)$',parameter_s) | |
2307 | # jump in directory history by number |
|
2307 | # jump in directory history by number | |
@@ -2326,19 +2326,20 b' Defaulting color scheme to \'NoColor\'"""' | |||||
2326 | except IndexError: |
|
2326 | except IndexError: | |
2327 | print 'No previous directory to change to.' |
|
2327 | print 'No previous directory to change to.' | |
2328 | return |
|
2328 | return | |
2329 | # jump to bookmark |
|
2329 | # jump to bookmark if needed | |
2330 | elif opts.has_key('b') or (bkms.has_key(ps) and not os.path.isdir(ps)): |
|
2330 | else: | |
2331 |
if |
|
2331 | if not os.path.isdir(ps) or opts.has_key('b'): | |
2332 | target = bkms[ps] |
|
2332 | bkms = self.db.get('bookmarks', {}) | |
2333 | print '(bookmark:%s) -> %s' % (ps,target) |
|
2333 | ||
2334 | ps = target |
|
2334 | if bkms.has_key(ps): | |
2335 | else: |
|
2335 | target = bkms[ps] | |
2336 | if bkms: |
|
2336 | print '(bookmark:%s) -> %s' % (ps,target) | |
2337 | error("Bookmark '%s' not found. " |
|
2337 | ps = target | |
2338 | "Use '%%bookmark -l' to see your bookmarks." % ps) |
|
|||
2339 | else: |
|
2338 | else: | |
2340 | print "Bookmarks not set - use %bookmark <bookmarkname>" |
|
2339 | if opts.has_key('b'): | |
2341 | return |
|
2340 | error("Bookmark '%s' not found. " | |
|
2341 | "Use '%%bookmark -l' to see your bookmarks." % ps) | |||
|
2342 | return | |||
2342 |
|
2343 | |||
2343 | # at this point ps should point to the target dir |
|
2344 | # at this point ps should point to the target dir | |
2344 | if ps: |
|
2345 | if ps: | |
@@ -2634,112 +2635,6 b' Defaulting color scheme to \'NoColor\'"""' | |||||
2634 |
|
2635 | |||
2635 | self.shell.jobs.new(parameter_s,self.shell.user_ns) |
|
2636 | self.shell.jobs.new(parameter_s,self.shell.user_ns) | |
2636 |
|
2637 | |||
2637 | def magic_store(self, parameter_s=''): |
|
|||
2638 | """Lightweight persistence for python variables. |
|
|||
2639 |
|
||||
2640 | Example: |
|
|||
2641 |
|
||||
2642 | ville@badger[~]|1> A = ['hello',10,'world']\\ |
|
|||
2643 | ville@badger[~]|2> %store A\\ |
|
|||
2644 | ville@badger[~]|3> Exit |
|
|||
2645 |
|
||||
2646 | (IPython session is closed and started again...) |
|
|||
2647 |
|
||||
2648 | ville@badger:~$ ipython -p pysh\\ |
|
|||
2649 | ville@badger[~]|1> print A |
|
|||
2650 |
|
||||
2651 | ['hello', 10, 'world'] |
|
|||
2652 |
|
||||
2653 | Usage: |
|
|||
2654 |
|
||||
2655 | %store - Show list of all variables and their current values\\ |
|
|||
2656 | %store <var> - Store the *current* value of the variable to disk\\ |
|
|||
2657 | %store -d <var> - Remove the variable and its value from storage\\ |
|
|||
2658 | %store -r - Remove all variables from storage\\ |
|
|||
2659 | %store foo >a.txt - Store value of foo to new file a.txt\\ |
|
|||
2660 | %store foo >>a.txt - Append value of foo to file a.txt\\ |
|
|||
2661 |
|
||||
2662 | It should be noted that if you change the value of a variable, you |
|
|||
2663 | need to %store it again if you want to persist the new value. |
|
|||
2664 |
|
||||
2665 | Note also that the variables will need to be pickleable; most basic |
|
|||
2666 | python types can be safely %stored. |
|
|||
2667 | """ |
|
|||
2668 |
|
||||
2669 | opts,argsl = self.parse_options(parameter_s,'dr',mode='string') |
|
|||
2670 | args = argsl.split(None,1) |
|
|||
2671 | ip = self.getapi() |
|
|||
2672 | # delete |
|
|||
2673 | if opts.has_key('d'): |
|
|||
2674 | try: |
|
|||
2675 | todel = args[0] |
|
|||
2676 | except IndexError: |
|
|||
2677 | error('You must provide the variable to forget') |
|
|||
2678 | else: |
|
|||
2679 | try: |
|
|||
2680 | del self.shell.persist['S:' + todel] |
|
|||
2681 | except: |
|
|||
2682 | error("Can't delete variable '%s'" % todel) |
|
|||
2683 | # reset |
|
|||
2684 | elif opts.has_key('r'): |
|
|||
2685 | for k in self.shell.persist.keys(): |
|
|||
2686 | if k.startswith('S:'): |
|
|||
2687 | del self.shell.persist[k] |
|
|||
2688 |
|
||||
2689 | # run without arguments -> list variables & values |
|
|||
2690 | elif not args: |
|
|||
2691 | vars = [v[2:] for v in self.shell.persist.keys() |
|
|||
2692 | if v.startswith('S:')] |
|
|||
2693 | vars.sort() |
|
|||
2694 | if vars: |
|
|||
2695 | size = max(map(len,vars)) |
|
|||
2696 | else: |
|
|||
2697 | size = 0 |
|
|||
2698 |
|
||||
2699 | print 'Stored variables and their in-memory values:' |
|
|||
2700 | fmt = '%-'+str(size)+'s -> %s' |
|
|||
2701 | get = self.shell.user_ns.get |
|
|||
2702 | for var in vars: |
|
|||
2703 | # print 30 first characters from every var |
|
|||
2704 | print fmt % (var,repr(get(var,'<unavailable>'))[:50]) |
|
|||
2705 |
|
||||
2706 | # default action - store the variable |
|
|||
2707 | else: |
|
|||
2708 | # %store foo >file.txt or >>file.txt |
|
|||
2709 | if len(args) > 1 and args[1].startswith('>'): |
|
|||
2710 | fnam = os.path.expanduser(args[1].lstrip('>').lstrip()) |
|
|||
2711 | if args[1].startswith('>>'): |
|
|||
2712 | fil = open(fnam,'a') |
|
|||
2713 | else: |
|
|||
2714 | fil = open(fnam,'w') |
|
|||
2715 | obj = ip.ev(args[0]) |
|
|||
2716 | print "Writing '%s' (%s) to file '%s'." % (args[0], |
|
|||
2717 | obj.__class__.__name__, fnam) |
|
|||
2718 |
|
||||
2719 |
|
||||
2720 | if not isinstance (obj,basestring): |
|
|||
2721 | pprint(obj,fil) |
|
|||
2722 | else: |
|
|||
2723 | fil.write(obj) |
|
|||
2724 | if not obj.endswith('\n'): |
|
|||
2725 | fil.write('\n') |
|
|||
2726 |
|
||||
2727 | fil.close() |
|
|||
2728 | return |
|
|||
2729 |
|
||||
2730 | # %store foo |
|
|||
2731 | obj = self.shell.user_ns[args[0] ] |
|
|||
2732 | if isinstance(inspect.getmodule(obj), FakeModule): |
|
|||
2733 | print textwrap.dedent("""\ |
|
|||
2734 | Warning:%s is %s |
|
|||
2735 | Proper storage of interactively declared classes (or instances |
|
|||
2736 | of those classes) is not possible! Only instances |
|
|||
2737 | of classes in real modules on file system can be %%store'd. |
|
|||
2738 | """ % (args[0], obj) ) |
|
|||
2739 | return |
|
|||
2740 | pickled = pickle.dumps(obj) |
|
|||
2741 | self.shell.persist[ 'S:' + args[0] ] = pickled |
|
|||
2742 | print "Stored '%s' (%s, %d bytes)" % (args[0], obj.__class__.__name__,len(pickled)) |
|
|||
2743 |
|
2638 | |||
2744 | def magic_bookmark(self, parameter_s=''): |
|
2639 | def magic_bookmark(self, parameter_s=''): | |
2745 | """Manage IPython's bookmark system. |
|
2640 | """Manage IPython's bookmark system. | |
@@ -2763,7 +2658,7 b' Defaulting color scheme to \'NoColor\'"""' | |||||
2763 | error('You can only give at most two arguments') |
|
2658 | error('You can only give at most two arguments') | |
2764 | return |
|
2659 | return | |
2765 |
|
2660 | |||
2766 |
bkms = self. |
|
2661 | bkms = self.db.get('bookmarks',{}) | |
2767 |
|
2662 | |||
2768 | if opts.has_key('d'): |
|
2663 | if opts.has_key('d'): | |
2769 | try: |
|
2664 | try: | |
@@ -2795,7 +2690,7 b' Defaulting color scheme to \'NoColor\'"""' | |||||
2795 | bkms[args[0]] = os.getcwd() |
|
2690 | bkms[args[0]] = os.getcwd() | |
2796 | elif len(args)==2: |
|
2691 | elif len(args)==2: | |
2797 | bkms[args[0]] = args[1] |
|
2692 | bkms[args[0]] = args[1] | |
2798 |
self. |
|
2693 | self.db['bookmarks'] = bkms | |
2799 |
|
2694 | |||
2800 | def magic_pycat(self, parameter_s=''): |
|
2695 | def magic_pycat(self, parameter_s=''): | |
2801 | """Show a syntax-highlighted file through a pager. |
|
2696 | """Show a syntax-highlighted file through a pager. |
@@ -32,7 +32,7 b" ip.set_hook('editor', calljed)" | |||||
32 | You can then enable the functionality by doing 'import myiphooks' |
|
32 | You can then enable the functionality by doing 'import myiphooks' | |
33 | somewhere in your configuration files or ipython command line. |
|
33 | somewhere in your configuration files or ipython command line. | |
34 |
|
34 | |||
35 |
$Id: hooks.py 1 |
|
35 | $Id: hooks.py 1107 2006-01-30 19:02:20Z vivainio $""" | |
36 |
|
36 | |||
37 | #***************************************************************************** |
|
37 | #***************************************************************************** | |
38 | # Copyright (C) 2005 Fernando Perez. <fperez@colorado.edu> |
|
38 | # Copyright (C) 2005 Fernando Perez. <fperez@colorado.edu> | |
@@ -54,7 +54,7 b' from pprint import pformat' | |||||
54 | # List here all the default hooks. For now it's just the editor functions |
|
54 | # List here all the default hooks. For now it's just the editor functions | |
55 | # but over time we'll move here all the public API for user-accessible things. |
|
55 | # but over time we'll move here all the public API for user-accessible things. | |
56 | __all__ = ['editor', 'fix_error_editor', 'result_display', |
|
56 | __all__ = ['editor', 'fix_error_editor', 'result_display', | |
57 | 'input_prefilter'] |
|
57 | 'input_prefilter', 'shutdown_hook', 'late_startup_hook'] | |
58 |
|
58 | |||
59 | def editor(self,filename, linenum=None): |
|
59 | def editor(self,filename, linenum=None): | |
60 | """Open the default editor at the given filename and linenumber. |
|
60 | """Open the default editor at the given filename and linenumber. | |
@@ -166,4 +166,19 b' def input_prefilter(self,line):' | |||||
166 |
|
166 | |||
167 | """ |
|
167 | """ | |
168 | #print "attempt to rewrite",line #dbg |
|
168 | #print "attempt to rewrite",line #dbg | |
169 | return line No newline at end of file |
|
169 | return line | |
|
170 | ||||
|
171 | def shutdown_hook(self): | |||
|
172 | """ default shutdown hook | |||
|
173 | ||||
|
174 | Typically, shotdown hooks should raise TryNext so all shutdown ops are done | |||
|
175 | """ | |||
|
176 | ||||
|
177 | #print "default shutdown hook ok" # dbg | |||
|
178 | return | |||
|
179 | ||||
|
180 | def late_startup_hook(self): | |||
|
181 | """ Executed after ipython has been constructed and configured | |||
|
182 | ||||
|
183 | """ | |||
|
184 | #print "default startup hook ok" # dbg No newline at end of file |
@@ -148,6 +148,13 b' class IPApi:' | |||||
148 | data that should persist through the ipython session. |
|
148 | data that should persist through the ipython session. | |
149 | """ |
|
149 | """ | |
150 | return self.IP.meta |
|
150 | return self.IP.meta | |
|
151 | ||||
|
152 | def getdb(self): | |||
|
153 | """ Return a handle to persistent dict-like database | |||
|
154 | ||||
|
155 | Return a PickleShareDB object. | |||
|
156 | """ | |||
|
157 | return self.IP.db | |||
151 |
|
158 | |||
152 |
|
159 | |||
153 | def launch_new_instance(user_ns = None): |
|
160 | def launch_new_instance(user_ns = None): |
@@ -6,7 +6,7 b' Requires Python 2.3 or newer.' | |||||
6 |
|
6 | |||
7 | This file contains all the classes and helper functions specific to IPython. |
|
7 | This file contains all the classes and helper functions specific to IPython. | |
8 |
|
8 | |||
9 |
$Id: iplib.py 110 |
|
9 | $Id: iplib.py 1107 2006-01-30 19:02:20Z vivainio $ | |
10 | """ |
|
10 | """ | |
11 |
|
11 | |||
12 | #***************************************************************************** |
|
12 | #***************************************************************************** | |
@@ -58,6 +58,7 b' import sys' | |||||
58 | import tempfile |
|
58 | import tempfile | |
59 | import traceback |
|
59 | import traceback | |
60 | import types |
|
60 | import types | |
|
61 | import pickleshare | |||
61 |
|
62 | |||
62 | from pprint import pprint, pformat |
|
63 | from pprint import pprint, pformat | |
63 |
|
64 | |||
@@ -191,6 +192,7 b' class InteractiveShell(object,Magic):' | |||||
191 | user_ns = None,user_global_ns=None,banner2='', |
|
192 | user_ns = None,user_global_ns=None,banner2='', | |
192 | custom_exceptions=((),None),embedded=False): |
|
193 | custom_exceptions=((),None),embedded=False): | |
193 |
|
194 | |||
|
195 | ||||
194 | # log system |
|
196 | # log system | |
195 | self.logger = Logger(self,logfname='ipython_log.py',logmode='rotate') |
|
197 | self.logger = Logger(self,logfname='ipython_log.py',logmode='rotate') | |
196 |
|
198 | |||
@@ -607,6 +609,7 b' class InteractiveShell(object,Magic):' | |||||
607 |
|
609 | |||
608 | rc = self.rc |
|
610 | rc = self.rc | |
609 |
|
611 | |||
|
612 | self.db = pickleshare.PickleShareDB(rc.ipythondir + "/db") | |||
610 | # Load readline proper |
|
613 | # Load readline proper | |
611 | if rc.readline: |
|
614 | if rc.readline: | |
612 | self.init_readline() |
|
615 | self.init_readline() | |
@@ -648,31 +651,8 b' class InteractiveShell(object,Magic):' | |||||
648 | # Load user aliases |
|
651 | # Load user aliases | |
649 | for alias in rc.alias: |
|
652 | for alias in rc.alias: | |
650 | self.magic_alias(alias) |
|
653 | self.magic_alias(alias) | |
651 |
|
654 | self.hooks.late_startup_hook() | ||
652 | # dynamic data that survives through sessions |
|
|||
653 | # XXX make the filename a config option? |
|
|||
654 | persist_base = 'persist' |
|
|||
655 | if rc.profile: |
|
|||
656 | persist_base += '_%s' % rc.profile |
|
|||
657 | self.persist_fname = os.path.join(rc.ipythondir,persist_base) |
|
|||
658 |
|
||||
659 | try: |
|
|||
660 | self.persist = pickle.load(file(self.persist_fname)) |
|
|||
661 | except: |
|
|||
662 | self.persist = {} |
|
|||
663 |
|
||||
664 |
|
655 | |||
665 | for (key, value) in [(k[2:],v) for (k,v) in self.persist.items() if k.startswith('S:')]: |
|
|||
666 | try: |
|
|||
667 | obj = pickle.loads(value) |
|
|||
668 | except: |
|
|||
669 |
|
||||
670 | print "Unable to restore variable '%s', ignoring (use %%store -d to forget!)" % key |
|
|||
671 | print "The error was:",sys.exc_info()[0] |
|
|||
672 | continue |
|
|||
673 |
|
||||
674 |
|
||||
675 | self.user_ns[key] = obj |
|
|||
676 |
|
656 | |||
677 | def add_builtins(self): |
|
657 | def add_builtins(self): | |
678 | """Store ipython references into the builtin namespace. |
|
658 | """Store ipython references into the builtin namespace. | |
@@ -1147,10 +1127,7 b' want to merge them back into the new files.""" % locals()' | |||||
1147 | pass |
|
1127 | pass | |
1148 |
|
1128 | |||
1149 | # save the "persistent data" catch-all dictionary |
|
1129 | # save the "persistent data" catch-all dictionary | |
1150 | try: |
|
1130 | self.hooks.shutdown_hook() | |
1151 | pickle.dump(self.persist, open(self.persist_fname,"w")) |
|
|||
1152 | except: |
|
|||
1153 | print "*** ERROR *** persistent data saving failed." |
|
|||
1154 |
|
1131 | |||
1155 | def savehist(self): |
|
1132 | def savehist(self): | |
1156 | """Save input history to a file (via readline library).""" |
|
1133 | """Save input history to a file (via readline library).""" |
@@ -1,3 +1,16 b'' | |||||
|
1 | 2006-01-30 Ville Vainio <vivainio@gmail.com> | |||
|
2 | ||||
|
3 | * pickleshare,pspersistence,ipapi,Magic: persistence overhaul. | |||
|
4 | Now %store and bookmarks work through PickleShare, meaning that | |||
|
5 | concurrent access is possible and all ipython sessions see the | |||
|
6 | same database situation all the time, instead of snapshot of | |||
|
7 | the situation when the session was started. Hence, %bookmark | |||
|
8 | results are immediately accessible from othes sessions. The database | |||
|
9 | is also available for use by user extensions. See: | |||
|
10 | http://www.python.org/pypi/pickleshare | |||
|
11 | ||||
|
12 | * hooks.py: Two new hooks, 'shutdown_hook' and 'late_startup_hook'. | |||
|
13 | ||||
1 | 2006-01-29 Fernando Perez <Fernando.Perez@colorado.edu> |
|
14 | 2006-01-29 Fernando Perez <Fernando.Perez@colorado.edu> | |
2 |
|
15 | |||
3 | * IPython/iplib.py (interact): Fix that we were not catching |
|
16 | * IPython/iplib.py (interact): Fix that we were not catching |
General Comments 0
You need to be logged in to leave comments.
Login now