##// END OF EJS Templates
allow setting HistoryManager.hist_file with config...
MinRK -
Show More
@@ -1,948 +1,966 b''
1 1 """ History related magics and functionality """
2 2 #-----------------------------------------------------------------------------
3 3 # Copyright (C) 2010 The IPython Development Team.
4 4 #
5 5 # Distributed under the terms of the BSD License.
6 6 #
7 7 # The full license is in the file COPYING.txt, distributed with this software.
8 8 #-----------------------------------------------------------------------------
9 9
10 10 #-----------------------------------------------------------------------------
11 11 # Imports
12 12 #-----------------------------------------------------------------------------
13 13 from __future__ import print_function
14 14
15 15 # Stdlib imports
16 16 import atexit
17 17 import datetime
18 18 import os
19 19 import re
20 20 try:
21 21 import sqlite3
22 22 except ImportError:
23 23 sqlite3 = None
24 24 import threading
25 25
26 26 # Our own packages
27 27 from IPython.config.configurable import Configurable
28 28 from IPython.external.decorator import decorator
29 29 from IPython.testing.skipdoctest import skip_doctest
30 30 from IPython.utils import io
31 31 from IPython.utils.path import locate_profile
32 32 from IPython.utils.traitlets import Bool, Dict, Instance, Int, CInt, List, Unicode
33 33 from IPython.utils.warn import warn
34 34
35 35 #-----------------------------------------------------------------------------
36 36 # Classes and functions
37 37 #-----------------------------------------------------------------------------
38 38
39 39 class DummyDB(object):
40 40 """Dummy DB that will act as a black hole for history.
41 41
42 42 Only used in the absence of sqlite"""
43 43 def execute(*args, **kwargs):
44 44 return []
45 45
46 46 def commit(self, *args, **kwargs):
47 47 pass
48 48
49 49 def __enter__(self, *args, **kwargs):
50 50 pass
51 51
52 52 def __exit__(self, *args, **kwargs):
53 53 pass
54 54
55 55 @decorator
56 56 def needs_sqlite(f,*a,**kw):
57 57 """return an empty list in the absence of sqlite"""
58 58 if sqlite3 is None:
59 59 return []
60 60 else:
61 61 return f(*a,**kw)
62 62
63 63 class HistoryAccessor(Configurable):
64 64 """Access the history database without adding to it.
65 65
66 66 This is intended for use by standalone history tools. IPython shells use
67 67 HistoryManager, below, which is a subclass of this."""
68
68 69 # String holding the path to the history file
69 hist_file = Unicode(config=True)
70 hist_file = Unicode(config=True,
71 help="""Path to file to use for SQLite history database.
72
73 By default, IPython will put the history database in the IPython profile
74 directory. If you would rather share one history among profiles,
75 you ca set this value in each, so that they are consistent.
76
77 Due to an issue with fcntl, SQLite is known to misbehave on some NFS mounts.
78 If you see IPython hanging, try setting this to something on a local disk,
79 e.g::
80
81 ipython --HistoryManager.hist_file=/tmp/ipython_hist.sqlite
82
83 """)
84
70 85
71 86 # The SQLite database
72 87 if sqlite3:
73 88 db = Instance(sqlite3.Connection)
74 89 else:
75 90 db = Instance(DummyDB)
76 91
77 def __init__(self, profile='default', hist_file=u'', shell=None, config=None, **traits):
92 def __init__(self, profile='default', hist_file=u'', config=None, **traits):
78 93 """Create a new history accessor.
79 94
80 95 Parameters
81 96 ----------
82 97 profile : str
83 98 The name of the profile from which to open history.
84 99 hist_file : str
85 100 Path to an SQLite history database stored by IPython. If specified,
86 101 hist_file overrides profile.
87 shell :
88 InteractiveShell object, for use by HistoryManager subclass
89 102 config :
90 103 Config object. hist_file can also be set through this.
91 104 """
92 105 # We need a pointer back to the shell for various tasks.
93 super(HistoryAccessor, self).__init__(shell=shell, config=config,
94 hist_file=hist_file, **traits)
95
106 super(HistoryAccessor, self).__init__(config=config, **traits)
107 # defer setting hist_file from kwarg until after init,
108 # otherwise the default kwarg value would clobber any value
109 # set by config
110 if hist_file:
111 self.hist_file = hist_file
112
96 113 if self.hist_file == u'':
97 114 # No one has set the hist_file, yet.
98 115 self.hist_file = self._get_hist_file_name(profile)
99 116
100 117 if sqlite3 is None:
101 118 warn("IPython History requires SQLite, your history will not be saved\n")
102 119 self.db = DummyDB()
103 120 return
104 121
105 122 try:
106 123 self.init_db()
107 124 except sqlite3.DatabaseError:
108 125 if os.path.isfile(self.hist_file):
109 # Try to move the file out of the way.
110 newpath = os.path.join(self.shell.profile_dir.location, "hist-corrupt.sqlite")
126 # Try to move the file out of the way
127 base,ext = os.path.splitext(self.hist_file)
128 newpath = base + '-corrupt' + ext
111 129 os.rename(self.hist_file, newpath)
112 130 print("ERROR! History file wasn't a valid SQLite database.",
113 131 "It was moved to %s" % newpath, "and a new file created.")
114 132 self.init_db()
115 133 else:
116 134 # The hist_file is probably :memory: or something else.
117 135 raise
118 136
119 137 def _get_hist_file_name(self, profile='default'):
120 138 """Find the history file for the given profile name.
121 139
122 140 This is overridden by the HistoryManager subclass, to use the shell's
123 141 active profile.
124 142
125 143 Parameters
126 144 ----------
127 145 profile : str
128 146 The name of a profile which has a history file.
129 147 """
130 148 return os.path.join(locate_profile(profile), 'history.sqlite')
131 149
132 150 def init_db(self):
133 151 """Connect to the database, and create tables if necessary."""
134 152 # use detect_types so that timestamps return datetime objects
135 153 self.db = sqlite3.connect(self.hist_file, detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)
136 154 self.db.execute("""CREATE TABLE IF NOT EXISTS sessions (session integer
137 155 primary key autoincrement, start timestamp,
138 156 end timestamp, num_cmds integer, remark text)""")
139 157 self.db.execute("""CREATE TABLE IF NOT EXISTS history
140 158 (session integer, line integer, source text, source_raw text,
141 159 PRIMARY KEY (session, line))""")
142 160 # Output history is optional, but ensure the table's there so it can be
143 161 # enabled later.
144 162 self.db.execute("""CREATE TABLE IF NOT EXISTS output_history
145 163 (session integer, line integer, output text,
146 164 PRIMARY KEY (session, line))""")
147 165 self.db.commit()
148 166
149 167 def writeout_cache(self):
150 168 """Overridden by HistoryManager to dump the cache before certain
151 169 database lookups."""
152 170 pass
153 171
154 172 ## -------------------------------
155 173 ## Methods for retrieving history:
156 174 ## -------------------------------
157 175 def _run_sql(self, sql, params, raw=True, output=False):
158 176 """Prepares and runs an SQL query for the history database.
159 177
160 178 Parameters
161 179 ----------
162 180 sql : str
163 181 Any filtering expressions to go after SELECT ... FROM ...
164 182 params : tuple
165 183 Parameters passed to the SQL query (to replace "?")
166 184 raw, output : bool
167 185 See :meth:`get_range`
168 186
169 187 Returns
170 188 -------
171 189 Tuples as :meth:`get_range`
172 190 """
173 191 toget = 'source_raw' if raw else 'source'
174 192 sqlfrom = "history"
175 193 if output:
176 194 sqlfrom = "history LEFT JOIN output_history USING (session, line)"
177 195 toget = "history.%s, output_history.output" % toget
178 196 cur = self.db.execute("SELECT session, line, %s FROM %s " %\
179 197 (toget, sqlfrom) + sql, params)
180 198 if output: # Regroup into 3-tuples, and parse JSON
181 199 return ((ses, lin, (inp, out)) for ses, lin, inp, out in cur)
182 200 return cur
183 201
184 202 @needs_sqlite
185 203 def get_session_info(self, session=0):
186 204 """get info about a session
187 205
188 206 Parameters
189 207 ----------
190 208
191 209 session : int
192 210 Session number to retrieve. The current session is 0, and negative
193 211 numbers count back from current session, so -1 is previous session.
194 212
195 213 Returns
196 214 -------
197 215
198 216 (session_id [int], start [datetime], end [datetime], num_cmds [int], remark [unicode])
199 217
200 218 Sessions that are running or did not exit cleanly will have `end=None`
201 219 and `num_cmds=None`.
202 220
203 221 """
204 222
205 223 if session <= 0:
206 224 session += self.session_number
207 225
208 226 query = "SELECT * from sessions where session == ?"
209 227 return self.db.execute(query, (session,)).fetchone()
210 228
211 229 def get_tail(self, n=10, raw=True, output=False, include_latest=False):
212 230 """Get the last n lines from the history database.
213 231
214 232 Parameters
215 233 ----------
216 234 n : int
217 235 The number of lines to get
218 236 raw, output : bool
219 237 See :meth:`get_range`
220 238 include_latest : bool
221 239 If False (default), n+1 lines are fetched, and the latest one
222 240 is discarded. This is intended to be used where the function
223 241 is called by a user command, which it should not return.
224 242
225 243 Returns
226 244 -------
227 245 Tuples as :meth:`get_range`
228 246 """
229 247 self.writeout_cache()
230 248 if not include_latest:
231 249 n += 1
232 250 cur = self._run_sql("ORDER BY session DESC, line DESC LIMIT ?",
233 251 (n,), raw=raw, output=output)
234 252 if not include_latest:
235 253 return reversed(list(cur)[1:])
236 254 return reversed(list(cur))
237 255
238 256 def search(self, pattern="*", raw=True, search_raw=True,
239 257 output=False):
240 258 """Search the database using unix glob-style matching (wildcards
241 259 * and ?).
242 260
243 261 Parameters
244 262 ----------
245 263 pattern : str
246 264 The wildcarded pattern to match when searching
247 265 search_raw : bool
248 266 If True, search the raw input, otherwise, the parsed input
249 267 raw, output : bool
250 268 See :meth:`get_range`
251 269
252 270 Returns
253 271 -------
254 272 Tuples as :meth:`get_range`
255 273 """
256 274 tosearch = "source_raw" if search_raw else "source"
257 275 if output:
258 276 tosearch = "history." + tosearch
259 277 self.writeout_cache()
260 278 return self._run_sql("WHERE %s GLOB ?" % tosearch, (pattern,),
261 279 raw=raw, output=output)
262 280
263 281 def get_range(self, session, start=1, stop=None, raw=True,output=False):
264 282 """Retrieve input by session.
265 283
266 284 Parameters
267 285 ----------
268 286 session : int
269 287 Session number to retrieve.
270 288 start : int
271 289 First line to retrieve.
272 290 stop : int
273 291 End of line range (excluded from output itself). If None, retrieve
274 292 to the end of the session.
275 293 raw : bool
276 294 If True, return untranslated input
277 295 output : bool
278 296 If True, attempt to include output. This will be 'real' Python
279 297 objects for the current session, or text reprs from previous
280 298 sessions if db_log_output was enabled at the time. Where no output
281 299 is found, None is used.
282 300
283 301 Returns
284 302 -------
285 303 An iterator over the desired lines. Each line is a 3-tuple, either
286 304 (session, line, input) if output is False, or
287 305 (session, line, (input, output)) if output is True.
288 306 """
289 307 if stop:
290 308 lineclause = "line >= ? AND line < ?"
291 309 params = (session, start, stop)
292 310 else:
293 311 lineclause = "line>=?"
294 312 params = (session, start)
295 313
296 314 return self._run_sql("WHERE session==? AND %s""" % lineclause,
297 315 params, raw=raw, output=output)
298 316
299 317 def get_range_by_str(self, rangestr, raw=True, output=False):
300 318 """Get lines of history from a string of ranges, as used by magic
301 319 commands %hist, %save, %macro, etc.
302 320
303 321 Parameters
304 322 ----------
305 323 rangestr : str
306 324 A string specifying ranges, e.g. "5 ~2/1-4". See
307 325 :func:`magic_history` for full details.
308 326 raw, output : bool
309 327 As :meth:`get_range`
310 328
311 329 Returns
312 330 -------
313 331 Tuples as :meth:`get_range`
314 332 """
315 333 for sess, s, e in extract_hist_ranges(rangestr):
316 334 for line in self.get_range(sess, s, e, raw=raw, output=output):
317 335 yield line
318 336
319 337
320 338 class HistoryManager(HistoryAccessor):
321 339 """A class to organize all history-related functionality in one place.
322 340 """
323 341 # Public interface
324 342
325 343 # An instance of the IPython shell we are attached to
326 344 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
327 345 # Lists to hold processed and raw history. These start with a blank entry
328 346 # so that we can index them starting from 1
329 347 input_hist_parsed = List([""])
330 348 input_hist_raw = List([""])
331 349 # A list of directories visited during session
332 350 dir_hist = List()
333 351 def _dir_hist_default(self):
334 352 try:
335 353 return [os.getcwdu()]
336 354 except OSError:
337 355 return []
338 356
339 357 # A dict of output history, keyed with ints from the shell's
340 358 # execution count.
341 359 output_hist = Dict()
342 360 # The text/plain repr of outputs.
343 361 output_hist_reprs = Dict()
344 362
345 363 # The number of the current session in the history database
346 364 session_number = CInt()
347 365 # Should we log output to the database? (default no)
348 366 db_log_output = Bool(False, config=True)
349 367 # Write to database every x commands (higher values save disk access & power)
350 368 # Values of 1 or less effectively disable caching.
351 369 db_cache_size = Int(0, config=True)
352 370 # The input and output caches
353 371 db_input_cache = List()
354 372 db_output_cache = List()
355 373
356 374 # History saving in separate thread
357 375 save_thread = Instance('IPython.core.history.HistorySavingThread')
358 376 try: # Event is a function returning an instance of _Event...
359 377 save_flag = Instance(threading._Event)
360 378 except AttributeError: # ...until Python 3.3, when it's a class.
361 379 save_flag = Instance(threading.Event)
362 380
363 381 # Private interface
364 382 # Variables used to store the three last inputs from the user. On each new
365 383 # history update, we populate the user's namespace with these, shifted as
366 384 # necessary.
367 385 _i00 = Unicode(u'')
368 386 _i = Unicode(u'')
369 387 _ii = Unicode(u'')
370 388 _iii = Unicode(u'')
371 389
372 390 # A regex matching all forms of the exit command, so that we don't store
373 391 # them in the history (it's annoying to rewind the first entry and land on
374 392 # an exit call).
375 393 _exit_re = re.compile(r"(exit|quit)(\s*\(.*\))?$")
376 394
377 395 def __init__(self, shell=None, config=None, **traits):
378 396 """Create a new history manager associated with a shell instance.
379 397 """
380 398 # We need a pointer back to the shell for various tasks.
381 399 super(HistoryManager, self).__init__(shell=shell, config=config,
382 400 **traits)
383 401 self.save_flag = threading.Event()
384 402 self.db_input_cache_lock = threading.Lock()
385 403 self.db_output_cache_lock = threading.Lock()
386 404 self.save_thread = HistorySavingThread(self)
387 405 self.save_thread.start()
388 406
389 407 self.new_session()
390 408
391 409 def _get_hist_file_name(self, profile=None):
392 410 """Get default history file name based on the Shell's profile.
393 411
394 412 The profile parameter is ignored, but must exist for compatibility with
395 413 the parent class."""
396 414 profile_dir = self.shell.profile_dir.location
397 415 return os.path.join(profile_dir, 'history.sqlite')
398 416
399 417 @needs_sqlite
400 418 def new_session(self, conn=None):
401 419 """Get a new session number."""
402 420 if conn is None:
403 421 conn = self.db
404 422
405 423 with conn:
406 424 cur = conn.execute("""INSERT INTO sessions VALUES (NULL, ?, NULL,
407 425 NULL, "") """, (datetime.datetime.now(),))
408 426 self.session_number = cur.lastrowid
409 427
410 428 def end_session(self):
411 429 """Close the database session, filling in the end time and line count."""
412 430 self.writeout_cache()
413 431 with self.db:
414 432 self.db.execute("""UPDATE sessions SET end=?, num_cmds=? WHERE
415 433 session==?""", (datetime.datetime.now(),
416 434 len(self.input_hist_parsed)-1, self.session_number))
417 435 self.session_number = 0
418 436
419 437 def name_session(self, name):
420 438 """Give the current session a name in the history database."""
421 439 with self.db:
422 440 self.db.execute("UPDATE sessions SET remark=? WHERE session==?",
423 441 (name, self.session_number))
424 442
425 443 def reset(self, new_session=True):
426 444 """Clear the session history, releasing all object references, and
427 445 optionally open a new session."""
428 446 self.output_hist.clear()
429 447 # The directory history can't be completely empty
430 448 self.dir_hist[:] = [os.getcwdu()]
431 449
432 450 if new_session:
433 451 if self.session_number:
434 452 self.end_session()
435 453 self.input_hist_parsed[:] = [""]
436 454 self.input_hist_raw[:] = [""]
437 455 self.new_session()
438 456
439 457 # ------------------------------
440 458 # Methods for retrieving history
441 459 # ------------------------------
442 460 def _get_range_session(self, start=1, stop=None, raw=True, output=False):
443 461 """Get input and output history from the current session. Called by
444 462 get_range, and takes similar parameters."""
445 463 input_hist = self.input_hist_raw if raw else self.input_hist_parsed
446 464
447 465 n = len(input_hist)
448 466 if start < 0:
449 467 start += n
450 468 if not stop or (stop > n):
451 469 stop = n
452 470 elif stop < 0:
453 471 stop += n
454 472
455 473 for i in range(start, stop):
456 474 if output:
457 475 line = (input_hist[i], self.output_hist_reprs.get(i))
458 476 else:
459 477 line = input_hist[i]
460 478 yield (0, i, line)
461 479
462 480 def get_range(self, session=0, start=1, stop=None, raw=True,output=False):
463 481 """Retrieve input by session.
464 482
465 483 Parameters
466 484 ----------
467 485 session : int
468 486 Session number to retrieve. The current session is 0, and negative
469 487 numbers count back from current session, so -1 is previous session.
470 488 start : int
471 489 First line to retrieve.
472 490 stop : int
473 491 End of line range (excluded from output itself). If None, retrieve
474 492 to the end of the session.
475 493 raw : bool
476 494 If True, return untranslated input
477 495 output : bool
478 496 If True, attempt to include output. This will be 'real' Python
479 497 objects for the current session, or text reprs from previous
480 498 sessions if db_log_output was enabled at the time. Where no output
481 499 is found, None is used.
482 500
483 501 Returns
484 502 -------
485 503 An iterator over the desired lines. Each line is a 3-tuple, either
486 504 (session, line, input) if output is False, or
487 505 (session, line, (input, output)) if output is True.
488 506 """
489 507 if session <= 0:
490 508 session += self.session_number
491 509 if session==self.session_number: # Current session
492 510 return self._get_range_session(start, stop, raw, output)
493 511 return super(HistoryManager, self).get_range(session, start, stop, raw, output)
494 512
495 513 ## ----------------------------
496 514 ## Methods for storing history:
497 515 ## ----------------------------
498 516 def store_inputs(self, line_num, source, source_raw=None):
499 517 """Store source and raw input in history and create input cache
500 518 variables _i*.
501 519
502 520 Parameters
503 521 ----------
504 522 line_num : int
505 523 The prompt number of this input.
506 524
507 525 source : str
508 526 Python input.
509 527
510 528 source_raw : str, optional
511 529 If given, this is the raw input without any IPython transformations
512 530 applied to it. If not given, ``source`` is used.
513 531 """
514 532 if source_raw is None:
515 533 source_raw = source
516 534 source = source.rstrip('\n')
517 535 source_raw = source_raw.rstrip('\n')
518 536
519 537 # do not store exit/quit commands
520 538 if self._exit_re.match(source_raw.strip()):
521 539 return
522 540
523 541 self.input_hist_parsed.append(source)
524 542 self.input_hist_raw.append(source_raw)
525 543
526 544 with self.db_input_cache_lock:
527 545 self.db_input_cache.append((line_num, source, source_raw))
528 546 # Trigger to flush cache and write to DB.
529 547 if len(self.db_input_cache) >= self.db_cache_size:
530 548 self.save_flag.set()
531 549
532 550 # update the auto _i variables
533 551 self._iii = self._ii
534 552 self._ii = self._i
535 553 self._i = self._i00
536 554 self._i00 = source_raw
537 555
538 556 # hackish access to user namespace to create _i1,_i2... dynamically
539 557 new_i = '_i%s' % line_num
540 558 to_main = {'_i': self._i,
541 559 '_ii': self._ii,
542 560 '_iii': self._iii,
543 561 new_i : self._i00 }
544 562 self.shell.user_ns.update(to_main)
545 563
546 564 def store_output(self, line_num):
547 565 """If database output logging is enabled, this saves all the
548 566 outputs from the indicated prompt number to the database. It's
549 567 called by run_cell after code has been executed.
550 568
551 569 Parameters
552 570 ----------
553 571 line_num : int
554 572 The line number from which to save outputs
555 573 """
556 574 if (not self.db_log_output) or (line_num not in self.output_hist_reprs):
557 575 return
558 576 output = self.output_hist_reprs[line_num]
559 577
560 578 with self.db_output_cache_lock:
561 579 self.db_output_cache.append((line_num, output))
562 580 if self.db_cache_size <= 1:
563 581 self.save_flag.set()
564 582
565 583 def _writeout_input_cache(self, conn):
566 584 with conn:
567 585 for line in self.db_input_cache:
568 586 conn.execute("INSERT INTO history VALUES (?, ?, ?, ?)",
569 587 (self.session_number,)+line)
570 588
571 589 def _writeout_output_cache(self, conn):
572 590 with conn:
573 591 for line in self.db_output_cache:
574 592 conn.execute("INSERT INTO output_history VALUES (?, ?, ?)",
575 593 (self.session_number,)+line)
576 594
577 595 @needs_sqlite
578 596 def writeout_cache(self, conn=None):
579 597 """Write any entries in the cache to the database."""
580 598 if conn is None:
581 599 conn = self.db
582 600
583 601 with self.db_input_cache_lock:
584 602 try:
585 603 self._writeout_input_cache(conn)
586 604 except sqlite3.IntegrityError:
587 605 self.new_session(conn)
588 606 print("ERROR! Session/line number was not unique in",
589 607 "database. History logging moved to new session",
590 608 self.session_number)
591 609 try: # Try writing to the new session. If this fails, don't recurse
592 610 self._writeout_input_cache(conn)
593 611 except sqlite3.IntegrityError:
594 612 pass
595 613 finally:
596 614 self.db_input_cache = []
597 615
598 616 with self.db_output_cache_lock:
599 617 try:
600 618 self._writeout_output_cache(conn)
601 619 except sqlite3.IntegrityError:
602 620 print("!! Session/line number for output was not unique",
603 621 "in database. Output will not be stored.")
604 622 finally:
605 623 self.db_output_cache = []
606 624
607 625
608 626 class HistorySavingThread(threading.Thread):
609 627 """This thread takes care of writing history to the database, so that
610 628 the UI isn't held up while that happens.
611 629
612 630 It waits for the HistoryManager's save_flag to be set, then writes out
613 631 the history cache. The main thread is responsible for setting the flag when
614 632 the cache size reaches a defined threshold."""
615 633 daemon = True
616 634 stop_now = False
617 635 def __init__(self, history_manager):
618 636 super(HistorySavingThread, self).__init__()
619 637 self.history_manager = history_manager
620 638 atexit.register(self.stop)
621 639
622 640 @needs_sqlite
623 641 def run(self):
624 642 # We need a separate db connection per thread:
625 643 try:
626 644 self.db = sqlite3.connect(self.history_manager.hist_file)
627 645 while True:
628 646 self.history_manager.save_flag.wait()
629 647 if self.stop_now:
630 648 return
631 649 self.history_manager.save_flag.clear()
632 650 self.history_manager.writeout_cache(self.db)
633 651 except Exception as e:
634 652 print(("The history saving thread hit an unexpected error (%s)."
635 653 "History will not be written to the database.") % repr(e))
636 654
637 655 def stop(self):
638 656 """This can be called from the main thread to safely stop this thread.
639 657
640 658 Note that it does not attempt to write out remaining history before
641 659 exiting. That should be done by calling the HistoryManager's
642 660 end_session method."""
643 661 self.stop_now = True
644 662 self.history_manager.save_flag.set()
645 663 self.join()
646 664
647 665
648 666 # To match, e.g. ~5/8-~2/3
649 667 range_re = re.compile(r"""
650 668 ((?P<startsess>~?\d+)/)?
651 669 (?P<start>\d+) # Only the start line num is compulsory
652 670 ((?P<sep>[\-:])
653 671 ((?P<endsess>~?\d+)/)?
654 672 (?P<end>\d+))?
655 673 $""", re.VERBOSE)
656 674
657 675 def extract_hist_ranges(ranges_str):
658 676 """Turn a string of history ranges into 3-tuples of (session, start, stop).
659 677
660 678 Examples
661 679 --------
662 680 list(extract_input_ranges("~8/5-~7/4 2"))
663 681 [(-8, 5, None), (-7, 1, 4), (0, 2, 3)]
664 682 """
665 683 for range_str in ranges_str.split():
666 684 rmatch = range_re.match(range_str)
667 685 if not rmatch:
668 686 continue
669 687 start = int(rmatch.group("start"))
670 688 end = rmatch.group("end")
671 689 end = int(end) if end else start+1 # If no end specified, get (a, a+1)
672 690 if rmatch.group("sep") == "-": # 1-3 == 1:4 --> [1, 2, 3]
673 691 end += 1
674 692 startsess = rmatch.group("startsess") or "0"
675 693 endsess = rmatch.group("endsess") or startsess
676 694 startsess = int(startsess.replace("~","-"))
677 695 endsess = int(endsess.replace("~","-"))
678 696 assert endsess >= startsess
679 697
680 698 if endsess == startsess:
681 699 yield (startsess, start, end)
682 700 continue
683 701 # Multiple sessions in one range:
684 702 yield (startsess, start, None)
685 703 for sess in range(startsess+1, endsess):
686 704 yield (sess, 1, None)
687 705 yield (endsess, 1, end)
688 706
689 707 def _format_lineno(session, line):
690 708 """Helper function to format line numbers properly."""
691 709 if session == 0:
692 710 return str(line)
693 711 return "%s#%s" % (session, line)
694 712
695 713 @skip_doctest
696 714 def magic_history(self, parameter_s = ''):
697 715 """Print input history (_i<n> variables), with most recent last.
698 716
699 717 %history -> print at most 40 inputs (some may be multi-line)\\
700 718 %history n -> print at most n inputs\\
701 719 %history n1 n2 -> print inputs between n1 and n2 (n2 not included)\\
702 720
703 721 By default, input history is printed without line numbers so it can be
704 722 directly pasted into an editor. Use -n to show them.
705 723
706 724 Ranges of history can be indicated using the syntax:
707 725 4 : Line 4, current session
708 726 4-6 : Lines 4-6, current session
709 727 243/1-5: Lines 1-5, session 243
710 728 ~2/7 : Line 7, session 2 before current
711 729 ~8/1-~6/5 : From the first line of 8 sessions ago, to the fifth line
712 730 of 6 sessions ago.
713 731 Multiple ranges can be entered, separated by spaces
714 732
715 733 The same syntax is used by %macro, %save, %edit, %rerun
716 734
717 735 Options:
718 736
719 737 -n: print line numbers for each input.
720 738 This feature is only available if numbered prompts are in use.
721 739
722 740 -o: also print outputs for each input.
723 741
724 742 -p: print classic '>>>' python prompts before each input. This is useful
725 743 for making documentation, and in conjunction with -o, for producing
726 744 doctest-ready output.
727 745
728 746 -r: (default) print the 'raw' history, i.e. the actual commands you typed.
729 747
730 748 -t: print the 'translated' history, as IPython understands it. IPython
731 749 filters your input and converts it all into valid Python source before
732 750 executing it (things like magics or aliases are turned into function
733 751 calls, for example). With this option, you'll see the native history
734 752 instead of the user-entered version: '%cd /' will be seen as
735 753 'get_ipython().magic("%cd /")' instead of '%cd /'.
736 754
737 755 -g: treat the arg as a pattern to grep for in (full) history.
738 756 This includes the saved history (almost all commands ever written).
739 757 Use '%hist -g' to show full saved history (may be very long).
740 758
741 759 -l: get the last n lines from all sessions. Specify n as a single arg, or
742 760 the default is the last 10 lines.
743 761
744 762 -f FILENAME: instead of printing the output to the screen, redirect it to
745 763 the given file. The file is always overwritten, though IPython asks for
746 764 confirmation first if it already exists.
747 765
748 766 Examples
749 767 --------
750 768 ::
751 769
752 770 In [6]: %hist -n 4 6
753 771 4:a = 12
754 772 5:print a**2
755 773
756 774 """
757 775
758 776 if not self.shell.displayhook.do_full_cache:
759 777 print('This feature is only available if numbered prompts are in use.')
760 778 return
761 779 opts,args = self.parse_options(parameter_s,'noprtglf:',mode='string')
762 780
763 781 # For brevity
764 782 history_manager = self.shell.history_manager
765 783
766 784 def _format_lineno(session, line):
767 785 """Helper function to format line numbers properly."""
768 786 if session in (0, history_manager.session_number):
769 787 return str(line)
770 788 return "%s/%s" % (session, line)
771 789
772 790 # Check if output to specific file was requested.
773 791 try:
774 792 outfname = opts['f']
775 793 except KeyError:
776 794 outfile = io.stdout # default
777 795 # We don't want to close stdout at the end!
778 796 close_at_end = False
779 797 else:
780 798 if os.path.exists(outfname):
781 799 if not io.ask_yes_no("File %r exists. Overwrite?" % outfname):
782 800 print('Aborting.')
783 801 return
784 802
785 803 outfile = open(outfname,'w')
786 804 close_at_end = True
787 805
788 806 print_nums = 'n' in opts
789 807 get_output = 'o' in opts
790 808 pyprompts = 'p' in opts
791 809 # Raw history is the default
792 810 raw = not('t' in opts)
793 811
794 812 default_length = 40
795 813 pattern = None
796 814
797 815 if 'g' in opts: # Glob search
798 816 pattern = "*" + args + "*" if args else "*"
799 817 hist = history_manager.search(pattern, raw=raw, output=get_output)
800 818 print_nums = True
801 819 elif 'l' in opts: # Get 'tail'
802 820 try:
803 821 n = int(args)
804 822 except ValueError, IndexError:
805 823 n = 10
806 824 hist = history_manager.get_tail(n, raw=raw, output=get_output)
807 825 else:
808 826 if args: # Get history by ranges
809 827 hist = history_manager.get_range_by_str(args, raw, get_output)
810 828 else: # Just get history for the current session
811 829 hist = history_manager.get_range(raw=raw, output=get_output)
812 830
813 831 # We could be displaying the entire history, so let's not try to pull it
814 832 # into a list in memory. Anything that needs more space will just misalign.
815 833 width = 4
816 834
817 835 for session, lineno, inline in hist:
818 836 # Print user history with tabs expanded to 4 spaces. The GUI clients
819 837 # use hard tabs for easier usability in auto-indented code, but we want
820 838 # to produce PEP-8 compliant history for safe pasting into an editor.
821 839 if get_output:
822 840 inline, output = inline
823 841 inline = inline.expandtabs(4).rstrip()
824 842
825 843 multiline = "\n" in inline
826 844 line_sep = '\n' if multiline else ' '
827 845 if print_nums:
828 846 print('%s:%s' % (_format_lineno(session, lineno).rjust(width),
829 847 line_sep), file=outfile, end='')
830 848 if pyprompts:
831 849 print(">>> ", end="", file=outfile)
832 850 if multiline:
833 851 inline = "\n... ".join(inline.splitlines()) + "\n..."
834 852 print(inline, file=outfile)
835 853 if get_output and output:
836 854 print(output, file=outfile)
837 855
838 856 if close_at_end:
839 857 outfile.close()
840 858
841 859
842 860 def magic_rep(self, arg):
843 861 r"""Repeat a command, or get command to input line for editing. %recall and
844 862 %rep are equivalent.
845 863
846 864 - %recall (no arguments):
847 865
848 866 Place a string version of last computation result (stored in the special '_'
849 867 variable) to the next input prompt. Allows you to create elaborate command
850 868 lines without using copy-paste::
851 869
852 870 In[1]: l = ["hei", "vaan"]
853 871 In[2]: "".join(l)
854 872 Out[2]: heivaan
855 873 In[3]: %rep
856 874 In[4]: heivaan_ <== cursor blinking
857 875
858 876 %recall 45
859 877
860 878 Place history line 45 on the next input prompt. Use %hist to find
861 879 out the number.
862 880
863 881 %recall 1-4
864 882
865 883 Combine the specified lines into one cell, and place it on the next
866 884 input prompt. See %history for the slice syntax.
867 885
868 886 %recall foo+bar
869 887
870 888 If foo+bar can be evaluated in the user namespace, the result is
871 889 placed at the next input prompt. Otherwise, the history is searched
872 890 for lines which contain that substring, and the most recent one is
873 891 placed at the next input prompt.
874 892 """
875 893 if not arg: # Last output
876 894 self.set_next_input(str(self.shell.user_ns["_"]))
877 895 return
878 896 # Get history range
879 897 histlines = self.history_manager.get_range_by_str(arg)
880 898 cmd = "\n".join(x[2] for x in histlines)
881 899 if cmd:
882 900 self.set_next_input(cmd.rstrip())
883 901 return
884 902
885 903 try: # Variable in user namespace
886 904 cmd = str(eval(arg, self.shell.user_ns))
887 905 except Exception: # Search for term in history
888 906 histlines = self.history_manager.search("*"+arg+"*")
889 907 for h in reversed([x[2] for x in histlines]):
890 908 if 'rep' in h:
891 909 continue
892 910 self.set_next_input(h.rstrip())
893 911 return
894 912 else:
895 913 self.set_next_input(cmd.rstrip())
896 914 print("Couldn't evaluate or find in history:", arg)
897 915
898 916 def magic_rerun(self, parameter_s=''):
899 917 """Re-run previous input
900 918
901 919 By default, you can specify ranges of input history to be repeated
902 920 (as with %history). With no arguments, it will repeat the last line.
903 921
904 922 Options:
905 923
906 924 -l <n> : Repeat the last n lines of input, not including the
907 925 current command.
908 926
909 927 -g foo : Repeat the most recent line which contains foo
910 928 """
911 929 opts, args = self.parse_options(parameter_s, 'l:g:', mode='string')
912 930 if "l" in opts: # Last n lines
913 931 n = int(opts['l'])
914 932 hist = self.history_manager.get_tail(n)
915 933 elif "g" in opts: # Search
916 934 p = "*"+opts['g']+"*"
917 935 hist = list(self.history_manager.search(p))
918 936 for l in reversed(hist):
919 937 if "rerun" not in l[2]:
920 938 hist = [l] # The last match which isn't a %rerun
921 939 break
922 940 else:
923 941 hist = [] # No matches except %rerun
924 942 elif args: # Specify history ranges
925 943 hist = self.history_manager.get_range_by_str(args)
926 944 else: # Last line
927 945 hist = self.history_manager.get_tail(1)
928 946 hist = [x[2] for x in hist]
929 947 if not hist:
930 948 print("No lines in history match specification")
931 949 return
932 950 histlines = "\n".join(hist)
933 951 print("=== Executing: ===")
934 952 print(histlines)
935 953 print("=== Output: ===")
936 954 self.run_cell("\n".join(hist), store_history=False)
937 955
938 956
939 957 def init_ipython(ip):
940 958 ip.define_magic("rep", magic_rep)
941 959 ip.define_magic("recall", magic_rep)
942 960 ip.define_magic("rerun", magic_rerun)
943 961 ip.define_magic("hist",magic_history) # Alternative name
944 962 ip.define_magic("history",magic_history)
945 963
946 964 # XXX - ipy_completers are in quarantine, need to be updated to new apis
947 965 #import ipy_completers
948 966 #ipy_completers.quick_completer('%hist' ,'-g -t -r -n')
@@ -1,124 +1,134 b''
1 1 # coding: utf-8
2 2 """Tests for the IPython tab-completion machinery.
3 3 """
4 4 #-----------------------------------------------------------------------------
5 5 # Module imports
6 6 #-----------------------------------------------------------------------------
7 7
8 8 # stdlib
9 9 import os
10 10 import sys
11 import tempfile
11 12 import unittest
12 13 from datetime import datetime
14
13 15 # third party
14 16 import nose.tools as nt
15 17
16 18 # our own packages
19 from IPython.config.loader import Config
17 20 from IPython.utils.tempdir import TemporaryDirectory
18 21 from IPython.core.history import HistoryManager, extract_hist_ranges
19 22 from IPython.utils import py3compat
20 23
21 24 def setUp():
22 25 nt.assert_equal(sys.getdefaultencoding(), "utf-8" if py3compat.PY3 else "ascii")
23 26
24 27 def test_history():
25 28 ip = get_ipython()
26 29 with TemporaryDirectory() as tmpdir:
27 30 hist_manager_ori = ip.history_manager
28 31 hist_file = os.path.join(tmpdir, 'history.sqlite')
29 32 try:
30 33 ip.history_manager = HistoryManager(shell=ip, hist_file=hist_file)
31 34 hist = ['a=1', 'def f():\n test = 1\n return test', u"b='β‚¬Γ†ΒΎΓ·ΓŸ'"]
32 35 for i, h in enumerate(hist, start=1):
33 36 ip.history_manager.store_inputs(i, h)
34 37
35 38 ip.history_manager.db_log_output = True
36 39 # Doesn't match the input, but we'll just check it's stored.
37 40 ip.history_manager.output_hist_reprs[3] = "spam"
38 41 ip.history_manager.store_output(3)
39 42
40 43 nt.assert_equal(ip.history_manager.input_hist_raw, [''] + hist)
41 44
42 45 # Detailed tests for _get_range_session
43 46 grs = ip.history_manager._get_range_session
44 47 nt.assert_equal(list(grs(start=2,stop=-1)), zip([0], [2], hist[1:-1]))
45 48 nt.assert_equal(list(grs(start=-2)), zip([0,0], [2,3], hist[-2:]))
46 49 nt.assert_equal(list(grs(output=True)), zip([0,0,0], [1,2,3], zip(hist, [None,None,'spam'])))
47 50
48 51 # Check whether specifying a range beyond the end of the current
49 52 # session results in an error (gh-804)
50 53 ip.magic('%hist 2-500')
51 54
52 55 # New session
53 56 ip.history_manager.reset()
54 57 newcmds = ["z=5","class X(object):\n pass", "k='p'"]
55 58 for i, cmd in enumerate(newcmds, start=1):
56 59 ip.history_manager.store_inputs(i, cmd)
57 60 gothist = ip.history_manager.get_range(start=1, stop=4)
58 61 nt.assert_equal(list(gothist), zip([0,0,0],[1,2,3], newcmds))
59 62 # Previous session:
60 63 gothist = ip.history_manager.get_range(-1, 1, 4)
61 64 nt.assert_equal(list(gothist), zip([1,1,1],[1,2,3], hist))
62 65
63 66 # Check get_hist_tail
64 67 gothist = ip.history_manager.get_tail(4, output=True,
65 68 include_latest=True)
66 69 expected = [(1, 3, (hist[-1], "spam")),
67 70 (2, 1, (newcmds[0], None)),
68 71 (2, 2, (newcmds[1], None)),
69 72 (2, 3, (newcmds[2], None)),]
70 73 nt.assert_equal(list(gothist), expected)
71 74
72 75 gothist = ip.history_manager.get_tail(2)
73 76 expected = [(2, 1, newcmds[0]),
74 77 (2, 2, newcmds[1])]
75 78 nt.assert_equal(list(gothist), expected)
76 79
77 80 # Check get_hist_search
78 81 gothist = ip.history_manager.search("*test*")
79 82 nt.assert_equal(list(gothist), [(1,2,hist[1])] )
80 83 gothist = ip.history_manager.search("b*", output=True)
81 84 nt.assert_equal(list(gothist), [(1,3,(hist[2],"spam"))] )
82 85
83 86 # Cross testing: check that magic %save can get previous session.
84 87 testfilename = os.path.realpath(os.path.join(tmpdir, "test.py"))
85 88 ip.magic_save(testfilename + " ~1/1-3")
86 89 with py3compat.open(testfilename) as testfile:
87 90 nt.assert_equal(testfile.read(),
88 91 u"# coding: utf-8\n" + u"\n".join(hist))
89 92
90 93 # Duplicate line numbers - check that it doesn't crash, and
91 94 # gets a new session
92 95 ip.history_manager.store_inputs(1, "rogue")
93 96 ip.history_manager.writeout_cache()
94 97 nt.assert_equal(ip.history_manager.session_number, 3)
95 98 finally:
96 99 # Restore history manager
97 100 ip.history_manager = hist_manager_ori
98 101
99 102
100 103 def test_extract_hist_ranges():
101 104 instr = "1 2/3 ~4/5-6 ~4/7-~4/9 ~9/2-~7/5"
102 105 expected = [(0, 1, 2), # 0 == current session
103 106 (2, 3, 4),
104 107 (-4, 5, 7),
105 108 (-4, 7, 10),
106 109 (-9, 2, None), # None == to end
107 110 (-8, 1, None),
108 111 (-7, 1, 6)]
109 112 actual = list(extract_hist_ranges(instr))
110 113 nt.assert_equal(actual, expected)
111 114
112 115 def test_magic_rerun():
113 116 """Simple test for %rerun (no args -> rerun last line)"""
114 117 ip = get_ipython()
115 118 ip.run_cell("a = 10", store_history=True)
116 119 ip.run_cell("a += 1", store_history=True)
117 120 nt.assert_equal(ip.user_ns["a"], 11)
118 121 ip.run_cell("%rerun", store_history=True)
119 122 nt.assert_equal(ip.user_ns["a"], 12)
120 123
121 124 def test_timestamp_type():
122 125 ip = get_ipython()
123 126 info = ip.history_manager.get_session_info()
124 127 nt.assert_true(isinstance(info[1], datetime))
128
129 def test_hist_file_config():
130 cfg = Config()
131 cfg.HistoryManager.hist_file = tempfile.mktemp()
132 hm = HistoryManager(shell=get_ipython(), config=cfg)
133 nt.assert_equals(hm.hist_file, cfg.HistoryManager.hist_file)
134
General Comments 0
You need to be logged in to leave comments. Login now