##// END OF EJS Templates
Fallback to :memory: on repeated failure to load history...
Min RK -
Show More
@@ -1,873 +1,890 b''
1 1 """ History related magics and functionality """
2 2
3 3 # Copyright (c) IPython Development Team.
4 4 # Distributed under the terms of the Modified BSD License.
5 5
6 6 from __future__ import print_function
7 7
8 8 import atexit
9 9 import datetime
10 10 import os
11 11 import re
12 12 try:
13 13 import sqlite3
14 14 except ImportError:
15 15 try:
16 16 from pysqlite2 import dbapi2 as sqlite3
17 17 except ImportError:
18 18 sqlite3 = None
19 import sys
20 19 import threading
21 20
22 21 from traitlets.config.configurable import LoggingConfigurable
23 22 from decorator import decorator
24 23 from IPython.utils.decorators import undoc
25 24 from IPython.utils.path import locate_profile
26 25 from IPython.utils import py3compat
27 26 from traitlets import (
28 27 Any, Bool, Dict, Instance, Integer, List, Unicode, TraitError,
29 28 )
30 29 from warnings import warn
31 30
32 31 #-----------------------------------------------------------------------------
33 32 # Classes and functions
34 33 #-----------------------------------------------------------------------------
35 34
36 35 @undoc
37 36 class DummyDB(object):
38 37 """Dummy DB that will act as a black hole for history.
39 38
40 39 Only used in the absence of sqlite"""
41 40 def execute(*args, **kwargs):
42 41 return []
43 42
44 43 def commit(self, *args, **kwargs):
45 44 pass
46 45
47 46 def __enter__(self, *args, **kwargs):
48 47 pass
49 48
50 49 def __exit__(self, *args, **kwargs):
51 50 pass
52 51
53 52
54 53 @decorator
55 54 def needs_sqlite(f, self, *a, **kw):
56 55 """Decorator: return an empty list in the absence of sqlite."""
57 56 if sqlite3 is None or not self.enabled:
58 57 return []
59 58 else:
60 59 return f(self, *a, **kw)
61 60
62 61
63 62 if sqlite3 is not None:
64 63 DatabaseError = sqlite3.DatabaseError
65 64 OperationalError = sqlite3.OperationalError
66 65 else:
67 66 @undoc
68 67 class DatabaseError(Exception):
69 68 "Dummy exception when sqlite could not be imported. Should never occur."
70 69
71 70 @undoc
72 71 class OperationalError(Exception):
73 72 "Dummy exception when sqlite could not be imported. Should never occur."
74 73
75 74 @decorator
76 75 def catch_corrupt_db(f, self, *a, **kw):
77 76 """A decorator which wraps HistoryAccessor method calls to catch errors from
78 77 a corrupt SQLite database, move the old database out of the way, and create
79 78 a new one.
80 79 """
81 80 try:
82 81 return f(self, *a, **kw)
83 82 except (DatabaseError, OperationalError) as e:
84 if os.path.isfile(self.hist_file):
85 # Try to move the file out of the way
86 base,ext = os.path.splitext(self.hist_file)
87 newpath = base + '-corrupt' + ext
88 os.rename(self.hist_file, newpath)
89 print("ERROR! History file wasn't a valid SQLite database (%s)." % e,
90 "It was moved to %s" % newpath, "and a new file created.", file=sys.stderr)
83 self._corrupt_db_counter += 1
84 self.log.error("Failed to open SQLite history %s (%s).", self.hist_file, e)
85 if self.hist_file != ':memory:':
86 if self._corrupt_db_counter > self._corrupt_db_limit:
87 self.hist_file = ':memory:'
88 self.log.error("Failed to load history too many times, history will not be saved.")
89 elif os.path.isfile(self.hist_file):
90 # Try to move the file out of the way
91 base,ext = os.path.splitext(self.hist_file)
92 now = datetime.datetime.now().isoformat().replace(':', '.')
93 newpath = base + '-corrupt-' + now + ext
94 # don't clobber previous corrupt backups
95 for i in range(100):
96 if not os.path.isfile(newpath):
97 break
98 else:
99 newpath = base + '-corrupt-' + now + (u'-%i' % i) + ext
100 os.rename(self.hist_file, newpath)
101 self.log.error("History file was moved to %s and a new file created.", newpath)
91 102 self.init_db()
92 103 return []
93
94 104 else:
95 # The hist_file is probably :memory: or something else.
105 # Failed with :memory:, something serious is wrong
96 106 raise
97 107
98 108 class HistoryAccessorBase(LoggingConfigurable):
99 109 """An abstract class for History Accessors """
100 110
101 111 def get_tail(self, n=10, raw=True, output=False, include_latest=False):
102 112 raise NotImplementedError
103 113
104 114 def search(self, pattern="*", raw=True, search_raw=True,
105 115 output=False, n=None, unique=False):
106 116 raise NotImplementedError
107 117
108 118 def get_range(self, session, start=1, stop=None, raw=True,output=False):
109 119 raise NotImplementedError
110 120
111 121 def get_range_by_str(self, rangestr, raw=True, output=False):
112 122 raise NotImplementedError
113 123
114 124
115 125 class HistoryAccessor(HistoryAccessorBase):
116 126 """Access the history database without adding to it.
117 127
118 128 This is intended for use by standalone history tools. IPython shells use
119 129 HistoryManager, below, which is a subclass of this."""
120 130
131 # counter for init_db retries, so we don't keep trying over and over
132 _corrupt_db_counter = 0
133 # after two failures, fallback on :memory:
134 _corrupt_db_limit = 2
135
121 136 # String holding the path to the history file
122 137 hist_file = Unicode(config=True,
123 138 help="""Path to file to use for SQLite history database.
124 139
125 140 By default, IPython will put the history database in the IPython
126 141 profile directory. If you would rather share one history among
127 142 profiles, you can set this value in each, so that they are consistent.
128 143
129 144 Due to an issue with fcntl, SQLite is known to misbehave on some NFS
130 145 mounts. If you see IPython hanging, try setting this to something on a
131 146 local disk, e.g::
132 147
133 148 ipython --HistoryManager.hist_file=/tmp/ipython_hist.sqlite
134 149
135 150 """)
136 151
137 152 enabled = Bool(True, config=True,
138 153 help="""enable the SQLite history
139 154
140 155 set enabled=False to disable the SQLite history,
141 156 in which case there will be no stored history, no SQLite connection,
142 157 and no background saving thread. This may be necessary in some
143 158 threaded environments where IPython is embedded.
144 159 """
145 160 )
146 161
147 162 connection_options = Dict(config=True,
148 163 help="""Options for configuring the SQLite connection
149 164
150 165 These options are passed as keyword args to sqlite3.connect
151 166 when establishing database conenctions.
152 167 """
153 168 )
154 169
155 170 # The SQLite database
156 171 db = Any()
157 172 def _db_changed(self, name, old, new):
158 173 """validate the db, since it can be an Instance of two different types"""
159 174 connection_types = (DummyDB,)
160 175 if sqlite3 is not None:
161 176 connection_types = (DummyDB, sqlite3.Connection)
162 177 if not isinstance(new, connection_types):
163 178 msg = "%s.db must be sqlite3 Connection or DummyDB, not %r" % \
164 179 (self.__class__.__name__, new)
165 180 raise TraitError(msg)
166 181
167 182 def __init__(self, profile='default', hist_file=u'', **traits):
168 183 """Create a new history accessor.
169 184
170 185 Parameters
171 186 ----------
172 187 profile : str
173 188 The name of the profile from which to open history.
174 189 hist_file : str
175 190 Path to an SQLite history database stored by IPython. If specified,
176 191 hist_file overrides profile.
177 192 config : :class:`~traitlets.config.loader.Config`
178 193 Config object. hist_file can also be set through this.
179 194 """
180 195 # We need a pointer back to the shell for various tasks.
181 196 super(HistoryAccessor, self).__init__(**traits)
182 197 # defer setting hist_file from kwarg until after init,
183 198 # otherwise the default kwarg value would clobber any value
184 199 # set by config
185 200 if hist_file:
186 201 self.hist_file = hist_file
187 202
188 203 if self.hist_file == u'':
189 204 # No one has set the hist_file, yet.
190 205 self.hist_file = self._get_hist_file_name(profile)
191 206
192 207 if sqlite3 is None and self.enabled:
193 208 warn("IPython History requires SQLite, your history will not be saved")
194 209 self.enabled = False
195 210
196 211 self.init_db()
197 212
198 213 def _get_hist_file_name(self, profile='default'):
199 214 """Find the history file for the given profile name.
200 215
201 216 This is overridden by the HistoryManager subclass, to use the shell's
202 217 active profile.
203 218
204 219 Parameters
205 220 ----------
206 221 profile : str
207 222 The name of a profile which has a history file.
208 223 """
209 224 return os.path.join(locate_profile(profile), 'history.sqlite')
210 225
211 226 @catch_corrupt_db
212 227 def init_db(self):
213 228 """Connect to the database, and create tables if necessary."""
214 229 if not self.enabled:
215 230 self.db = DummyDB()
216 231 return
217 232
218 233 # use detect_types so that timestamps return datetime objects
219 234 kwargs = dict(detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)
220 235 kwargs.update(self.connection_options)
221 236 self.db = sqlite3.connect(self.hist_file, **kwargs)
222 237 self.db.execute("""CREATE TABLE IF NOT EXISTS sessions (session integer
223 238 primary key autoincrement, start timestamp,
224 239 end timestamp, num_cmds integer, remark text)""")
225 240 self.db.execute("""CREATE TABLE IF NOT EXISTS history
226 241 (session integer, line integer, source text, source_raw text,
227 242 PRIMARY KEY (session, line))""")
228 243 # Output history is optional, but ensure the table's there so it can be
229 244 # enabled later.
230 245 self.db.execute("""CREATE TABLE IF NOT EXISTS output_history
231 246 (session integer, line integer, output text,
232 247 PRIMARY KEY (session, line))""")
233 248 self.db.commit()
249 # success! reset corrupt db count
250 self._corrupt_db_counter = 0
234 251
235 252 def writeout_cache(self):
236 253 """Overridden by HistoryManager to dump the cache before certain
237 254 database lookups."""
238 255 pass
239 256
240 257 ## -------------------------------
241 258 ## Methods for retrieving history:
242 259 ## -------------------------------
243 260 def _run_sql(self, sql, params, raw=True, output=False):
244 261 """Prepares and runs an SQL query for the history database.
245 262
246 263 Parameters
247 264 ----------
248 265 sql : str
249 266 Any filtering expressions to go after SELECT ... FROM ...
250 267 params : tuple
251 268 Parameters passed to the SQL query (to replace "?")
252 269 raw, output : bool
253 270 See :meth:`get_range`
254 271
255 272 Returns
256 273 -------
257 274 Tuples as :meth:`get_range`
258 275 """
259 276 toget = 'source_raw' if raw else 'source'
260 277 sqlfrom = "history"
261 278 if output:
262 279 sqlfrom = "history LEFT JOIN output_history USING (session, line)"
263 280 toget = "history.%s, output_history.output" % toget
264 281 cur = self.db.execute("SELECT session, line, %s FROM %s " %\
265 282 (toget, sqlfrom) + sql, params)
266 283 if output: # Regroup into 3-tuples, and parse JSON
267 284 return ((ses, lin, (inp, out)) for ses, lin, inp, out in cur)
268 285 return cur
269 286
270 287 @needs_sqlite
271 288 @catch_corrupt_db
272 289 def get_session_info(self, session):
273 290 """Get info about a session.
274 291
275 292 Parameters
276 293 ----------
277 294
278 295 session : int
279 296 Session number to retrieve.
280 297
281 298 Returns
282 299 -------
283 300
284 301 session_id : int
285 302 Session ID number
286 303 start : datetime
287 304 Timestamp for the start of the session.
288 305 end : datetime
289 306 Timestamp for the end of the session, or None if IPython crashed.
290 307 num_cmds : int
291 308 Number of commands run, or None if IPython crashed.
292 309 remark : unicode
293 310 A manually set description.
294 311 """
295 312 query = "SELECT * from sessions where session == ?"
296 313 return self.db.execute(query, (session,)).fetchone()
297 314
298 315 @catch_corrupt_db
299 316 def get_last_session_id(self):
300 317 """Get the last session ID currently in the database.
301 318
302 319 Within IPython, this should be the same as the value stored in
303 320 :attr:`HistoryManager.session_number`.
304 321 """
305 322 for record in self.get_tail(n=1, include_latest=True):
306 323 return record[0]
307 324
308 325 @catch_corrupt_db
309 326 def get_tail(self, n=10, raw=True, output=False, include_latest=False):
310 327 """Get the last n lines from the history database.
311 328
312 329 Parameters
313 330 ----------
314 331 n : int
315 332 The number of lines to get
316 333 raw, output : bool
317 334 See :meth:`get_range`
318 335 include_latest : bool
319 336 If False (default), n+1 lines are fetched, and the latest one
320 337 is discarded. This is intended to be used where the function
321 338 is called by a user command, which it should not return.
322 339
323 340 Returns
324 341 -------
325 342 Tuples as :meth:`get_range`
326 343 """
327 344 self.writeout_cache()
328 345 if not include_latest:
329 346 n += 1
330 347 cur = self._run_sql("ORDER BY session DESC, line DESC LIMIT ?",
331 348 (n,), raw=raw, output=output)
332 349 if not include_latest:
333 350 return reversed(list(cur)[1:])
334 351 return reversed(list(cur))
335 352
336 353 @catch_corrupt_db
337 354 def search(self, pattern="*", raw=True, search_raw=True,
338 355 output=False, n=None, unique=False):
339 356 """Search the database using unix glob-style matching (wildcards
340 357 * and ?).
341 358
342 359 Parameters
343 360 ----------
344 361 pattern : str
345 362 The wildcarded pattern to match when searching
346 363 search_raw : bool
347 364 If True, search the raw input, otherwise, the parsed input
348 365 raw, output : bool
349 366 See :meth:`get_range`
350 367 n : None or int
351 368 If an integer is given, it defines the limit of
352 369 returned entries.
353 370 unique : bool
354 371 When it is true, return only unique entries.
355 372
356 373 Returns
357 374 -------
358 375 Tuples as :meth:`get_range`
359 376 """
360 377 tosearch = "source_raw" if search_raw else "source"
361 378 if output:
362 379 tosearch = "history." + tosearch
363 380 self.writeout_cache()
364 381 sqlform = "WHERE %s GLOB ?" % tosearch
365 382 params = (pattern,)
366 383 if unique:
367 384 sqlform += ' GROUP BY {0}'.format(tosearch)
368 385 if n is not None:
369 386 sqlform += " ORDER BY session DESC, line DESC LIMIT ?"
370 387 params += (n,)
371 388 elif unique:
372 389 sqlform += " ORDER BY session, line"
373 390 cur = self._run_sql(sqlform, params, raw=raw, output=output)
374 391 if n is not None:
375 392 return reversed(list(cur))
376 393 return cur
377 394
378 395 @catch_corrupt_db
379 396 def get_range(self, session, start=1, stop=None, raw=True,output=False):
380 397 """Retrieve input by session.
381 398
382 399 Parameters
383 400 ----------
384 401 session : int
385 402 Session number to retrieve.
386 403 start : int
387 404 First line to retrieve.
388 405 stop : int
389 406 End of line range (excluded from output itself). If None, retrieve
390 407 to the end of the session.
391 408 raw : bool
392 409 If True, return untranslated input
393 410 output : bool
394 411 If True, attempt to include output. This will be 'real' Python
395 412 objects for the current session, or text reprs from previous
396 413 sessions if db_log_output was enabled at the time. Where no output
397 414 is found, None is used.
398 415
399 416 Returns
400 417 -------
401 418 entries
402 419 An iterator over the desired lines. Each line is a 3-tuple, either
403 420 (session, line, input) if output is False, or
404 421 (session, line, (input, output)) if output is True.
405 422 """
406 423 if stop:
407 424 lineclause = "line >= ? AND line < ?"
408 425 params = (session, start, stop)
409 426 else:
410 427 lineclause = "line>=?"
411 428 params = (session, start)
412 429
413 430 return self._run_sql("WHERE session==? AND %s" % lineclause,
414 431 params, raw=raw, output=output)
415 432
416 433 def get_range_by_str(self, rangestr, raw=True, output=False):
417 434 """Get lines of history from a string of ranges, as used by magic
418 435 commands %hist, %save, %macro, etc.
419 436
420 437 Parameters
421 438 ----------
422 439 rangestr : str
423 440 A string specifying ranges, e.g. "5 ~2/1-4". See
424 441 :func:`magic_history` for full details.
425 442 raw, output : bool
426 443 As :meth:`get_range`
427 444
428 445 Returns
429 446 -------
430 447 Tuples as :meth:`get_range`
431 448 """
432 449 for sess, s, e in extract_hist_ranges(rangestr):
433 450 for line in self.get_range(sess, s, e, raw=raw, output=output):
434 451 yield line
435 452
436 453
437 454 class HistoryManager(HistoryAccessor):
438 455 """A class to organize all history-related functionality in one place.
439 456 """
440 457 # Public interface
441 458
442 459 # An instance of the IPython shell we are attached to
443 460 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC',
444 461 allow_none=True)
445 462 # Lists to hold processed and raw history. These start with a blank entry
446 463 # so that we can index them starting from 1
447 464 input_hist_parsed = List([""])
448 465 input_hist_raw = List([""])
449 466 # A list of directories visited during session
450 467 dir_hist = List()
451 468 def _dir_hist_default(self):
452 469 try:
453 470 return [py3compat.getcwd()]
454 471 except OSError:
455 472 return []
456 473
457 474 # A dict of output history, keyed with ints from the shell's
458 475 # execution count.
459 476 output_hist = Dict()
460 477 # The text/plain repr of outputs.
461 478 output_hist_reprs = Dict()
462 479
463 480 # The number of the current session in the history database
464 481 session_number = Integer()
465 482
466 483 db_log_output = Bool(False, config=True,
467 484 help="Should the history database include output? (default: no)"
468 485 )
469 486 db_cache_size = Integer(0, config=True,
470 487 help="Write to database every x commands (higher values save disk access & power).\n"
471 488 "Values of 1 or less effectively disable caching."
472 489 )
473 490 # The input and output caches
474 491 db_input_cache = List()
475 492 db_output_cache = List()
476 493
477 494 # History saving in separate thread
478 495 save_thread = Instance('IPython.core.history.HistorySavingThread',
479 496 allow_none=True)
480 497 try: # Event is a function returning an instance of _Event...
481 498 save_flag = Instance(threading._Event, allow_none=True)
482 499 except AttributeError: # ...until Python 3.3, when it's a class.
483 500 save_flag = Instance(threading.Event, allow_none=True)
484 501
485 502 # Private interface
486 503 # Variables used to store the three last inputs from the user. On each new
487 504 # history update, we populate the user's namespace with these, shifted as
488 505 # necessary.
489 506 _i00 = Unicode(u'')
490 507 _i = Unicode(u'')
491 508 _ii = Unicode(u'')
492 509 _iii = Unicode(u'')
493 510
494 511 # A regex matching all forms of the exit command, so that we don't store
495 512 # them in the history (it's annoying to rewind the first entry and land on
496 513 # an exit call).
497 514 _exit_re = re.compile(r"(exit|quit)(\s*\(.*\))?$")
498 515
499 516 def __init__(self, shell=None, config=None, **traits):
500 517 """Create a new history manager associated with a shell instance.
501 518 """
502 519 # We need a pointer back to the shell for various tasks.
503 520 super(HistoryManager, self).__init__(shell=shell, config=config,
504 521 **traits)
505 522 self.save_flag = threading.Event()
506 523 self.db_input_cache_lock = threading.Lock()
507 524 self.db_output_cache_lock = threading.Lock()
508 525
509 526 try:
510 527 self.new_session()
511 528 except OperationalError:
512 529 self.log.error("Failed to create history session in %s. History will not be saved.",
513 530 self.hist_file, exc_info=True)
514 531 self.hist_file = ':memory:'
515 532
516 533 if self.enabled and self.hist_file != ':memory:':
517 534 self.save_thread = HistorySavingThread(self)
518 535 self.save_thread.start()
519 536
520 537 def _get_hist_file_name(self, profile=None):
521 538 """Get default history file name based on the Shell's profile.
522 539
523 540 The profile parameter is ignored, but must exist for compatibility with
524 541 the parent class."""
525 542 profile_dir = self.shell.profile_dir.location
526 543 return os.path.join(profile_dir, 'history.sqlite')
527 544
528 545 @needs_sqlite
529 546 def new_session(self, conn=None):
530 547 """Get a new session number."""
531 548 if conn is None:
532 549 conn = self.db
533 550
534 551 with conn:
535 552 cur = conn.execute("""INSERT INTO sessions VALUES (NULL, ?, NULL,
536 553 NULL, "") """, (datetime.datetime.now(),))
537 554 self.session_number = cur.lastrowid
538 555
539 556 def end_session(self):
540 557 """Close the database session, filling in the end time and line count."""
541 558 self.writeout_cache()
542 559 with self.db:
543 560 self.db.execute("""UPDATE sessions SET end=?, num_cmds=? WHERE
544 561 session==?""", (datetime.datetime.now(),
545 562 len(self.input_hist_parsed)-1, self.session_number))
546 563 self.session_number = 0
547 564
548 565 def name_session(self, name):
549 566 """Give the current session a name in the history database."""
550 567 with self.db:
551 568 self.db.execute("UPDATE sessions SET remark=? WHERE session==?",
552 569 (name, self.session_number))
553 570
554 571 def reset(self, new_session=True):
555 572 """Clear the session history, releasing all object references, and
556 573 optionally open a new session."""
557 574 self.output_hist.clear()
558 575 # The directory history can't be completely empty
559 576 self.dir_hist[:] = [py3compat.getcwd()]
560 577
561 578 if new_session:
562 579 if self.session_number:
563 580 self.end_session()
564 581 self.input_hist_parsed[:] = [""]
565 582 self.input_hist_raw[:] = [""]
566 583 self.new_session()
567 584
568 585 # ------------------------------
569 586 # Methods for retrieving history
570 587 # ------------------------------
571 588 def get_session_info(self, session=0):
572 589 """Get info about a session.
573 590
574 591 Parameters
575 592 ----------
576 593
577 594 session : int
578 595 Session number to retrieve. The current session is 0, and negative
579 596 numbers count back from current session, so -1 is the previous session.
580 597
581 598 Returns
582 599 -------
583 600
584 601 session_id : int
585 602 Session ID number
586 603 start : datetime
587 604 Timestamp for the start of the session.
588 605 end : datetime
589 606 Timestamp for the end of the session, or None if IPython crashed.
590 607 num_cmds : int
591 608 Number of commands run, or None if IPython crashed.
592 609 remark : unicode
593 610 A manually set description.
594 611 """
595 612 if session <= 0:
596 613 session += self.session_number
597 614
598 615 return super(HistoryManager, self).get_session_info(session=session)
599 616
600 617 def _get_range_session(self, start=1, stop=None, raw=True, output=False):
601 618 """Get input and output history from the current session. Called by
602 619 get_range, and takes similar parameters."""
603 620 input_hist = self.input_hist_raw if raw else self.input_hist_parsed
604 621
605 622 n = len(input_hist)
606 623 if start < 0:
607 624 start += n
608 625 if not stop or (stop > n):
609 626 stop = n
610 627 elif stop < 0:
611 628 stop += n
612 629
613 630 for i in range(start, stop):
614 631 if output:
615 632 line = (input_hist[i], self.output_hist_reprs.get(i))
616 633 else:
617 634 line = input_hist[i]
618 635 yield (0, i, line)
619 636
620 637 def get_range(self, session=0, start=1, stop=None, raw=True,output=False):
621 638 """Retrieve input by session.
622 639
623 640 Parameters
624 641 ----------
625 642 session : int
626 643 Session number to retrieve. The current session is 0, and negative
627 644 numbers count back from current session, so -1 is previous session.
628 645 start : int
629 646 First line to retrieve.
630 647 stop : int
631 648 End of line range (excluded from output itself). If None, retrieve
632 649 to the end of the session.
633 650 raw : bool
634 651 If True, return untranslated input
635 652 output : bool
636 653 If True, attempt to include output. This will be 'real' Python
637 654 objects for the current session, or text reprs from previous
638 655 sessions if db_log_output was enabled at the time. Where no output
639 656 is found, None is used.
640 657
641 658 Returns
642 659 -------
643 660 entries
644 661 An iterator over the desired lines. Each line is a 3-tuple, either
645 662 (session, line, input) if output is False, or
646 663 (session, line, (input, output)) if output is True.
647 664 """
648 665 if session <= 0:
649 666 session += self.session_number
650 667 if session==self.session_number: # Current session
651 668 return self._get_range_session(start, stop, raw, output)
652 669 return super(HistoryManager, self).get_range(session, start, stop, raw,
653 670 output)
654 671
655 672 ## ----------------------------
656 673 ## Methods for storing history:
657 674 ## ----------------------------
658 675 def store_inputs(self, line_num, source, source_raw=None):
659 676 """Store source and raw input in history and create input cache
660 677 variables ``_i*``.
661 678
662 679 Parameters
663 680 ----------
664 681 line_num : int
665 682 The prompt number of this input.
666 683
667 684 source : str
668 685 Python input.
669 686
670 687 source_raw : str, optional
671 688 If given, this is the raw input without any IPython transformations
672 689 applied to it. If not given, ``source`` is used.
673 690 """
674 691 if source_raw is None:
675 692 source_raw = source
676 693 source = source.rstrip('\n')
677 694 source_raw = source_raw.rstrip('\n')
678 695
679 696 # do not store exit/quit commands
680 697 if self._exit_re.match(source_raw.strip()):
681 698 return
682 699
683 700 self.input_hist_parsed.append(source)
684 701 self.input_hist_raw.append(source_raw)
685 702
686 703 with self.db_input_cache_lock:
687 704 self.db_input_cache.append((line_num, source, source_raw))
688 705 # Trigger to flush cache and write to DB.
689 706 if len(self.db_input_cache) >= self.db_cache_size:
690 707 self.save_flag.set()
691 708
692 709 # update the auto _i variables
693 710 self._iii = self._ii
694 711 self._ii = self._i
695 712 self._i = self._i00
696 713 self._i00 = source_raw
697 714
698 715 # hackish access to user namespace to create _i1,_i2... dynamically
699 716 new_i = '_i%s' % line_num
700 717 to_main = {'_i': self._i,
701 718 '_ii': self._ii,
702 719 '_iii': self._iii,
703 720 new_i : self._i00 }
704 721
705 722 if self.shell is not None:
706 723 self.shell.push(to_main, interactive=False)
707 724
708 725 def store_output(self, line_num):
709 726 """If database output logging is enabled, this saves all the
710 727 outputs from the indicated prompt number to the database. It's
711 728 called by run_cell after code has been executed.
712 729
713 730 Parameters
714 731 ----------
715 732 line_num : int
716 733 The line number from which to save outputs
717 734 """
718 735 if (not self.db_log_output) or (line_num not in self.output_hist_reprs):
719 736 return
720 737 output = self.output_hist_reprs[line_num]
721 738
722 739 with self.db_output_cache_lock:
723 740 self.db_output_cache.append((line_num, output))
724 741 if self.db_cache_size <= 1:
725 742 self.save_flag.set()
726 743
727 744 def _writeout_input_cache(self, conn):
728 745 with conn:
729 746 for line in self.db_input_cache:
730 747 conn.execute("INSERT INTO history VALUES (?, ?, ?, ?)",
731 748 (self.session_number,)+line)
732 749
733 750 def _writeout_output_cache(self, conn):
734 751 with conn:
735 752 for line in self.db_output_cache:
736 753 conn.execute("INSERT INTO output_history VALUES (?, ?, ?)",
737 754 (self.session_number,)+line)
738 755
739 756 @needs_sqlite
740 757 def writeout_cache(self, conn=None):
741 758 """Write any entries in the cache to the database."""
742 759 if conn is None:
743 760 conn = self.db
744 761
745 762 with self.db_input_cache_lock:
746 763 try:
747 764 self._writeout_input_cache(conn)
748 765 except sqlite3.IntegrityError:
749 766 self.new_session(conn)
750 767 print("ERROR! Session/line number was not unique in",
751 768 "database. History logging moved to new session",
752 769 self.session_number)
753 770 try:
754 771 # Try writing to the new session. If this fails, don't
755 772 # recurse
756 773 self._writeout_input_cache(conn)
757 774 except sqlite3.IntegrityError:
758 775 pass
759 776 finally:
760 777 self.db_input_cache = []
761 778
762 779 with self.db_output_cache_lock:
763 780 try:
764 781 self._writeout_output_cache(conn)
765 782 except sqlite3.IntegrityError:
766 783 print("!! Session/line number for output was not unique",
767 784 "in database. Output will not be stored.")
768 785 finally:
769 786 self.db_output_cache = []
770 787
771 788
772 789 class HistorySavingThread(threading.Thread):
773 790 """This thread takes care of writing history to the database, so that
774 791 the UI isn't held up while that happens.
775 792
776 793 It waits for the HistoryManager's save_flag to be set, then writes out
777 794 the history cache. The main thread is responsible for setting the flag when
778 795 the cache size reaches a defined threshold."""
779 796 daemon = True
780 797 stop_now = False
781 798 enabled = True
782 799 def __init__(self, history_manager):
783 800 super(HistorySavingThread, self).__init__(name="IPythonHistorySavingThread")
784 801 self.history_manager = history_manager
785 802 self.enabled = history_manager.enabled
786 803 atexit.register(self.stop)
787 804
788 805 @needs_sqlite
789 806 def run(self):
790 807 # We need a separate db connection per thread:
791 808 try:
792 809 self.db = sqlite3.connect(self.history_manager.hist_file,
793 810 **self.history_manager.connection_options
794 811 )
795 812 while True:
796 813 self.history_manager.save_flag.wait()
797 814 if self.stop_now:
798 815 self.db.close()
799 816 return
800 817 self.history_manager.save_flag.clear()
801 818 self.history_manager.writeout_cache(self.db)
802 819 except Exception as e:
803 820 print(("The history saving thread hit an unexpected error (%s)."
804 821 "History will not be written to the database.") % repr(e))
805 822
806 823 def stop(self):
807 824 """This can be called from the main thread to safely stop this thread.
808 825
809 826 Note that it does not attempt to write out remaining history before
810 827 exiting. That should be done by calling the HistoryManager's
811 828 end_session method."""
812 829 self.stop_now = True
813 830 self.history_manager.save_flag.set()
814 831 self.join()
815 832
816 833
817 834 # To match, e.g. ~5/8-~2/3
818 835 range_re = re.compile(r"""
819 836 ((?P<startsess>~?\d+)/)?
820 837 (?P<start>\d+)?
821 838 ((?P<sep>[\-:])
822 839 ((?P<endsess>~?\d+)/)?
823 840 (?P<end>\d+))?
824 841 $""", re.VERBOSE)
825 842
826 843
827 844 def extract_hist_ranges(ranges_str):
828 845 """Turn a string of history ranges into 3-tuples of (session, start, stop).
829 846
830 847 Examples
831 848 --------
832 849 >>> list(extract_hist_ranges("~8/5-~7/4 2"))
833 850 [(-8, 5, None), (-7, 1, 5), (0, 2, 3)]
834 851 """
835 852 for range_str in ranges_str.split():
836 853 rmatch = range_re.match(range_str)
837 854 if not rmatch:
838 855 continue
839 856 start = rmatch.group("start")
840 857 if start:
841 858 start = int(start)
842 859 end = rmatch.group("end")
843 860 # If no end specified, get (a, a + 1)
844 861 end = int(end) if end else start + 1
845 862 else: # start not specified
846 863 if not rmatch.group('startsess'): # no startsess
847 864 continue
848 865 start = 1
849 866 end = None # provide the entire session hist
850 867
851 868 if rmatch.group("sep") == "-": # 1-3 == 1:4 --> [1, 2, 3]
852 869 end += 1
853 870 startsess = rmatch.group("startsess") or "0"
854 871 endsess = rmatch.group("endsess") or startsess
855 872 startsess = int(startsess.replace("~","-"))
856 873 endsess = int(endsess.replace("~","-"))
857 874 assert endsess >= startsess, "start session must be earlier than end session"
858 875
859 876 if endsess == startsess:
860 877 yield (startsess, start, end)
861 878 continue
862 879 # Multiple sessions in one range:
863 880 yield (startsess, start, None)
864 881 for sess in range(startsess+1, endsess):
865 882 yield (sess, 1, None)
866 883 yield (endsess, 1, end)
867 884
868 885
869 886 def _format_lineno(session, line):
870 887 """Helper function to format line numbers properly."""
871 888 if session == 0:
872 889 return str(line)
873 890 return "%s#%s" % (session, line)
General Comments 0
You need to be logged in to leave comments. Login now