Show More
@@ -13,13 +13,8 b'' | |||||
13 | from __future__ import print_function |
|
13 | from __future__ import print_function | |
14 |
|
14 | |||
15 | # Stdlib imports |
|
15 | # Stdlib imports | |
16 | import atexit |
|
|||
17 | import fnmatch |
|
|||
18 | import json |
|
|||
19 | import os |
|
16 | import os | |
20 |
import s |
|
17 | import sqlite3 | |
21 | import threading |
|
|||
22 | import time |
|
|||
23 |
|
18 | |||
24 | # Our own packages |
|
19 | # Our own packages | |
25 | import IPython.utils.io |
|
20 | import IPython.utils.io | |
@@ -50,14 +45,10 b' class HistoryManager(object):' | |||||
50 | output_hist = None |
|
45 | output_hist = None | |
51 | # String with path to the history file |
|
46 | # String with path to the history file | |
52 | hist_file = None |
|
47 | hist_file = None | |
53 | # PickleShareDB instance holding the raw data for the shadow history |
|
48 | # The SQLite database | |
54 |
|
|
49 | db = None | |
55 | # ShadowHist instance with the actual shadow history |
|
50 | # The number of the current session in the history database | |
56 | shadow_hist = None |
|
51 | session_number = None | |
57 |
|
||||
58 | # Offset so the first line of the current session is #1. Can be |
|
|||
59 | # updated after loading history from file. |
|
|||
60 | session_offset = -1 |
|
|||
61 |
|
52 | |||
62 | # Private interface |
|
53 | # Private interface | |
63 | # Variables used to store the three last inputs from the user. On each new |
|
54 | # Variables used to store the three last inputs from the user. On each new | |
@@ -83,12 +74,13 b' class HistoryManager(object):' | |||||
83 | # We need a pointer back to the shell for various tasks. |
|
74 | # We need a pointer back to the shell for various tasks. | |
84 | self.shell = shell |
|
75 | self.shell = shell | |
85 |
|
76 | |||
86 | # List of input with multi-line handling. |
|
77 | # List of input with multi-line handling. One blank entry so indexing | |
87 | self.input_hist_parsed = [] |
|
78 | # starts from 1. | |
|
79 | self.input_hist_parsed = [""] | |||
88 | # This one will hold the 'raw' input history, without any |
|
80 | # This one will hold the 'raw' input history, without any | |
89 | # pre-processing. This will allow users to retrieve the input just as |
|
81 | # pre-processing. This will allow users to retrieve the input just as | |
90 | # it was exactly typed in by the user, with %hist -r. |
|
82 | # it was exactly typed in by the user, with %hist -r. | |
91 | self.input_hist_raw = [] |
|
83 | self.input_hist_raw = [""] | |
92 |
|
84 | |||
93 | # list of visited directories |
|
85 | # list of visited directories | |
94 | try: |
|
86 | try: | |
@@ -104,99 +96,100 b' class HistoryManager(object):' | |||||
104 | histfname = 'history-%s' % shell.profile |
|
96 | histfname = 'history-%s' % shell.profile | |
105 | else: |
|
97 | else: | |
106 | histfname = 'history' |
|
98 | histfname = 'history' | |
107 |
self.hist_file = os.path.join(shell.ipython_dir, histfname + '. |
|
99 | self.hist_file = os.path.join(shell.ipython_dir, histfname + '.sqlite') | |
108 |
|
||||
109 | # Objects related to shadow history management |
|
|||
110 | self._init_shadow_hist() |
|
|||
111 |
|
100 | |||
112 | self._i00, self._i, self._ii, self._iii = '','','','' |
|
101 | self._i00, self._i, self._ii, self._iii = '','','','' | |
113 |
|
102 | |||
114 | self._exit_commands = set(['Quit', 'quit', 'Exit', 'exit', '%Quit', |
|
103 | self._exit_commands = set(['Quit', 'quit', 'Exit', 'exit', '%Quit', | |
115 | '%quit', '%Exit', '%exit']) |
|
104 | '%quit', '%Exit', '%exit']) | |
116 |
|
105 | |||
117 | # Object is fully initialized, we can now call methods on it. |
|
106 | self.init_db() | |
|
107 | ||||
|
108 | def init_db(self): | |||
|
109 | self.db = sqlite3.connect(self.hist_file) | |||
|
110 | self.db.execute("""CREATE TABLE IF NOT EXISTS history (session integer, | |||
|
111 | line integer, source text, source_raw text, | |||
|
112 | PRIMARY KEY (session, line))""") | |||
|
113 | cur = self.db.execute("""SELECT name FROM sqlite_master WHERE | |||
|
114 | type='table' AND name='singletons'""") | |||
|
115 | if not cur.fetchone(): | |||
|
116 | self.db.execute("""CREATE TABLE singletons | |||
|
117 | (name text PRIMARY KEY, value)""") | |||
|
118 | self.db.execute("""INSERT INTO singletons VALUES | |||
|
119 | ('session_number', 1)""") | |||
|
120 | self.db.commit() | |||
|
121 | cur = self.db.execute("""SELECT value FROM singletons WHERE | |||
|
122 | name='session_number'""") | |||
|
123 | self.session_number = cur.fetchone()[0] | |||
|
124 | ||||
|
125 | #Increment by one for next session. | |||
|
126 | self.db.execute("""UPDATE singletons SET value=? WHERE | |||
|
127 | name='session_number'""", (self.session_number+1,)) | |||
|
128 | self.db.commit() | |||
|
129 | ||||
|
130 | def get_db_history(self, session, start=1, stop=None, raw=True): | |||
|
131 | """Retrieve input history from the database by session. | |||
118 |
|
|
132 | ||
119 | if load_history: |
|
133 | Parameters | |
120 | self.reload_history() |
|
134 | ---------- | |
121 | self.session_offset = len(self.input_hist_raw) -1 |
|
135 | session : int | |
|
136 | Session number to retrieve. If negative, counts back from current | |||
|
137 | session (so -1 is previous session). | |||
|
138 | start : int | |||
|
139 | First line to retrieve. | |||
|
140 | stop : int | |||
|
141 | Last line to retrieve. If None, retrieve to the end of the session. | |||
|
142 | raw : bool | |||
|
143 | If True, return raw input | |||
122 |
|
144 | |||
123 | # Create and start the autosaver. |
|
145 | Returns | |
124 | self.autosave_flag = threading.Event() |
|
146 | ------- | |
125 | self.autosave_timer = HistorySaveThread(self.autosave_flag, 60) |
|
147 | An iterator over the desired lines. | |
126 | self.autosave_timer.start() |
|
148 | """ | |
127 | # Register the autosave handler to be triggered as a post execute |
|
149 | toget = 'source_raw' if raw else 'source' | |
128 | # callback. |
|
150 | if session < 0: | |
129 | self.shell.register_post_execute(self.autosave_if_due) |
|
151 | session += self.session_number | |
|
152 | ||||
|
153 | if stop: | |||
|
154 | cur = self.db.execute("SELECT " + toget + """ FROM history WHERE | |||
|
155 | session==? AND line BETWEEN ? and ?""", | |||
|
156 | (session, start, stop)) | |||
|
157 | else: | |||
|
158 | cur = self.db.execute("SELECT " + toget + """ FROM history WHERE | |||
|
159 | session==? AND line>=?""", (session, start)) | |||
|
160 | return (x[0] for x in cur) | |||
130 |
|
161 | |||
|
162 | def tail_db_history(self, n=10, raw=True): | |||
|
163 | """Get the last n lines from the history database.""" | |||
|
164 | toget = 'source_raw' if raw else 'source' | |||
|
165 | cur = self.db.execute("SELECT " + toget + """ FROM history ORDER BY | |||
|
166 | session DESC, line DESC LIMIT ?""", (n,)) | |||
|
167 | return (x[0] for x in reversed(cur.fetchall())) | |||
131 |
|
168 | |||
132 | def _init_shadow_hist(self): |
|
169 | def globsearch_db(self, pattern="*"): | |
133 | try: |
|
170 | """Search the database using unix glob-style matching (wildcards * and | |
134 | self.shadow_db = PickleShareDB(os.path.join( |
|
171 | ?, escape using \). | |
135 | self.shell.ipython_dir, 'db')) |
|
|||
136 | except UnicodeDecodeError: |
|
|||
137 | print("Your ipython_dir can't be decoded to unicode!") |
|
|||
138 | print("Please set HOME environment variable to something that") |
|
|||
139 | print(r"only has ASCII characters, e.g. c:\home") |
|
|||
140 | print("Now it is", self.ipython_dir) |
|
|||
141 | sys.exit() |
|
|||
142 | self.shadow_hist = ShadowHist(self.shadow_db, self.shell) |
|
|||
143 |
|
||||
144 | def populate_readline_history(self): |
|
|||
145 | """Populate the readline history from the raw history. |
|
|||
146 |
|
||||
147 | We only store one copy of the raw history, which is persisted to a json |
|
|||
148 | file on disk. The readline history is repopulated from the contents of |
|
|||
149 | this file.""" |
|
|||
150 |
|
172 | |||
151 |
|
|
173 | Returns | |
152 | self.shell.readline.clear_history() |
|
174 | ------- | |
153 | except AttributeError: |
|
175 | An iterator over tuples: (session, line_number, command) | |
154 | pass |
|
176 | """ | |
155 | else: |
|
177 | return self.db.execute("""SELECT session, line, source_raw FROM history | |
156 | for h in self.input_hist_raw: |
|
178 | WHERE source_raw GLOB ?""", (pattern,)) | |
157 | if not h.isspace(): |
|
|||
158 | for line in h.splitlines(): |
|
|||
159 | self.shell.readline.add_history(line) |
|
|||
160 |
|
||||
161 | def save_history(self): |
|
|||
162 | """Save input history to a file (via readline library).""" |
|
|||
163 | hist = dict(raw=self.input_hist_raw, #[-self.shell.history_length:], |
|
|||
164 | parsed=self.input_hist_parsed) #[-self.shell.history_length:]) |
|
|||
165 | with open(self.hist_file,'wt') as hfile: |
|
|||
166 | json.dump(hist, hfile, |
|
|||
167 | sort_keys=True, indent=4) |
|
|||
168 |
|
||||
169 | def autosave_if_due(self): |
|
|||
170 | """Check if the autosave event is set; if so, save history. We do it |
|
|||
171 | this way so that the save takes place in the main thread.""" |
|
|||
172 | if self.autosave_flag.is_set(): |
|
|||
173 | self.save_history() |
|
|||
174 | self.autosave_flag.clear() |
|
|||
175 |
|
||||
176 | def reload_history(self): |
|
|||
177 | """Reload the input history from disk file.""" |
|
|||
178 |
|
||||
179 | with open(self.hist_file,'rt') as hfile: |
|
|||
180 | try: |
|
|||
181 | hist = json.load(hfile) |
|
|||
182 | except ValueError: # Ignore it if JSON is corrupt. |
|
|||
183 | return |
|
|||
184 | self.input_hist_parsed = hist['parsed'] |
|
|||
185 | self.input_hist_raw = hist['raw'] |
|
|||
186 | if self.shell.has_readline: |
|
|||
187 | self.populate_readline_history() |
|
|||
188 |
|
179 | |||
189 |
def get_history(self, |
|
180 | def get_history(self, start=1, stop=None, raw=False, output=True): | |
190 | """Get the history list. |
|
181 | """Get the history list. | |
191 |
|
182 | |||
192 | Get the input and output history. |
|
183 | Get the input and output history. | |
193 |
|
184 | |||
194 | Parameters |
|
185 | Parameters | |
195 | ---------- |
|
186 | ---------- | |
196 | index : n or (n1, n2) or None |
|
187 | start : int | |
197 | If n, then the last n entries. If a tuple, then all in |
|
188 | From (prompt number in the current session). Negative numbers count | |
198 | range(n1, n2). If None, then all entries. Raises IndexError if |
|
189 | back from the end. | |
199 | the format of index is incorrect. |
|
190 | stop : int | |
|
191 | To (prompt number in the current session, exclusive). Negative | |||
|
192 | numbers count back from the end, and None goes to the end. | |||
200 | raw : bool |
|
193 | raw : bool | |
201 | If True, return the raw input. |
|
194 | If True, return the raw input. | |
202 | output : bool |
|
195 | output : bool | |
@@ -218,28 +211,20 b' class HistoryManager(object):' | |||||
218 | if output: |
|
211 | if output: | |
219 | output_hist = self.output_hist |
|
212 | output_hist = self.output_hist | |
220 |
|
213 | |||
221 | if this_session: |
|
|||
222 | offset = self.session_offset |
|
|||
223 | else: |
|
|||
224 | offset = -1 |
|
|||
225 |
|
||||
226 | n = len(input_hist) |
|
214 | n = len(input_hist) | |
227 |
if |
|
215 | if start < 0: | |
228 |
start= |
|
216 | start += n | |
229 | elif isinstance(index, int): |
|
217 | if not stop: | |
230 |
st |
|
218 | stop = n | |
231 |
elif |
|
219 | elif stop < 0: | |
232 |
st |
|
220 | stop += n | |
233 | stop = index[1] + offset |
|
221 | ||
234 | else: |
|
|||
235 | raise IndexError('Not a valid index for the input history: %r' |
|
|||
236 | % index) |
|
|||
237 | hist = {} |
|
222 | hist = {} | |
238 | for i in range(start, stop): |
|
223 | for i in range(start, stop): | |
239 | if output: |
|
224 | if output: | |
240 |
hist[i |
|
225 | hist[i] = (input_hist[i], output_hist.get(i)) | |
241 | else: |
|
226 | else: | |
242 |
hist[i |
|
227 | hist[i] = input_hist[i] | |
243 | return hist |
|
228 | return hist | |
244 |
|
229 | |||
245 | def store_inputs(self, source, source_raw=None): |
|
230 | def store_inputs(self, source, source_raw=None): | |
@@ -264,7 +249,10 b' class HistoryManager(object):' | |||||
264 |
|
249 | |||
265 | self.input_hist_parsed.append(source.rstrip()) |
|
250 | self.input_hist_parsed.append(source.rstrip()) | |
266 | self.input_hist_raw.append(source_raw.rstrip()) |
|
251 | self.input_hist_raw.append(source_raw.rstrip()) | |
267 | self.shadow_hist.add(source) |
|
252 | with self.db: | |
|
253 | self.db.execute("INSERT INTO history VALUES (?, ?, ?, ?)", | |||
|
254 | (self.session_number, self.shell.execution_count, | |||
|
255 | source, source_raw)) | |||
268 |
|
256 | |||
269 | # update the auto _i variables |
|
257 | # update the auto _i variables | |
270 | self._iii = self._ii |
|
258 | self._iii = self._ii | |
@@ -282,8 +270,12 b' class HistoryManager(object):' | |||||
282 |
|
270 | |||
283 | def sync_inputs(self): |
|
271 | def sync_inputs(self): | |
284 | """Ensure raw and translated histories have same length.""" |
|
272 | """Ensure raw and translated histories have same length.""" | |
285 |
|
|
273 | lr = len(self.input_hist_raw) | |
286 |
|
|
274 | lp = len(self.input_hist_parsed) | |
|
275 | if lp < lr: | |||
|
276 | self.input_hist_raw[:lr-lp] = [] | |||
|
277 | elif lr < lp: | |||
|
278 | self.input_hist_parsed[:lp-lr] = [] | |||
287 |
|
279 | |||
288 | def reset(self): |
|
280 | def reset(self): | |
289 | """Clear all histories managed by this object.""" |
|
281 | """Clear all histories managed by this object.""" | |
@@ -292,41 +284,6 b' class HistoryManager(object):' | |||||
292 | self.output_hist.clear() |
|
284 | self.output_hist.clear() | |
293 | # The directory history can't be completely empty |
|
285 | # The directory history can't be completely empty | |
294 | self.dir_hist[:] = [os.getcwd()] |
|
286 | self.dir_hist[:] = [os.getcwd()] | |
295 | # Reset session offset to -1, so next command counts as #1 |
|
|||
296 | self.session_offset = -1 |
|
|||
297 |
|
||||
298 | class HistorySaveThread(threading.Thread): |
|
|||
299 | """This thread makes IPython save history periodically. |
|
|||
300 |
|
||||
301 | Without this class, IPython would only save the history on a clean exit. |
|
|||
302 | This saves the history periodically (the current default is once per |
|
|||
303 | minute), so that it is not lost in the event of a crash. |
|
|||
304 |
|
||||
305 | The implementation sets an event to indicate that history should be saved. |
|
|||
306 | The actual save is carried out after executing a user command, to avoid |
|
|||
307 | thread issues. |
|
|||
308 | """ |
|
|||
309 | daemon = True |
|
|||
310 |
|
||||
311 | def __init__(self, autosave_flag, time_interval=60): |
|
|||
312 | threading.Thread.__init__(self) |
|
|||
313 | self.time_interval = time_interval |
|
|||
314 | self.autosave_flag = autosave_flag |
|
|||
315 | self.exit_now = threading.Event() |
|
|||
316 | # Ensure the thread is stopped tidily when exiting normally |
|
|||
317 | atexit.register(self.stop) |
|
|||
318 |
|
||||
319 | def run(self): |
|
|||
320 | while True: |
|
|||
321 | self.exit_now.wait(self.time_interval) |
|
|||
322 | if self.exit_now.is_set(): |
|
|||
323 | break |
|
|||
324 | self.autosave_flag.set() |
|
|||
325 |
|
||||
326 | def stop(self): |
|
|||
327 | """Safely and quickly stop the autosave timer thread.""" |
|
|||
328 | self.exit_now.set() |
|
|||
329 | self.join() |
|
|||
330 |
|
287 | |||
331 | @testdec.skip_doctest |
|
288 | @testdec.skip_doctest | |
332 | def magic_history(self, parameter_s = ''): |
|
289 | def magic_history(self, parameter_s = ''): | |
@@ -364,9 +321,8 b" def magic_history(self, parameter_s = ''):" | |||||
364 | 'get_ipython().magic("%cd /")' instead of '%cd /'. |
|
321 | 'get_ipython().magic("%cd /")' instead of '%cd /'. | |
365 |
|
322 | |||
366 | -g: treat the arg as a pattern to grep for in (full) history. |
|
323 | -g: treat the arg as a pattern to grep for in (full) history. | |
367 |
This includes the |
|
324 | This includes the saved history (almost all commands ever written). | |
368 |
Use '%hist -g' to show full s |
|
325 | Use '%hist -g' to show full saved history (may be very long). | |
369 | In shadow history, every index nuwber starts with 0. |
|
|||
370 |
|
326 | |||
371 | -f FILENAME: instead of printing the output to the screen, redirect it to |
|
327 | -f FILENAME: instead of printing the output to the screen, redirect it to | |
372 | the given file. The file is always overwritten, though IPython asks for |
|
328 | the given file. The file is always overwritten, though IPython asks for | |
@@ -415,39 +371,37 b" def magic_history(self, parameter_s = ''):" | |||||
415 | default_length = 40 |
|
371 | default_length = 40 | |
416 | pattern = None |
|
372 | pattern = None | |
417 | if 'g' in opts: |
|
373 | if 'g' in opts: | |
418 |
|
|
374 | start = 1; stop = None | |
419 | parts = parameter_s.split(None, 1) |
|
375 | parts = parameter_s.split(None, 1) | |
420 | if len(parts) == 1: |
|
376 | if len(parts) == 1: | |
421 | parts += '*' |
|
377 | parts += '*' | |
422 | head, pattern = parts |
|
378 | head, pattern = parts | |
423 | pattern = "*" + pattern + "*" |
|
379 | pattern = "*" + pattern + "*" | |
424 | elif len(args) == 0: |
|
380 | elif len(args) == 0: | |
425 |
|
|
381 | start = 1; stop = None | |
426 | elif len(args) == 1: |
|
382 | elif len(args) == 1: | |
427 |
|
|
383 | start = -int(args[0]); stop=None | |
428 | elif len(args) == 2: |
|
384 | elif len(args) == 2: | |
429 | index = map(int, args) |
|
385 | start = int(args[0]); stop = int(args[1]) | |
430 | else: |
|
386 | else: | |
431 | warn('%hist takes 0, 1 or 2 arguments separated by spaces.') |
|
387 | warn('%hist takes 0, 1 or 2 arguments separated by spaces.') | |
432 | print(self.magic_hist.__doc__, file=IPython.utils.io.Term.cout) |
|
388 | print(self.magic_hist.__doc__, file=IPython.utils.io.Term.cout) | |
433 | return |
|
389 | return | |
434 |
|
390 | |||
435 |
hist = history_manager.get_history( |
|
391 | hist = history_manager.get_history(start, stop, raw, print_outputs) | |
436 |
|
392 | |||
437 | width = len(str(max(hist.iterkeys()))) |
|
393 | width = len(str(max(hist.iterkeys()))) | |
438 | line_sep = ['','\n'] |
|
394 | line_sep = ['','\n'] | |
439 |
|
395 | |||
440 | found = False |
|
396 | found = False | |
441 | if pattern is not None: |
|
397 | if pattern is not None: | |
442 | sh = history_manager.shadow_hist.all() |
|
398 | for session, line, s in history_manager.globsearch_db(pattern): | |
443 | for idx, s in sh: |
|
399 | print("%d#%d: %s" %(session, line, s.expandtabs(4)), file=outfile) | |
444 | if fnmatch.fnmatch(s, pattern): |
|
|||
445 | print("0%d: %s" %(idx, s.expandtabs(4)), file=outfile) |
|
|||
446 |
|
|
400 | found = True | |
447 |
|
401 | |||
448 | if found: |
|
402 | if found: | |
449 | print("===", file=outfile) |
|
403 | print("===", file=outfile) | |
450 |
print("shadow history ends, fetch by %rep |
|
404 | print("shadow history ends, fetch by %rep session#line", | |
451 | file=outfile) |
|
405 | file=outfile) | |
452 | print("=== start of normal history ===", file=outfile) |
|
406 | print("=== start of normal history ===", file=outfile) | |
453 |
|
407 | |||
@@ -547,50 +501,6 b' def rep_f(self, arg):' | |||||
547 | print("Not found in recent history:", args) |
|
501 | print("Not found in recent history:", args) | |
548 |
|
502 | |||
549 |
|
503 | |||
550 | _sentinel = object() |
|
|||
551 |
|
||||
552 | class ShadowHist(object): |
|
|||
553 | def __init__(self, db, shell): |
|
|||
554 | # cmd => idx mapping |
|
|||
555 | self.curidx = 0 |
|
|||
556 | self.db = db |
|
|||
557 | self.disabled = False |
|
|||
558 | self.shell = shell |
|
|||
559 |
|
||||
560 | def inc_idx(self): |
|
|||
561 | idx = self.db.get('shadowhist_idx', 1) |
|
|||
562 | self.db['shadowhist_idx'] = idx + 1 |
|
|||
563 | return idx |
|
|||
564 |
|
||||
565 | def add(self, ent): |
|
|||
566 | if self.disabled: |
|
|||
567 | return |
|
|||
568 | try: |
|
|||
569 | old = self.db.hget('shadowhist', ent, _sentinel) |
|
|||
570 | if old is not _sentinel: |
|
|||
571 | return |
|
|||
572 | newidx = self.inc_idx() |
|
|||
573 | #print("new", newidx) # dbg |
|
|||
574 | self.db.hset('shadowhist',ent, newidx) |
|
|||
575 | except: |
|
|||
576 | self.shell.showtraceback() |
|
|||
577 | print("WARNING: disabling shadow history") |
|
|||
578 | self.disabled = True |
|
|||
579 |
|
||||
580 | def all(self): |
|
|||
581 | d = self.db.hdict('shadowhist') |
|
|||
582 | items = [(i,s) for (s,i) in d.iteritems()] |
|
|||
583 | items.sort() |
|
|||
584 | return items |
|
|||
585 |
|
||||
586 | def get(self, idx): |
|
|||
587 | all = self.all() |
|
|||
588 |
|
||||
589 | for k, v in all: |
|
|||
590 | if k == idx: |
|
|||
591 | return v |
|
|||
592 |
|
||||
593 |
|
||||
594 | def init_ipython(ip): |
|
504 | def init_ipython(ip): | |
595 | ip.define_magic("rep",rep_f) |
|
505 | ip.define_magic("rep",rep_f) | |
596 | ip.define_magic("hist",magic_hist) |
|
506 | ip.define_magic("hist",magic_hist) |
@@ -56,11 +56,11 b' from IPython.core.prefilter import PrefilterManager, ESC_MAGIC' | |||||
56 | from IPython.external.Itpl import ItplNS |
|
56 | from IPython.external.Itpl import ItplNS | |
57 | from IPython.utils import PyColorize |
|
57 | from IPython.utils import PyColorize | |
58 | from IPython.utils import io |
|
58 | from IPython.utils import io | |
59 | from IPython.utils import pickleshare |
|
|||
60 | from IPython.utils.doctestreload import doctest_reload |
|
59 | from IPython.utils.doctestreload import doctest_reload | |
61 | from IPython.utils.io import ask_yes_no, rprint |
|
60 | from IPython.utils.io import ask_yes_no, rprint | |
62 | from IPython.utils.ipstruct import Struct |
|
61 | from IPython.utils.ipstruct import Struct | |
63 | from IPython.utils.path import get_home_dir, get_ipython_dir, HomeDirError |
|
62 | from IPython.utils.path import get_home_dir, get_ipython_dir, HomeDirError | |
|
63 | from IPython.utils.pickleshare import PickleShareDB | |||
64 | from IPython.utils.process import system, getoutput |
|
64 | from IPython.utils.process import system, getoutput | |
65 | from IPython.utils.strdispatch import StrDispatch |
|
65 | from IPython.utils.strdispatch import StrDispatch | |
66 | from IPython.utils.syspathcontext import prepended_to_syspath |
|
66 | from IPython.utils.syspathcontext import prepended_to_syspath | |
@@ -251,6 +251,11 b' class InteractiveShell(Configurable, Magic):' | |||||
251 | self.save_sys_module_state() |
|
251 | self.save_sys_module_state() | |
252 | self.init_sys_modules() |
|
252 | self.init_sys_modules() | |
253 |
|
253 | |||
|
254 | # While we're trying to have each part of the code directly access what | |||
|
255 | # it needs without keeping redundant references to objects, we have too | |||
|
256 | # much legacy code that expects ip.db to exist. | |||
|
257 | self.db = PickleShareDB(os.path.join(self.ipython_dir, 'db')) | |||
|
258 | ||||
254 | self.init_history() |
|
259 | self.init_history() | |
255 | self.init_encoding() |
|
260 | self.init_encoding() | |
256 | self.init_prefilter() |
|
261 | self.init_prefilter() | |
@@ -300,14 +305,6 b' class InteractiveShell(Configurable, Magic):' | |||||
300 | self.hooks.late_startup_hook() |
|
305 | self.hooks.late_startup_hook() | |
301 | atexit.register(self.atexit_operations) |
|
306 | atexit.register(self.atexit_operations) | |
302 |
|
307 | |||
303 | # While we're trying to have each part of the code directly access what it |
|
|||
304 | # needs without keeping redundant references to objects, we have too much |
|
|||
305 | # legacy code that expects ip.db to exist, so let's make it a property that |
|
|||
306 | # retrieves the underlying object from our new history manager. |
|
|||
307 | @property |
|
|||
308 | def db(self): |
|
|||
309 | return self.history_manager.shadow_db |
|
|||
310 |
|
||||
311 | @classmethod |
|
308 | @classmethod | |
312 | def instance(cls, *args, **kwargs): |
|
309 | def instance(cls, *args, **kwargs): | |
313 | """Returns a global InteractiveShell instance.""" |
|
310 | """Returns a global InteractiveShell instance.""" | |
@@ -1248,15 +1245,7 b' class InteractiveShell(Configurable, Magic):' | |||||
1248 |
|
1245 | |||
1249 | def init_history(self): |
|
1246 | def init_history(self): | |
1250 | """Sets up the command history, and starts regular autosaves.""" |
|
1247 | """Sets up the command history, and starts regular autosaves.""" | |
1251 |
self.history_manager = HistoryManager(shell=self |
|
1248 | self.history_manager = HistoryManager(shell=self) | |
1252 |
|
||||
1253 | def save_history(self): |
|
|||
1254 | """Save input history to a file (via readline library).""" |
|
|||
1255 | self.history_manager.save_history() |
|
|||
1256 |
|
||||
1257 | def reload_history(self): |
|
|||
1258 | """Reload the input history from disk file.""" |
|
|||
1259 | self.history_manager.reload_history() |
|
|||
1260 |
|
1249 | |||
1261 | def history_saving_wrapper(self, func): |
|
1250 | def history_saving_wrapper(self, func): | |
1262 | """ Wrap func for readline history saving |
|
1251 | """ Wrap func for readline history saving | |
@@ -1277,8 +1266,8 b' class InteractiveShell(Configurable, Magic):' | |||||
1277 | self.reload_history() |
|
1266 | self.reload_history() | |
1278 | return wrapper |
|
1267 | return wrapper | |
1279 |
|
1268 | |||
1280 |
def get_history(self, |
|
1269 | def get_history(self, start=1, stop=None, raw=False, output=True): | |
1281 |
return self.history_manager.get_history( |
|
1270 | return self.history_manager.get_history(start, stop, raw, output) | |
1282 |
|
1271 | |||
1283 |
|
1272 | |||
1284 | #------------------------------------------------------------------------- |
|
1273 | #------------------------------------------------------------------------- | |
@@ -1561,7 +1550,11 b' class InteractiveShell(Configurable, Magic):' | |||||
1561 | # otherwise we end up with a monster history after a while: |
|
1550 | # otherwise we end up with a monster history after a while: | |
1562 | readline.set_history_length(self.history_length) |
|
1551 | readline.set_history_length(self.history_length) | |
1563 |
|
1552 | |||
1564 | self.history_manager.populate_readline_history() |
|
1553 | # Load the last 1000 lines from history | |
|
1554 | for cell in self.history_manager.tail_db_history(1000): | |||
|
1555 | if cell.strip(): # Ignore blank lines | |||
|
1556 | for line in cell.splitlines(): | |||
|
1557 | readline.add_history(line) | |||
1565 |
|
1558 | |||
1566 | # Configure auto-indent for all platforms |
|
1559 | # Configure auto-indent for all platforms | |
1567 | self.set_autoindent(self.autoindent) |
|
1560 | self.set_autoindent(self.autoindent) | |
@@ -2536,8 +2529,6 b' class InteractiveShell(Configurable, Magic):' | |||||
2536 | except OSError: |
|
2529 | except OSError: | |
2537 | pass |
|
2530 | pass | |
2538 |
|
2531 | |||
2539 | self.save_history() |
|
|||
2540 |
|
||||
2541 | # Clear all user namespaces to release all references cleanly. |
|
2532 | # Clear all user namespaces to release all references cleanly. | |
2542 | self.reset() |
|
2533 | self.reset() | |
2543 |
|
2534 |
General Comments 0
You need to be logged in to leave comments.
Login now