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 | 17 | import ext_rehashdir # %rehashdir magic |
|
18 | 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 | 1 | # -*- coding: utf-8 -*- |
|
2 | 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 | 7 | # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> and |
@@ -2301,7 +2301,7 b' Defaulting color scheme to \'NoColor\'"""' | |||
|
2301 | 2301 | !command runs is immediately discarded after executing 'command'.""" |
|
2302 | 2302 | |
|
2303 | 2303 | parameter_s = parameter_s.strip() |
|
2304 | bkms = self.shell.persist.get("bookmarks",{}) | |
|
2304 | #bkms = self.shell.persist.get("bookmarks",{}) | |
|
2305 | 2305 | |
|
2306 | 2306 | numcd = re.match(r'(-)(\d+)$',parameter_s) |
|
2307 | 2307 | # jump in directory history by number |
@@ -2326,19 +2326,20 b' Defaulting color scheme to \'NoColor\'"""' | |||
|
2326 | 2326 | except IndexError: |
|
2327 | 2327 | print 'No previous directory to change to.' |
|
2328 | 2328 | return |
|
2329 | # jump to bookmark | |
|
2330 | elif opts.has_key('b') or (bkms.has_key(ps) and not os.path.isdir(ps)): | |
|
2331 |
if |
|
|
2332 | target = bkms[ps] | |
|
2333 | print '(bookmark:%s) -> %s' % (ps,target) | |
|
2334 | ps = target | |
|
2335 | else: | |
|
2336 | if bkms: | |
|
2337 | error("Bookmark '%s' not found. " | |
|
2338 | "Use '%%bookmark -l' to see your bookmarks." % ps) | |
|
2329 | # jump to bookmark if needed | |
|
2330 | else: | |
|
2331 | if not os.path.isdir(ps) or opts.has_key('b'): | |
|
2332 | bkms = self.db.get('bookmarks', {}) | |
|
2333 | ||
|
2334 | if bkms.has_key(ps): | |
|
2335 | target = bkms[ps] | |
|
2336 | print '(bookmark:%s) -> %s' % (ps,target) | |
|
2337 | ps = target | |
|
2339 | 2338 | else: |
|
2340 | print "Bookmarks not set - use %bookmark <bookmarkname>" | |
|
2341 | return | |
|
2339 | if opts.has_key('b'): | |
|
2340 | error("Bookmark '%s' not found. " | |
|
2341 | "Use '%%bookmark -l' to see your bookmarks." % ps) | |
|
2342 | return | |
|
2342 | 2343 | |
|
2343 | 2344 | # at this point ps should point to the target dir |
|
2344 | 2345 | if ps: |
@@ -2634,112 +2635,6 b' Defaulting color scheme to \'NoColor\'"""' | |||
|
2634 | 2635 | |
|
2635 | 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 | 2639 | def magic_bookmark(self, parameter_s=''): |
|
2745 | 2640 | """Manage IPython's bookmark system. |
@@ -2763,7 +2658,7 b' Defaulting color scheme to \'NoColor\'"""' | |||
|
2763 | 2658 | error('You can only give at most two arguments') |
|
2764 | 2659 | return |
|
2765 | 2660 | |
|
2766 |
bkms = self. |
|
|
2661 | bkms = self.db.get('bookmarks',{}) | |
|
2767 | 2662 | |
|
2768 | 2663 | if opts.has_key('d'): |
|
2769 | 2664 | try: |
@@ -2795,7 +2690,7 b' Defaulting color scheme to \'NoColor\'"""' | |||
|
2795 | 2690 | bkms[args[0]] = os.getcwd() |
|
2796 | 2691 | elif len(args)==2: |
|
2797 | 2692 | bkms[args[0]] = args[1] |
|
2798 |
self. |
|
|
2693 | self.db['bookmarks'] = bkms | |
|
2799 | 2694 | |
|
2800 | 2695 | def magic_pycat(self, parameter_s=''): |
|
2801 | 2696 | """Show a syntax-highlighted file through a pager. |
@@ -32,7 +32,7 b" ip.set_hook('editor', calljed)" | |||
|
32 | 32 | You can then enable the functionality by doing 'import myiphooks' |
|
33 | 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 | 38 | # Copyright (C) 2005 Fernando Perez. <fperez@colorado.edu> |
@@ -54,7 +54,7 b' from pprint import pformat' | |||
|
54 | 54 | # List here all the default hooks. For now it's just the editor functions |
|
55 | 55 | # but over time we'll move here all the public API for user-accessible things. |
|
56 | 56 | __all__ = ['editor', 'fix_error_editor', 'result_display', |
|
57 | 'input_prefilter'] | |
|
57 | 'input_prefilter', 'shutdown_hook', 'late_startup_hook'] | |
|
58 | 58 | |
|
59 | 59 | def editor(self,filename, linenum=None): |
|
60 | 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 | 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 | 148 | data that should persist through the ipython session. |
|
149 | 149 | """ |
|
150 | 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 | 160 | def launch_new_instance(user_ns = None): |
@@ -6,7 +6,7 b' Requires Python 2.3 or newer.' | |||
|
6 | 6 | |
|
7 | 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 | 58 | import tempfile |
|
59 | 59 | import traceback |
|
60 | 60 | import types |
|
61 | import pickleshare | |
|
61 | 62 | |
|
62 | 63 | from pprint import pprint, pformat |
|
63 | 64 | |
@@ -191,6 +192,7 b' class InteractiveShell(object,Magic):' | |||
|
191 | 192 | user_ns = None,user_global_ns=None,banner2='', |
|
192 | 193 | custom_exceptions=((),None),embedded=False): |
|
193 | 194 | |
|
195 | ||
|
194 | 196 | # log system |
|
195 | 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 | 610 | rc = self.rc |
|
609 | 611 | |
|
612 | self.db = pickleshare.PickleShareDB(rc.ipythondir + "/db") | |
|
610 | 613 | # Load readline proper |
|
611 | 614 | if rc.readline: |
|
612 | 615 | self.init_readline() |
@@ -648,31 +651,8 b' class InteractiveShell(object,Magic):' | |||
|
648 | 651 | # Load user aliases |
|
649 | 652 | for alias in rc.alias: |
|
650 | 653 | self.magic_alias(alias) |
|
651 | ||
|
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 | ||
|
654 | self.hooks.late_startup_hook() | |
|
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 | 657 | def add_builtins(self): |
|
678 | 658 | """Store ipython references into the builtin namespace. |
@@ -1147,10 +1127,7 b' want to merge them back into the new files.""" % locals()' | |||
|
1147 | 1127 | pass |
|
1148 | 1128 | |
|
1149 | 1129 | # save the "persistent data" catch-all dictionary |
|
1150 | try: | |
|
1151 | pickle.dump(self.persist, open(self.persist_fname,"w")) | |
|
1152 | except: | |
|
1153 | print "*** ERROR *** persistent data saving failed." | |
|
1130 | self.hooks.shutdown_hook() | |
|
1154 | 1131 | |
|
1155 | 1132 | def savehist(self): |
|
1156 | 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 | 14 | 2006-01-29 Fernando Perez <Fernando.Perez@colorado.edu> |
|
2 | 15 | |
|
3 | 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