Show More
@@ -32,79 +32,27 b' from IPython.utils.warn import warn' | |||
|
32 | 32 | # Classes and functions |
|
33 | 33 | #----------------------------------------------------------------------------- |
|
34 | 34 | |
|
35 |
class History |
|
|
36 | """A class to organize all history-related functionality in one place. | |
|
37 | """ | |
|
38 | # Public interface | |
|
39 | ||
|
40 | # An instance of the IPython shell we are attached to | |
|
41 | shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') | |
|
42 | # Lists to hold processed and raw history. These start with a blank entry | |
|
43 | # so that we can index them starting from 1 | |
|
44 | input_hist_parsed = List([""]) | |
|
45 | input_hist_raw = List([""]) | |
|
46 | # A list of directories visited during session | |
|
47 | dir_hist = List() | |
|
48 | def _dir_hist_default(self): | |
|
49 | try: | |
|
50 | return [os.getcwdu()] | |
|
51 | except OSError: | |
|
52 | return [] | |
|
53 | ||
|
54 | # A dict of output history, keyed with ints from the shell's | |
|
55 | # execution count. | |
|
56 | output_hist = Dict() | |
|
57 | # The text/plain repr of outputs. | |
|
58 | output_hist_reprs = Dict() | |
|
59 | ||
|
35 | class HistoryAccessor(Configurable): | |
|
36 | """Access the history database without adding to it. For use by standalone | |
|
37 | history tools.""" | |
|
60 | 38 | # String holding the path to the history file |
|
61 | 39 | hist_file = Unicode(config=True) |
|
62 | 40 | |
|
63 | 41 | # The SQLite database |
|
64 | 42 | db = Instance(sqlite3.Connection) |
|
65 | # The number of the current session in the history database | |
|
66 | session_number = CInt() | |
|
67 | # Should we log output to the database? (default no) | |
|
68 | db_log_output = Bool(False, config=True) | |
|
69 | # Write to database every x commands (higher values save disk access & power) | |
|
70 | # Values of 1 or less effectively disable caching. | |
|
71 | db_cache_size = Int(0, config=True) | |
|
72 | # The input and output caches | |
|
73 | db_input_cache = List() | |
|
74 | db_output_cache = List() | |
|
75 | ||
|
76 | # History saving in separate thread | |
|
77 | save_thread = Instance('IPython.core.history.HistorySavingThread') | |
|
78 | try: # Event is a function returning an instance of _Event... | |
|
79 | save_flag = Instance(threading._Event) | |
|
80 | except AttributeError: # ...until Python 3.3, when it's a class. | |
|
81 | save_flag = Instance(threading.Event) | |
|
82 | ||
|
83 | # Private interface | |
|
84 | # Variables used to store the three last inputs from the user. On each new | |
|
85 | # history update, we populate the user's namespace with these, shifted as | |
|
86 | # necessary. | |
|
87 | _i00 = Unicode(u'') | |
|
88 | _i = Unicode(u'') | |
|
89 | _ii = Unicode(u'') | |
|
90 | _iii = Unicode(u'') | |
|
91 | ||
|
92 | # A regex matching all forms of the exit command, so that we don't store | |
|
93 | # them in the history (it's annoying to rewind the first entry and land on | |
|
94 | # an exit call). | |
|
95 | _exit_re = re.compile(r"(exit|quit)(\s*\(.*\))?$") | |
|
96 | ||
|
97 | def __init__(self, shell, config=None, **traits): | |
|
98 | """Create a new history manager associated with a shell instance. | |
|
43 | ||
|
44 | def __init__(self, hist_file=u'', shell=None, config=None, **traits): | |
|
45 | """Create a new history accessor. | |
|
46 | ||
|
47 | hist_file must be given, either as an argument or through config. | |
|
99 | 48 | """ |
|
100 | 49 | # We need a pointer back to the shell for various tasks. |
|
101 |
super(History |
|
|
102 | **traits) | |
|
50 | super(HistoryAccessor, self).__init__(shell=shell, config=config, | |
|
51 | hist_file=hist_file, **traits) | |
|
103 | 52 | |
|
104 | 53 | if self.hist_file == u'': |
|
105 | 54 | # No one has set the hist_file, yet. |
|
106 | histfname = 'history' | |
|
107 | self.hist_file = os.path.join(shell.profile_dir.location, histfname + '.sqlite') | |
|
55 | self.hist_file = self._get_hist_file_name(hist_file) | |
|
108 | 56 | |
|
109 | 57 | try: |
|
110 | 58 | self.init_db() |
@@ -119,16 +67,11 b' class HistoryManager(Configurable):' | |||
|
119 | 67 | else: |
|
120 | 68 | # The hist_file is probably :memory: or something else. |
|
121 | 69 | raise |
|
122 | ||
|
123 | self.save_flag = threading.Event() | |
|
124 | self.db_input_cache_lock = threading.Lock() | |
|
125 | self.db_output_cache_lock = threading.Lock() | |
|
126 | self.save_thread = HistorySavingThread(self) | |
|
127 | self.save_thread.start() | |
|
128 | ||
|
129 | self.new_session() | |
|
130 | ||
|
131 | ||
|
70 | ||
|
71 | def _get_hist_file_name(self, hist_file=None): | |
|
72 | "Override to produce a default history file name." | |
|
73 | raise NotImplementedError("No default history file") | |
|
74 | ||
|
132 | 75 | def init_db(self): |
|
133 | 76 | """Connect to the database, and create tables if necessary.""" |
|
134 | 77 | # use detect_types so that timestamps return datetime objects |
@@ -146,49 +89,6 b' class HistoryManager(Configurable):' | |||
|
146 | 89 | PRIMARY KEY (session, line))""") |
|
147 | 90 | self.db.commit() |
|
148 | 91 | |
|
149 | def new_session(self, conn=None): | |
|
150 | """Get a new session number.""" | |
|
151 | if conn is None: | |
|
152 | conn = self.db | |
|
153 | ||
|
154 | with conn: | |
|
155 | # N.B. 'insert into' here is lower case because of a bug in the | |
|
156 | # sqlite3 module that affects the Turkish locale. This should be | |
|
157 | # fixed for Python 2.7.3 and 3.2.3, as well as 3.3 onwards. | |
|
158 | # http://bugs.python.org/issue13099 | |
|
159 | cur = conn.execute("""insert into sessions VALUES (NULL, ?, NULL, | |
|
160 | NULL, "") """, (datetime.datetime.now(),)) | |
|
161 | self.session_number = cur.lastrowid | |
|
162 | ||
|
163 | def end_session(self): | |
|
164 | """Close the database session, filling in the end time and line count.""" | |
|
165 | self.writeout_cache() | |
|
166 | with self.db: | |
|
167 | self.db.execute("""UPDATE sessions SET end=?, num_cmds=? WHERE | |
|
168 | session==?""", (datetime.datetime.now(), | |
|
169 | len(self.input_hist_parsed)-1, self.session_number)) | |
|
170 | self.session_number = 0 | |
|
171 | ||
|
172 | def name_session(self, name): | |
|
173 | """Give the current session a name in the history database.""" | |
|
174 | with self.db: | |
|
175 | self.db.execute("UPDATE sessions SET remark=? WHERE session==?", | |
|
176 | (name, self.session_number)) | |
|
177 | ||
|
178 | def reset(self, new_session=True): | |
|
179 | """Clear the session history, releasing all object references, and | |
|
180 | optionally open a new session.""" | |
|
181 | self.output_hist.clear() | |
|
182 | # The directory history can't be completely empty | |
|
183 | self.dir_hist[:] = [os.getcwdu()] | |
|
184 | ||
|
185 | if new_session: | |
|
186 | if self.session_number: | |
|
187 | self.end_session() | |
|
188 | self.input_hist_parsed[:] = [""] | |
|
189 | self.input_hist_raw[:] = [""] | |
|
190 | self.new_session() | |
|
191 | ||
|
192 | 92 | ## ------------------------------- |
|
193 | 93 | ## Methods for retrieving history: |
|
194 | 94 | ## ------------------------------- |
@@ -299,26 +199,6 b' class HistoryManager(Configurable):' | |||
|
299 | 199 | return self._run_sql("WHERE %s GLOB ?" % tosearch, (pattern,), |
|
300 | 200 | raw=raw, output=output) |
|
301 | 201 | |
|
302 | def _get_range_session(self, start=1, stop=None, raw=True, output=False): | |
|
303 | """Get input and output history from the current session. Called by | |
|
304 | get_range, and takes similar parameters.""" | |
|
305 | input_hist = self.input_hist_raw if raw else self.input_hist_parsed | |
|
306 | ||
|
307 | n = len(input_hist) | |
|
308 | if start < 0: | |
|
309 | start += n | |
|
310 | if not stop or (stop > n): | |
|
311 | stop = n | |
|
312 | elif stop < 0: | |
|
313 | stop += n | |
|
314 | ||
|
315 | for i in range(start, stop): | |
|
316 | if output: | |
|
317 | line = (input_hist[i], self.output_hist_reprs.get(i)) | |
|
318 | else: | |
|
319 | line = input_hist[i] | |
|
320 | yield (0, i, line) | |
|
321 | ||
|
322 | 202 | def get_range(self, session=0, start=1, stop=None, raw=True,output=False): |
|
323 | 203 | """Retrieve input by session. |
|
324 | 204 | |
@@ -381,6 +261,144 b' class HistoryManager(Configurable):' | |||
|
381 | 261 | for line in self.get_range(sess, s, e, raw=raw, output=output): |
|
382 | 262 | yield line |
|
383 | 263 | |
|
264 | ||
|
265 | class HistoryManager(HistoryAccessor): | |
|
266 | """A class to organize all history-related functionality in one place. | |
|
267 | """ | |
|
268 | # Public interface | |
|
269 | ||
|
270 | # An instance of the IPython shell we are attached to | |
|
271 | shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') | |
|
272 | # Lists to hold processed and raw history. These start with a blank entry | |
|
273 | # so that we can index them starting from 1 | |
|
274 | input_hist_parsed = List([""]) | |
|
275 | input_hist_raw = List([""]) | |
|
276 | # A list of directories visited during session | |
|
277 | dir_hist = List() | |
|
278 | def _dir_hist_default(self): | |
|
279 | try: | |
|
280 | return [os.getcwdu()] | |
|
281 | except OSError: | |
|
282 | return [] | |
|
283 | ||
|
284 | # A dict of output history, keyed with ints from the shell's | |
|
285 | # execution count. | |
|
286 | output_hist = Dict() | |
|
287 | # The text/plain repr of outputs. | |
|
288 | output_hist_reprs = Dict() | |
|
289 | ||
|
290 | # The number of the current session in the history database | |
|
291 | session_number = CInt() | |
|
292 | # Should we log output to the database? (default no) | |
|
293 | db_log_output = Bool(False, config=True) | |
|
294 | # Write to database every x commands (higher values save disk access & power) | |
|
295 | # Values of 1 or less effectively disable caching. | |
|
296 | db_cache_size = Int(0, config=True) | |
|
297 | # The input and output caches | |
|
298 | db_input_cache = List() | |
|
299 | db_output_cache = List() | |
|
300 | ||
|
301 | # History saving in separate thread | |
|
302 | save_thread = Instance('IPython.core.history.HistorySavingThread') | |
|
303 | try: # Event is a function returning an instance of _Event... | |
|
304 | save_flag = Instance(threading._Event) | |
|
305 | except AttributeError: # ...until Python 3.3, when it's a class. | |
|
306 | save_flag = Instance(threading.Event) | |
|
307 | ||
|
308 | # Private interface | |
|
309 | # Variables used to store the three last inputs from the user. On each new | |
|
310 | # history update, we populate the user's namespace with these, shifted as | |
|
311 | # necessary. | |
|
312 | _i00 = Unicode(u'') | |
|
313 | _i = Unicode(u'') | |
|
314 | _ii = Unicode(u'') | |
|
315 | _iii = Unicode(u'') | |
|
316 | ||
|
317 | # A regex matching all forms of the exit command, so that we don't store | |
|
318 | # them in the history (it's annoying to rewind the first entry and land on | |
|
319 | # an exit call). | |
|
320 | _exit_re = re.compile(r"(exit|quit)(\s*\(.*\))?$") | |
|
321 | ||
|
322 | def __init__(self, shell=None, config=None, **traits): | |
|
323 | """Create a new history manager associated with a shell instance. | |
|
324 | """ | |
|
325 | # We need a pointer back to the shell for various tasks. | |
|
326 | super(HistoryManager, self).__init__(shell=shell, config=config, | |
|
327 | **traits) | |
|
328 | self.save_flag = threading.Event() | |
|
329 | self.db_input_cache_lock = threading.Lock() | |
|
330 | self.db_output_cache_lock = threading.Lock() | |
|
331 | self.save_thread = HistorySavingThread(self) | |
|
332 | self.save_thread.start() | |
|
333 | ||
|
334 | self.new_session() | |
|
335 | ||
|
336 | def _get_hist_file_name(self, hist_file=None): | |
|
337 | profile_dir = self.shell.profile_dir.location | |
|
338 | return os.path.join(profile_dir, 'history.sqlite') | |
|
339 | ||
|
340 | def new_session(self, conn=None): | |
|
341 | """Get a new session number.""" | |
|
342 | if conn is None: | |
|
343 | conn = self.db | |
|
344 | ||
|
345 | with conn: | |
|
346 | cur = conn.execute("""INSERT INTO sessions VALUES (NULL, ?, NULL, | |
|
347 | NULL, "") """, (datetime.datetime.now(),)) | |
|
348 | self.session_number = cur.lastrowid | |
|
349 | ||
|
350 | def end_session(self): | |
|
351 | """Close the database session, filling in the end time and line count.""" | |
|
352 | self.writeout_cache() | |
|
353 | with self.db: | |
|
354 | self.db.execute("""UPDATE sessions SET end=?, num_cmds=? WHERE | |
|
355 | session==?""", (datetime.datetime.now(), | |
|
356 | len(self.input_hist_parsed)-1, self.session_number)) | |
|
357 | self.session_number = 0 | |
|
358 | ||
|
359 | def name_session(self, name): | |
|
360 | """Give the current session a name in the history database.""" | |
|
361 | with self.db: | |
|
362 | self.db.execute("UPDATE sessions SET remark=? WHERE session==?", | |
|
363 | (name, self.session_number)) | |
|
364 | ||
|
365 | def reset(self, new_session=True): | |
|
366 | """Clear the session history, releasing all object references, and | |
|
367 | optionally open a new session.""" | |
|
368 | self.output_hist.clear() | |
|
369 | # The directory history can't be completely empty | |
|
370 | self.dir_hist[:] = [os.getcwdu()] | |
|
371 | ||
|
372 | if new_session: | |
|
373 | if self.session_number: | |
|
374 | self.end_session() | |
|
375 | self.input_hist_parsed[:] = [""] | |
|
376 | self.input_hist_raw[:] = [""] | |
|
377 | self.new_session() | |
|
378 | ||
|
379 | # ------------------------------ | |
|
380 | # Methods for retrieving history | |
|
381 | # ------------------------------ | |
|
382 | def _get_range_session(self, start=1, stop=None, raw=True, output=False): | |
|
383 | """Get input and output history from the current session. Called by | |
|
384 | get_range, and takes similar parameters.""" | |
|
385 | input_hist = self.input_hist_raw if raw else self.input_hist_parsed | |
|
386 | ||
|
387 | n = len(input_hist) | |
|
388 | if start < 0: | |
|
389 | start += n | |
|
390 | if not stop or (stop > n): | |
|
391 | stop = n | |
|
392 | elif stop < 0: | |
|
393 | stop += n | |
|
394 | ||
|
395 | for i in range(start, stop): | |
|
396 | if output: | |
|
397 | line = (input_hist[i], self.output_hist_reprs.get(i)) | |
|
398 | else: | |
|
399 | line = input_hist[i] | |
|
400 | yield (0, i, line) | |
|
401 | ||
|
384 | 402 | ## ---------------------------- |
|
385 | 403 | ## Methods for storing history: |
|
386 | 404 | ## ---------------------------- |
General Comments 0
You need to be logged in to leave comments.
Login now