##// END OF EJS Templates
HistoryAccessor can automatically find a history file for a standard profile.
Thomas Kluyver -
Show More
@@ -1,885 +1,909 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 import sqlite3
21 21 import threading
22 22
23 23 # Our own packages
24 24 from IPython.config.configurable import Configurable
25 25
26 26 from IPython.testing.skipdoctest import skip_doctest
27 27 from IPython.utils import io
28 from IPython.utils.path import locate_profile
28 29 from IPython.utils.traitlets import Bool, Dict, Instance, Int, CInt, List, Unicode
29 30 from IPython.utils.warn import warn
30 31
31 32 #-----------------------------------------------------------------------------
32 33 # Classes and functions
33 34 #-----------------------------------------------------------------------------
34 35
35 36 class HistoryAccessor(Configurable):
36 37 """Access the history database without adding to it.
37 38
38 39 This is intended for use by standalone history tools. IPython shells use
39 40 HistoryManager, below, which is a subclass of this."""
40 41 # String holding the path to the history file
41 42 hist_file = Unicode(config=True)
42 43
43 44 # The SQLite database
44 45 db = Instance(sqlite3.Connection)
45 46
46 def __init__(self, hist_file=u'', shell=None, config=None, **traits):
47 def __init__(self, profile='default', hist_file=u'', shell=None, config=None, **traits):
47 48 """Create a new history accessor.
48 49
49 hist_file must be given, either as an argument or through config.
50 Parameters
51 ----------
52 profile : str
53 The name of the profile from which to open history.
54 hist_file : str
55 Path to an SQLite history database stored by IPython. If specified,
56 hist_file overrides profile.
57 shell :
58 InteractiveShell object, for use by HistoryManager subclass
59 config :
60 Config object. hist_file can also be set through this.
50 61 """
51 62 # We need a pointer back to the shell for various tasks.
52 63 super(HistoryAccessor, self).__init__(shell=shell, config=config,
53 64 hist_file=hist_file, **traits)
54 65
55 66 if self.hist_file == u'':
56 67 # No one has set the hist_file, yet.
57 self.hist_file = self._get_hist_file_name(hist_file)
68 self.hist_file = self._get_hist_file_name(profile)
58 69
59 70 try:
60 71 self.init_db()
61 72 except sqlite3.DatabaseError:
62 73 if os.path.isfile(self.hist_file):
63 74 # Try to move the file out of the way.
64 75 newpath = os.path.join(self.shell.profile_dir.location, "hist-corrupt.sqlite")
65 76 os.rename(self.hist_file, newpath)
66 77 print("ERROR! History file wasn't a valid SQLite database.",
67 78 "It was moved to %s" % newpath, "and a new file created.")
68 79 self.init_db()
69 80 else:
70 81 # The hist_file is probably :memory: or something else.
71 82 raise
72 83
73 def _get_hist_file_name(self, hist_file=None):
74 "Override to produce a default history file name."
75 raise NotImplementedError("No default history file")
84 def _get_hist_file_name(self, profile='default'):
85 """Find the history file for the given profile name.
86
87 This is overridden by the HistoryManager subclass, to use the shell's
88 active profile.
89
90 Parameters
91 ----------
92 profile : str
93 The name of a profile which has a history file.
94 """
95 return os.path.join(locate_profile(profile), 'history.sqlite')
76 96
77 97 def init_db(self):
78 98 """Connect to the database, and create tables if necessary."""
79 99 # use detect_types so that timestamps return datetime objects
80 100 self.db = sqlite3.connect(self.hist_file, detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)
81 101 self.db.execute("""CREATE TABLE IF NOT EXISTS sessions (session integer
82 102 primary key autoincrement, start timestamp,
83 103 end timestamp, num_cmds integer, remark text)""")
84 104 self.db.execute("""CREATE TABLE IF NOT EXISTS history
85 105 (session integer, line integer, source text, source_raw text,
86 106 PRIMARY KEY (session, line))""")
87 107 # Output history is optional, but ensure the table's there so it can be
88 108 # enabled later.
89 109 self.db.execute("""CREATE TABLE IF NOT EXISTS output_history
90 110 (session integer, line integer, output text,
91 111 PRIMARY KEY (session, line))""")
92 112 self.db.commit()
93 113
94 114 def writeout_cache(self):
95 115 """Overridden by HistoryManager to dump the cache before certain
96 116 database lookups."""
97 117 pass
98 118
99 119 ## -------------------------------
100 120 ## Methods for retrieving history:
101 121 ## -------------------------------
102 122 def _run_sql(self, sql, params, raw=True, output=False):
103 123 """Prepares and runs an SQL query for the history database.
104 124
105 125 Parameters
106 126 ----------
107 127 sql : str
108 128 Any filtering expressions to go after SELECT ... FROM ...
109 129 params : tuple
110 130 Parameters passed to the SQL query (to replace "?")
111 131 raw, output : bool
112 132 See :meth:`get_range`
113 133
114 134 Returns
115 135 -------
116 136 Tuples as :meth:`get_range`
117 137 """
118 138 toget = 'source_raw' if raw else 'source'
119 139 sqlfrom = "history"
120 140 if output:
121 141 sqlfrom = "history LEFT JOIN output_history USING (session, line)"
122 142 toget = "history.%s, output_history.output" % toget
123 143 cur = self.db.execute("SELECT session, line, %s FROM %s " %\
124 144 (toget, sqlfrom) + sql, params)
125 145 if output: # Regroup into 3-tuples, and parse JSON
126 146 return ((ses, lin, (inp, out)) for ses, lin, inp, out in cur)
127 147 return cur
128 148
129 149 def get_session_info(self, session=0):
130 150 """get info about a session
131 151
132 152 Parameters
133 153 ----------
134 154
135 155 session : int
136 156 Session number to retrieve. The current session is 0, and negative
137 157 numbers count back from current session, so -1 is previous session.
138 158
139 159 Returns
140 160 -------
141 161
142 162 (session_id [int], start [datetime], end [datetime], num_cmds [int], remark [unicode])
143 163
144 164 Sessions that are running or did not exit cleanly will have `end=None`
145 165 and `num_cmds=None`.
146 166
147 167 """
148 168
149 169 if session <= 0:
150 170 session += self.session_number
151 171
152 172 query = "SELECT * from sessions where session == ?"
153 173 return self.db.execute(query, (session,)).fetchone()
154 174
155 175 def get_tail(self, n=10, raw=True, output=False, include_latest=False):
156 176 """Get the last n lines from the history database.
157 177
158 178 Parameters
159 179 ----------
160 180 n : int
161 181 The number of lines to get
162 182 raw, output : bool
163 183 See :meth:`get_range`
164 184 include_latest : bool
165 185 If False (default), n+1 lines are fetched, and the latest one
166 186 is discarded. This is intended to be used where the function
167 187 is called by a user command, which it should not return.
168 188
169 189 Returns
170 190 -------
171 191 Tuples as :meth:`get_range`
172 192 """
173 193 self.writeout_cache()
174 194 if not include_latest:
175 195 n += 1
176 196 cur = self._run_sql("ORDER BY session DESC, line DESC LIMIT ?",
177 197 (n,), raw=raw, output=output)
178 198 if not include_latest:
179 199 return reversed(list(cur)[1:])
180 200 return reversed(list(cur))
181 201
182 202 def search(self, pattern="*", raw=True, search_raw=True,
183 203 output=False):
184 204 """Search the database using unix glob-style matching (wildcards
185 205 * and ?).
186 206
187 207 Parameters
188 208 ----------
189 209 pattern : str
190 210 The wildcarded pattern to match when searching
191 211 search_raw : bool
192 212 If True, search the raw input, otherwise, the parsed input
193 213 raw, output : bool
194 214 See :meth:`get_range`
195 215
196 216 Returns
197 217 -------
198 218 Tuples as :meth:`get_range`
199 219 """
200 220 tosearch = "source_raw" if search_raw else "source"
201 221 if output:
202 222 tosearch = "history." + tosearch
203 223 self.writeout_cache()
204 224 return self._run_sql("WHERE %s GLOB ?" % tosearch, (pattern,),
205 225 raw=raw, output=output)
206 226
207 227 def get_range(self, session, start=1, stop=None, raw=True,output=False):
208 228 """Retrieve input by session.
209 229
210 230 Parameters
211 231 ----------
212 232 session : int
213 233 Session number to retrieve.
214 234 start : int
215 235 First line to retrieve.
216 236 stop : int
217 237 End of line range (excluded from output itself). If None, retrieve
218 238 to the end of the session.
219 239 raw : bool
220 240 If True, return untranslated input
221 241 output : bool
222 242 If True, attempt to include output. This will be 'real' Python
223 243 objects for the current session, or text reprs from previous
224 244 sessions if db_log_output was enabled at the time. Where no output
225 245 is found, None is used.
226 246
227 247 Returns
228 248 -------
229 249 An iterator over the desired lines. Each line is a 3-tuple, either
230 250 (session, line, input) if output is False, or
231 251 (session, line, (input, output)) if output is True.
232 252 """
233 253 if stop:
234 254 lineclause = "line >= ? AND line < ?"
235 255 params = (session, start, stop)
236 256 else:
237 257 lineclause = "line>=?"
238 258 params = (session, start)
239 259
240 260 return self._run_sql("WHERE session==? AND %s""" % lineclause,
241 261 params, raw=raw, output=output)
242 262
243 263 def get_range_by_str(self, rangestr, raw=True, output=False):
244 264 """Get lines of history from a string of ranges, as used by magic
245 265 commands %hist, %save, %macro, etc.
246 266
247 267 Parameters
248 268 ----------
249 269 rangestr : str
250 270 A string specifying ranges, e.g. "5 ~2/1-4". See
251 271 :func:`magic_history` for full details.
252 272 raw, output : bool
253 273 As :meth:`get_range`
254 274
255 275 Returns
256 276 -------
257 277 Tuples as :meth:`get_range`
258 278 """
259 279 for sess, s, e in extract_hist_ranges(rangestr):
260 280 for line in self.get_range(sess, s, e, raw=raw, output=output):
261 281 yield line
262 282
263 283
264 284 class HistoryManager(HistoryAccessor):
265 285 """A class to organize all history-related functionality in one place.
266 286 """
267 287 # Public interface
268 288
269 289 # An instance of the IPython shell we are attached to
270 290 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
271 291 # Lists to hold processed and raw history. These start with a blank entry
272 292 # so that we can index them starting from 1
273 293 input_hist_parsed = List([""])
274 294 input_hist_raw = List([""])
275 295 # A list of directories visited during session
276 296 dir_hist = List()
277 297 def _dir_hist_default(self):
278 298 try:
279 299 return [os.getcwdu()]
280 300 except OSError:
281 301 return []
282 302
283 303 # A dict of output history, keyed with ints from the shell's
284 304 # execution count.
285 305 output_hist = Dict()
286 306 # The text/plain repr of outputs.
287 307 output_hist_reprs = Dict()
288 308
289 309 # The number of the current session in the history database
290 310 session_number = CInt()
291 311 # Should we log output to the database? (default no)
292 312 db_log_output = Bool(False, config=True)
293 313 # Write to database every x commands (higher values save disk access & power)
294 314 # Values of 1 or less effectively disable caching.
295 315 db_cache_size = Int(0, config=True)
296 316 # The input and output caches
297 317 db_input_cache = List()
298 318 db_output_cache = List()
299 319
300 320 # History saving in separate thread
301 321 save_thread = Instance('IPython.core.history.HistorySavingThread')
302 322 try: # Event is a function returning an instance of _Event...
303 323 save_flag = Instance(threading._Event)
304 324 except AttributeError: # ...until Python 3.3, when it's a class.
305 325 save_flag = Instance(threading.Event)
306 326
307 327 # Private interface
308 328 # Variables used to store the three last inputs from the user. On each new
309 329 # history update, we populate the user's namespace with these, shifted as
310 330 # necessary.
311 331 _i00 = Unicode(u'')
312 332 _i = Unicode(u'')
313 333 _ii = Unicode(u'')
314 334 _iii = Unicode(u'')
315 335
316 336 # A regex matching all forms of the exit command, so that we don't store
317 337 # them in the history (it's annoying to rewind the first entry and land on
318 338 # an exit call).
319 339 _exit_re = re.compile(r"(exit|quit)(\s*\(.*\))?$")
320 340
321 341 def __init__(self, shell=None, config=None, **traits):
322 342 """Create a new history manager associated with a shell instance.
323 343 """
324 344 # We need a pointer back to the shell for various tasks.
325 345 super(HistoryManager, self).__init__(shell=shell, config=config,
326 346 **traits)
327 347 self.save_flag = threading.Event()
328 348 self.db_input_cache_lock = threading.Lock()
329 349 self.db_output_cache_lock = threading.Lock()
330 350 self.save_thread = HistorySavingThread(self)
331 351 self.save_thread.start()
332 352
333 353 self.new_session()
334 354
335 def _get_hist_file_name(self, hist_file=None):
355 def _get_hist_file_name(self, profile=None):
356 """Get default history file name based on the Shell's profile.
357
358 The profile parameter is ignored, but must exist for compatibility with
359 the parent class."""
336 360 profile_dir = self.shell.profile_dir.location
337 361 return os.path.join(profile_dir, 'history.sqlite')
338 362
339 363 def new_session(self, conn=None):
340 364 """Get a new session number."""
341 365 if conn is None:
342 366 conn = self.db
343 367
344 368 with conn:
345 369 cur = conn.execute("""INSERT INTO sessions VALUES (NULL, ?, NULL,
346 370 NULL, "") """, (datetime.datetime.now(),))
347 371 self.session_number = cur.lastrowid
348 372
349 373 def end_session(self):
350 374 """Close the database session, filling in the end time and line count."""
351 375 self.writeout_cache()
352 376 with self.db:
353 377 self.db.execute("""UPDATE sessions SET end=?, num_cmds=? WHERE
354 378 session==?""", (datetime.datetime.now(),
355 379 len(self.input_hist_parsed)-1, self.session_number))
356 380 self.session_number = 0
357 381
358 382 def name_session(self, name):
359 383 """Give the current session a name in the history database."""
360 384 with self.db:
361 385 self.db.execute("UPDATE sessions SET remark=? WHERE session==?",
362 386 (name, self.session_number))
363 387
364 388 def reset(self, new_session=True):
365 389 """Clear the session history, releasing all object references, and
366 390 optionally open a new session."""
367 391 self.output_hist.clear()
368 392 # The directory history can't be completely empty
369 393 self.dir_hist[:] = [os.getcwdu()]
370 394
371 395 if new_session:
372 396 if self.session_number:
373 397 self.end_session()
374 398 self.input_hist_parsed[:] = [""]
375 399 self.input_hist_raw[:] = [""]
376 400 self.new_session()
377 401
378 402 # ------------------------------
379 403 # Methods for retrieving history
380 404 # ------------------------------
381 405 def _get_range_session(self, start=1, stop=None, raw=True, output=False):
382 406 """Get input and output history from the current session. Called by
383 407 get_range, and takes similar parameters."""
384 408 input_hist = self.input_hist_raw if raw else self.input_hist_parsed
385 409
386 410 n = len(input_hist)
387 411 if start < 0:
388 412 start += n
389 413 if not stop or (stop > n):
390 414 stop = n
391 415 elif stop < 0:
392 416 stop += n
393 417
394 418 for i in range(start, stop):
395 419 if output:
396 420 line = (input_hist[i], self.output_hist_reprs.get(i))
397 421 else:
398 422 line = input_hist[i]
399 423 yield (0, i, line)
400 424
401 425 def get_range(self, session=0, start=1, stop=None, raw=True,output=False):
402 426 """Retrieve input by session.
403 427
404 428 Parameters
405 429 ----------
406 430 session : int
407 431 Session number to retrieve. The current session is 0, and negative
408 432 numbers count back from current session, so -1 is previous session.
409 433 start : int
410 434 First line to retrieve.
411 435 stop : int
412 436 End of line range (excluded from output itself). If None, retrieve
413 437 to the end of the session.
414 438 raw : bool
415 439 If True, return untranslated input
416 440 output : bool
417 441 If True, attempt to include output. This will be 'real' Python
418 442 objects for the current session, or text reprs from previous
419 443 sessions if db_log_output was enabled at the time. Where no output
420 444 is found, None is used.
421 445
422 446 Returns
423 447 -------
424 448 An iterator over the desired lines. Each line is a 3-tuple, either
425 449 (session, line, input) if output is False, or
426 450 (session, line, (input, output)) if output is True.
427 451 """
428 452 if session <= 0:
429 453 session += self.session_number
430 454 if session==self.session_number: # Current session
431 455 return self._get_range_session(start, stop, raw, output)
432 456 return super(HistoryManager, self).get_range(session, start, stop, raw, output)
433 457
434 458 ## ----------------------------
435 459 ## Methods for storing history:
436 460 ## ----------------------------
437 461 def store_inputs(self, line_num, source, source_raw=None):
438 462 """Store source and raw input in history and create input cache
439 463 variables _i*.
440 464
441 465 Parameters
442 466 ----------
443 467 line_num : int
444 468 The prompt number of this input.
445 469
446 470 source : str
447 471 Python input.
448 472
449 473 source_raw : str, optional
450 474 If given, this is the raw input without any IPython transformations
451 475 applied to it. If not given, ``source`` is used.
452 476 """
453 477 if source_raw is None:
454 478 source_raw = source
455 479 source = source.rstrip('\n')
456 480 source_raw = source_raw.rstrip('\n')
457 481
458 482 # do not store exit/quit commands
459 483 if self._exit_re.match(source_raw.strip()):
460 484 return
461 485
462 486 self.input_hist_parsed.append(source)
463 487 self.input_hist_raw.append(source_raw)
464 488
465 489 with self.db_input_cache_lock:
466 490 self.db_input_cache.append((line_num, source, source_raw))
467 491 # Trigger to flush cache and write to DB.
468 492 if len(self.db_input_cache) >= self.db_cache_size:
469 493 self.save_flag.set()
470 494
471 495 # update the auto _i variables
472 496 self._iii = self._ii
473 497 self._ii = self._i
474 498 self._i = self._i00
475 499 self._i00 = source_raw
476 500
477 501 # hackish access to user namespace to create _i1,_i2... dynamically
478 502 new_i = '_i%s' % line_num
479 503 to_main = {'_i': self._i,
480 504 '_ii': self._ii,
481 505 '_iii': self._iii,
482 506 new_i : self._i00 }
483 507 self.shell.user_ns.update(to_main)
484 508
485 509 def store_output(self, line_num):
486 510 """If database output logging is enabled, this saves all the
487 511 outputs from the indicated prompt number to the database. It's
488 512 called by run_cell after code has been executed.
489 513
490 514 Parameters
491 515 ----------
492 516 line_num : int
493 517 The line number from which to save outputs
494 518 """
495 519 if (not self.db_log_output) or (line_num not in self.output_hist_reprs):
496 520 return
497 521 output = self.output_hist_reprs[line_num]
498 522
499 523 with self.db_output_cache_lock:
500 524 self.db_output_cache.append((line_num, output))
501 525 if self.db_cache_size <= 1:
502 526 self.save_flag.set()
503 527
504 528 def _writeout_input_cache(self, conn):
505 529 with conn:
506 530 for line in self.db_input_cache:
507 531 conn.execute("INSERT INTO history VALUES (?, ?, ?, ?)",
508 532 (self.session_number,)+line)
509 533
510 534 def _writeout_output_cache(self, conn):
511 535 with conn:
512 536 for line in self.db_output_cache:
513 537 conn.execute("INSERT INTO output_history VALUES (?, ?, ?)",
514 538 (self.session_number,)+line)
515 539
516 540 def writeout_cache(self, conn=None):
517 541 """Write any entries in the cache to the database."""
518 542 if conn is None:
519 543 conn = self.db
520 544
521 545 with self.db_input_cache_lock:
522 546 try:
523 547 self._writeout_input_cache(conn)
524 548 except sqlite3.IntegrityError:
525 549 self.new_session(conn)
526 550 print("ERROR! Session/line number was not unique in",
527 551 "database. History logging moved to new session",
528 552 self.session_number)
529 553 try: # Try writing to the new session. If this fails, don't recurse
530 554 self._writeout_input_cache(conn)
531 555 except sqlite3.IntegrityError:
532 556 pass
533 557 finally:
534 558 self.db_input_cache = []
535 559
536 560 with self.db_output_cache_lock:
537 561 try:
538 562 self._writeout_output_cache(conn)
539 563 except sqlite3.IntegrityError:
540 564 print("!! Session/line number for output was not unique",
541 565 "in database. Output will not be stored.")
542 566 finally:
543 567 self.db_output_cache = []
544 568
545 569
546 570 class HistorySavingThread(threading.Thread):
547 571 """This thread takes care of writing history to the database, so that
548 572 the UI isn't held up while that happens.
549 573
550 574 It waits for the HistoryManager's save_flag to be set, then writes out
551 575 the history cache. The main thread is responsible for setting the flag when
552 576 the cache size reaches a defined threshold."""
553 577 daemon = True
554 578 stop_now = False
555 579 def __init__(self, history_manager):
556 580 super(HistorySavingThread, self).__init__()
557 581 self.history_manager = history_manager
558 582 atexit.register(self.stop)
559 583
560 584 def run(self):
561 585 # We need a separate db connection per thread:
562 586 try:
563 587 self.db = sqlite3.connect(self.history_manager.hist_file)
564 588 while True:
565 589 self.history_manager.save_flag.wait()
566 590 if self.stop_now:
567 591 return
568 592 self.history_manager.save_flag.clear()
569 593 self.history_manager.writeout_cache(self.db)
570 594 except Exception as e:
571 595 print(("The history saving thread hit an unexpected error (%s)."
572 596 "History will not be written to the database.") % repr(e))
573 597
574 598 def stop(self):
575 599 """This can be called from the main thread to safely stop this thread.
576 600
577 601 Note that it does not attempt to write out remaining history before
578 602 exiting. That should be done by calling the HistoryManager's
579 603 end_session method."""
580 604 self.stop_now = True
581 605 self.history_manager.save_flag.set()
582 606 self.join()
583 607
584 608
585 609 # To match, e.g. ~5/8-~2/3
586 610 range_re = re.compile(r"""
587 611 ((?P<startsess>~?\d+)/)?
588 612 (?P<start>\d+) # Only the start line num is compulsory
589 613 ((?P<sep>[\-:])
590 614 ((?P<endsess>~?\d+)/)?
591 615 (?P<end>\d+))?
592 616 $""", re.VERBOSE)
593 617
594 618 def extract_hist_ranges(ranges_str):
595 619 """Turn a string of history ranges into 3-tuples of (session, start, stop).
596 620
597 621 Examples
598 622 --------
599 623 list(extract_input_ranges("~8/5-~7/4 2"))
600 624 [(-8, 5, None), (-7, 1, 4), (0, 2, 3)]
601 625 """
602 626 for range_str in ranges_str.split():
603 627 rmatch = range_re.match(range_str)
604 628 if not rmatch:
605 629 continue
606 630 start = int(rmatch.group("start"))
607 631 end = rmatch.group("end")
608 632 end = int(end) if end else start+1 # If no end specified, get (a, a+1)
609 633 if rmatch.group("sep") == "-": # 1-3 == 1:4 --> [1, 2, 3]
610 634 end += 1
611 635 startsess = rmatch.group("startsess") or "0"
612 636 endsess = rmatch.group("endsess") or startsess
613 637 startsess = int(startsess.replace("~","-"))
614 638 endsess = int(endsess.replace("~","-"))
615 639 assert endsess >= startsess
616 640
617 641 if endsess == startsess:
618 642 yield (startsess, start, end)
619 643 continue
620 644 # Multiple sessions in one range:
621 645 yield (startsess, start, None)
622 646 for sess in range(startsess+1, endsess):
623 647 yield (sess, 1, None)
624 648 yield (endsess, 1, end)
625 649
626 650 def _format_lineno(session, line):
627 651 """Helper function to format line numbers properly."""
628 652 if session == 0:
629 653 return str(line)
630 654 return "%s#%s" % (session, line)
631 655
632 656 @skip_doctest
633 657 def magic_history(self, parameter_s = ''):
634 658 """Print input history (_i<n> variables), with most recent last.
635 659
636 660 %history -> print at most 40 inputs (some may be multi-line)\\
637 661 %history n -> print at most n inputs\\
638 662 %history n1 n2 -> print inputs between n1 and n2 (n2 not included)\\
639 663
640 664 By default, input history is printed without line numbers so it can be
641 665 directly pasted into an editor. Use -n to show them.
642 666
643 667 Ranges of history can be indicated using the syntax:
644 668 4 : Line 4, current session
645 669 4-6 : Lines 4-6, current session
646 670 243/1-5: Lines 1-5, session 243
647 671 ~2/7 : Line 7, session 2 before current
648 672 ~8/1-~6/5 : From the first line of 8 sessions ago, to the fifth line
649 673 of 6 sessions ago.
650 674 Multiple ranges can be entered, separated by spaces
651 675
652 676 The same syntax is used by %macro, %save, %edit, %rerun
653 677
654 678 Options:
655 679
656 680 -n: print line numbers for each input.
657 681 This feature is only available if numbered prompts are in use.
658 682
659 683 -o: also print outputs for each input.
660 684
661 685 -p: print classic '>>>' python prompts before each input. This is useful
662 686 for making documentation, and in conjunction with -o, for producing
663 687 doctest-ready output.
664 688
665 689 -r: (default) print the 'raw' history, i.e. the actual commands you typed.
666 690
667 691 -t: print the 'translated' history, as IPython understands it. IPython
668 692 filters your input and converts it all into valid Python source before
669 693 executing it (things like magics or aliases are turned into function
670 694 calls, for example). With this option, you'll see the native history
671 695 instead of the user-entered version: '%cd /' will be seen as
672 696 'get_ipython().magic("%cd /")' instead of '%cd /'.
673 697
674 698 -g: treat the arg as a pattern to grep for in (full) history.
675 699 This includes the saved history (almost all commands ever written).
676 700 Use '%hist -g' to show full saved history (may be very long).
677 701
678 702 -l: get the last n lines from all sessions. Specify n as a single arg, or
679 703 the default is the last 10 lines.
680 704
681 705 -f FILENAME: instead of printing the output to the screen, redirect it to
682 706 the given file. The file is always overwritten, though IPython asks for
683 707 confirmation first if it already exists.
684 708
685 709 Examples
686 710 --------
687 711 ::
688 712
689 713 In [6]: %hist -n 4 6
690 714 4:a = 12
691 715 5:print a**2
692 716
693 717 """
694 718
695 719 if not self.shell.displayhook.do_full_cache:
696 720 print('This feature is only available if numbered prompts are in use.')
697 721 return
698 722 opts,args = self.parse_options(parameter_s,'noprtglf:',mode='string')
699 723
700 724 # For brevity
701 725 history_manager = self.shell.history_manager
702 726
703 727 def _format_lineno(session, line):
704 728 """Helper function to format line numbers properly."""
705 729 if session in (0, history_manager.session_number):
706 730 return str(line)
707 731 return "%s/%s" % (session, line)
708 732
709 733 # Check if output to specific file was requested.
710 734 try:
711 735 outfname = opts['f']
712 736 except KeyError:
713 737 outfile = io.stdout # default
714 738 # We don't want to close stdout at the end!
715 739 close_at_end = False
716 740 else:
717 741 if os.path.exists(outfname):
718 742 if not io.ask_yes_no("File %r exists. Overwrite?" % outfname):
719 743 print('Aborting.')
720 744 return
721 745
722 746 outfile = open(outfname,'w')
723 747 close_at_end = True
724 748
725 749 print_nums = 'n' in opts
726 750 get_output = 'o' in opts
727 751 pyprompts = 'p' in opts
728 752 # Raw history is the default
729 753 raw = not('t' in opts)
730 754
731 755 default_length = 40
732 756 pattern = None
733 757
734 758 if 'g' in opts: # Glob search
735 759 pattern = "*" + args + "*" if args else "*"
736 760 hist = history_manager.search(pattern, raw=raw, output=get_output)
737 761 print_nums = True
738 762 elif 'l' in opts: # Get 'tail'
739 763 try:
740 764 n = int(args)
741 765 except ValueError, IndexError:
742 766 n = 10
743 767 hist = history_manager.get_tail(n, raw=raw, output=get_output)
744 768 else:
745 769 if args: # Get history by ranges
746 770 hist = history_manager.get_range_by_str(args, raw, get_output)
747 771 else: # Just get history for the current session
748 772 hist = history_manager.get_range(raw=raw, output=get_output)
749 773
750 774 # We could be displaying the entire history, so let's not try to pull it
751 775 # into a list in memory. Anything that needs more space will just misalign.
752 776 width = 4
753 777
754 778 for session, lineno, inline in hist:
755 779 # Print user history with tabs expanded to 4 spaces. The GUI clients
756 780 # use hard tabs for easier usability in auto-indented code, but we want
757 781 # to produce PEP-8 compliant history for safe pasting into an editor.
758 782 if get_output:
759 783 inline, output = inline
760 784 inline = inline.expandtabs(4).rstrip()
761 785
762 786 multiline = "\n" in inline
763 787 line_sep = '\n' if multiline else ' '
764 788 if print_nums:
765 789 print('%s:%s' % (_format_lineno(session, lineno).rjust(width),
766 790 line_sep), file=outfile, end='')
767 791 if pyprompts:
768 792 print(">>> ", end="", file=outfile)
769 793 if multiline:
770 794 inline = "\n... ".join(inline.splitlines()) + "\n..."
771 795 print(inline, file=outfile)
772 796 if get_output and output:
773 797 print(output, file=outfile)
774 798
775 799 if close_at_end:
776 800 outfile.close()
777 801
778 802
779 803 def magic_rep(self, arg):
780 804 r"""Repeat a command, or get command to input line for editing. %recall and
781 805 %rep are equivalent.
782 806
783 807 - %recall (no arguments):
784 808
785 809 Place a string version of last computation result (stored in the special '_'
786 810 variable) to the next input prompt. Allows you to create elaborate command
787 811 lines without using copy-paste::
788 812
789 813 In[1]: l = ["hei", "vaan"]
790 814 In[2]: "".join(l)
791 815 Out[2]: heivaan
792 816 In[3]: %rep
793 817 In[4]: heivaan_ <== cursor blinking
794 818
795 819 %recall 45
796 820
797 821 Place history line 45 on the next input prompt. Use %hist to find
798 822 out the number.
799 823
800 824 %recall 1-4
801 825
802 826 Combine the specified lines into one cell, and place it on the next
803 827 input prompt. See %history for the slice syntax.
804 828
805 829 %recall foo+bar
806 830
807 831 If foo+bar can be evaluated in the user namespace, the result is
808 832 placed at the next input prompt. Otherwise, the history is searched
809 833 for lines which contain that substring, and the most recent one is
810 834 placed at the next input prompt.
811 835 """
812 836 if not arg: # Last output
813 837 self.set_next_input(str(self.shell.user_ns["_"]))
814 838 return
815 839 # Get history range
816 840 histlines = self.history_manager.get_range_by_str(arg)
817 841 cmd = "\n".join(x[2] for x in histlines)
818 842 if cmd:
819 843 self.set_next_input(cmd.rstrip())
820 844 return
821 845
822 846 try: # Variable in user namespace
823 847 cmd = str(eval(arg, self.shell.user_ns))
824 848 except Exception: # Search for term in history
825 849 histlines = self.history_manager.search("*"+arg+"*")
826 850 for h in reversed([x[2] for x in histlines]):
827 851 if 'rep' in h:
828 852 continue
829 853 self.set_next_input(h.rstrip())
830 854 return
831 855 else:
832 856 self.set_next_input(cmd.rstrip())
833 857 print("Couldn't evaluate or find in history:", arg)
834 858
835 859 def magic_rerun(self, parameter_s=''):
836 860 """Re-run previous input
837 861
838 862 By default, you can specify ranges of input history to be repeated
839 863 (as with %history). With no arguments, it will repeat the last line.
840 864
841 865 Options:
842 866
843 867 -l <n> : Repeat the last n lines of input, not including the
844 868 current command.
845 869
846 870 -g foo : Repeat the most recent line which contains foo
847 871 """
848 872 opts, args = self.parse_options(parameter_s, 'l:g:', mode='string')
849 873 if "l" in opts: # Last n lines
850 874 n = int(opts['l'])
851 875 hist = self.history_manager.get_tail(n)
852 876 elif "g" in opts: # Search
853 877 p = "*"+opts['g']+"*"
854 878 hist = list(self.history_manager.search(p))
855 879 for l in reversed(hist):
856 880 if "rerun" not in l[2]:
857 881 hist = [l] # The last match which isn't a %rerun
858 882 break
859 883 else:
860 884 hist = [] # No matches except %rerun
861 885 elif args: # Specify history ranges
862 886 hist = self.history_manager.get_range_by_str(args)
863 887 else: # Last line
864 888 hist = self.history_manager.get_tail(1)
865 889 hist = [x[2] for x in hist]
866 890 if not hist:
867 891 print("No lines in history match specification")
868 892 return
869 893 histlines = "\n".join(hist)
870 894 print("=== Executing: ===")
871 895 print(histlines)
872 896 print("=== Output: ===")
873 897 self.run_cell("\n".join(hist), store_history=False)
874 898
875 899
876 900 def init_ipython(ip):
877 901 ip.define_magic("rep", magic_rep)
878 902 ip.define_magic("recall", magic_rep)
879 903 ip.define_magic("rerun", magic_rerun)
880 904 ip.define_magic("hist",magic_history) # Alternative name
881 905 ip.define_magic("history",magic_history)
882 906
883 907 # XXX - ipy_completers are in quarantine, need to be updated to new apis
884 908 #import ipy_completers
885 909 #ipy_completers.quick_completer('%hist' ,'-g -t -r -n')
@@ -1,478 +1,490 b''
1 1 # encoding: utf-8
2 2 """
3 3 Utilities for path handling.
4 4 """
5 5
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (C) 2008-2009 The IPython Development Team
8 8 #
9 9 # Distributed under the terms of the BSD License. The full license is in
10 10 # the file COPYING, distributed as part of this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16
17 17 import os
18 18 import sys
19 19 import tempfile
20 20 from hashlib import md5
21 21
22 22 import IPython
23 23 from IPython.utils import warn
24 24 from IPython.utils.process import system
25 25 from IPython.utils.importstring import import_item
26 26 from IPython.utils import py3compat
27 27
28 28 #-----------------------------------------------------------------------------
29 29 # Code
30 30 #-----------------------------------------------------------------------------
31 31
32 32 fs_encoding = sys.getfilesystemencoding()
33 33
34 34 def _get_long_path_name(path):
35 35 """Dummy no-op."""
36 36 return path
37 37
38 38 def _writable_dir(path):
39 39 """Whether `path` is a directory, to which the user has write access."""
40 40 return os.path.isdir(path) and os.access(path, os.W_OK)
41 41
42 42 if sys.platform == 'win32':
43 43 def _get_long_path_name(path):
44 44 """Get a long path name (expand ~) on Windows using ctypes.
45 45
46 46 Examples
47 47 --------
48 48
49 49 >>> get_long_path_name('c:\\docume~1')
50 50 u'c:\\\\Documents and Settings'
51 51
52 52 """
53 53 try:
54 54 import ctypes
55 55 except ImportError:
56 56 raise ImportError('you need to have ctypes installed for this to work')
57 57 _GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
58 58 _GetLongPathName.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p,
59 59 ctypes.c_uint ]
60 60
61 61 buf = ctypes.create_unicode_buffer(260)
62 62 rv = _GetLongPathName(path, buf, 260)
63 63 if rv == 0 or rv > 260:
64 64 return path
65 65 else:
66 66 return buf.value
67 67
68 68
69 69 def get_long_path_name(path):
70 70 """Expand a path into its long form.
71 71
72 72 On Windows this expands any ~ in the paths. On other platforms, it is
73 73 a null operation.
74 74 """
75 75 return _get_long_path_name(path)
76 76
77 77
78 78 def unquote_filename(name, win32=(sys.platform=='win32')):
79 79 """ On Windows, remove leading and trailing quotes from filenames.
80 80 """
81 81 if win32:
82 82 if name.startswith(("'", '"')) and name.endswith(("'", '"')):
83 83 name = name[1:-1]
84 84 return name
85 85
86 86
87 87 def get_py_filename(name, force_win32=None):
88 88 """Return a valid python filename in the current directory.
89 89
90 90 If the given name is not a file, it adds '.py' and searches again.
91 91 Raises IOError with an informative message if the file isn't found.
92 92
93 93 On Windows, apply Windows semantics to the filename. In particular, remove
94 94 any quoting that has been applied to it. This option can be forced for
95 95 testing purposes.
96 96 """
97 97
98 98 name = os.path.expanduser(name)
99 99 if force_win32 is None:
100 100 win32 = (sys.platform == 'win32')
101 101 else:
102 102 win32 = force_win32
103 103 name = unquote_filename(name, win32=win32)
104 104 if not os.path.isfile(name) and not name.endswith('.py'):
105 105 name += '.py'
106 106 if os.path.isfile(name):
107 107 return name
108 108 else:
109 109 raise IOError,'File `%s` not found.' % name
110 110
111 111
112 112 def filefind(filename, path_dirs=None):
113 113 """Find a file by looking through a sequence of paths.
114 114
115 115 This iterates through a sequence of paths looking for a file and returns
116 116 the full, absolute path of the first occurence of the file. If no set of
117 117 path dirs is given, the filename is tested as is, after running through
118 118 :func:`expandvars` and :func:`expanduser`. Thus a simple call::
119 119
120 120 filefind('myfile.txt')
121 121
122 122 will find the file in the current working dir, but::
123 123
124 124 filefind('~/myfile.txt')
125 125
126 126 Will find the file in the users home directory. This function does not
127 127 automatically try any paths, such as the cwd or the user's home directory.
128 128
129 129 Parameters
130 130 ----------
131 131 filename : str
132 132 The filename to look for.
133 133 path_dirs : str, None or sequence of str
134 134 The sequence of paths to look for the file in. If None, the filename
135 135 need to be absolute or be in the cwd. If a string, the string is
136 136 put into a sequence and the searched. If a sequence, walk through
137 137 each element and join with ``filename``, calling :func:`expandvars`
138 138 and :func:`expanduser` before testing for existence.
139 139
140 140 Returns
141 141 -------
142 142 Raises :exc:`IOError` or returns absolute path to file.
143 143 """
144 144
145 145 # If paths are quoted, abspath gets confused, strip them...
146 146 filename = filename.strip('"').strip("'")
147 147 # If the input is an absolute path, just check it exists
148 148 if os.path.isabs(filename) and os.path.isfile(filename):
149 149 return filename
150 150
151 151 if path_dirs is None:
152 152 path_dirs = ("",)
153 153 elif isinstance(path_dirs, basestring):
154 154 path_dirs = (path_dirs,)
155 155
156 156 for path in path_dirs:
157 157 if path == '.': path = os.getcwdu()
158 158 testname = expand_path(os.path.join(path, filename))
159 159 if os.path.isfile(testname):
160 160 return os.path.abspath(testname)
161 161
162 162 raise IOError("File %r does not exist in any of the search paths: %r" %
163 163 (filename, path_dirs) )
164 164
165 165
166 166 class HomeDirError(Exception):
167 167 pass
168 168
169 169
170 170 def get_home_dir():
171 171 """Return the closest possible equivalent to a 'home' directory.
172 172
173 173 * On POSIX, we try $HOME.
174 174 * On Windows we try:
175 175 - %HOMESHARE%
176 176 - %HOMEDRIVE\%HOMEPATH%
177 177 - %USERPROFILE%
178 178 - Registry hack for My Documents
179 179 - %HOME%: rare, but some people with unix-like setups may have defined it
180 180 * On Dos C:\
181 181
182 182 Currently only Posix and NT are implemented, a HomeDirError exception is
183 183 raised for all other OSes.
184 184 """
185 185
186 186 env = os.environ
187 187
188 188 # first, check py2exe distribution root directory for _ipython.
189 189 # This overrides all. Normally does not exist.
190 190
191 191 if hasattr(sys, "frozen"): #Is frozen by py2exe
192 192 if '\\library.zip\\' in IPython.__file__.lower():#libraries compressed to zip-file
193 193 root, rest = IPython.__file__.lower().split('library.zip')
194 194 else:
195 195 root=os.path.join(os.path.split(IPython.__file__)[0],"../../")
196 196 root=os.path.abspath(root).rstrip('\\')
197 197 if _writable_dir(os.path.join(root, '_ipython')):
198 198 os.environ["IPYKITROOT"] = root
199 199 return py3compat.cast_unicode(root, fs_encoding)
200 200
201 201 if os.name == 'posix':
202 202 # Linux, Unix, AIX, OS X
203 203 try:
204 204 homedir = env['HOME']
205 205 except KeyError:
206 206 # Last-ditch attempt at finding a suitable $HOME, on systems where
207 207 # it may not be defined in the environment but the system shell
208 208 # still knows it - reported once as:
209 209 # https://github.com/ipython/ipython/issues/154
210 210 from subprocess import Popen, PIPE
211 211 homedir = Popen('echo $HOME', shell=True,
212 212 stdout=PIPE).communicate()[0].strip()
213 213 if homedir:
214 214 return py3compat.cast_unicode(homedir, fs_encoding)
215 215 else:
216 216 raise HomeDirError('Undefined $HOME, IPython cannot proceed.')
217 217 else:
218 218 return py3compat.cast_unicode(homedir, fs_encoding)
219 219 elif os.name == 'nt':
220 220 # Now for win9x, XP, Vista, 7?
221 221 # For some strange reason all of these return 'nt' for os.name.
222 222 # First look for a network home directory. This will return the UNC
223 223 # path (\\server\\Users\%username%) not the mapped path (Z:\). This
224 224 # is needed when running IPython on cluster where all paths have to
225 225 # be UNC.
226 226 try:
227 227 homedir = env['HOMESHARE']
228 228 except KeyError:
229 229 pass
230 230 else:
231 231 if _writable_dir(homedir):
232 232 return py3compat.cast_unicode(homedir, fs_encoding)
233 233
234 234 # Now look for a local home directory
235 235 try:
236 236 homedir = os.path.join(env['HOMEDRIVE'],env['HOMEPATH'])
237 237 except KeyError:
238 238 pass
239 239 else:
240 240 if _writable_dir(homedir):
241 241 return py3compat.cast_unicode(homedir, fs_encoding)
242 242
243 243 # Now the users profile directory
244 244 try:
245 245 homedir = os.path.join(env['USERPROFILE'])
246 246 except KeyError:
247 247 pass
248 248 else:
249 249 if _writable_dir(homedir):
250 250 return py3compat.cast_unicode(homedir, fs_encoding)
251 251
252 252 # Use the registry to get the 'My Documents' folder.
253 253 try:
254 254 import _winreg as wreg
255 255 key = wreg.OpenKey(
256 256 wreg.HKEY_CURRENT_USER,
257 257 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
258 258 )
259 259 homedir = wreg.QueryValueEx(key,'Personal')[0]
260 260 key.Close()
261 261 except:
262 262 pass
263 263 else:
264 264 if _writable_dir(homedir):
265 265 return py3compat.cast_unicode(homedir, fs_encoding)
266 266
267 267 # A user with a lot of unix tools in win32 may have defined $HOME.
268 268 # Try this as a last ditch option.
269 269 try:
270 270 homedir = env['HOME']
271 271 except KeyError:
272 272 pass
273 273 else:
274 274 if _writable_dir(homedir):
275 275 return py3compat.cast_unicode(homedir, fs_encoding)
276 276
277 277 # If all else fails, raise HomeDirError
278 278 raise HomeDirError('No valid home directory could be found')
279 279 elif os.name == 'dos':
280 280 # Desperate, may do absurd things in classic MacOS. May work under DOS.
281 281 return u'C:\\'
282 282 else:
283 283 raise HomeDirError('No valid home directory could be found for your OS')
284 284
285 285 def get_xdg_dir():
286 286 """Return the XDG_CONFIG_HOME, if it is defined and exists, else None.
287 287
288 288 This is only for posix (Linux,Unix,OS X, etc) systems.
289 289 """
290 290
291 291 env = os.environ
292 292
293 293 if os.name == 'posix':
294 294 # Linux, Unix, AIX, OS X
295 295 # use ~/.config if not set OR empty
296 296 xdg = env.get("XDG_CONFIG_HOME", None) or os.path.join(get_home_dir(), '.config')
297 297 if xdg and _writable_dir(xdg):
298 298 return py3compat.cast_unicode(xdg, fs_encoding)
299 299
300 300 return None
301 301
302 302
303 303 def get_ipython_dir():
304 304 """Get the IPython directory for this platform and user.
305 305
306 306 This uses the logic in `get_home_dir` to find the home directory
307 307 and then adds .ipython to the end of the path.
308 308 """
309 309
310 310 env = os.environ
311 311 pjoin = os.path.join
312 312
313 313
314 314 ipdir_def = '.ipython'
315 315 xdg_def = 'ipython'
316 316
317 317 home_dir = get_home_dir()
318 318 xdg_dir = get_xdg_dir()
319 319 # import pdb; pdb.set_trace() # dbg
320 320 ipdir = env.get('IPYTHON_DIR', env.get('IPYTHONDIR', None))
321 321 if ipdir is None:
322 322 # not set explicitly, use XDG_CONFIG_HOME or HOME
323 323 home_ipdir = pjoin(home_dir, ipdir_def)
324 324 if xdg_dir:
325 325 # use XDG, as long as the user isn't already
326 326 # using $HOME/.ipython and *not* XDG/ipython
327 327
328 328 xdg_ipdir = pjoin(xdg_dir, xdg_def)
329 329
330 330 if _writable_dir(xdg_ipdir) or not _writable_dir(home_ipdir):
331 331 ipdir = xdg_ipdir
332 332
333 333 if ipdir is None:
334 334 # not using XDG
335 335 ipdir = home_ipdir
336 336
337 337 ipdir = os.path.normpath(os.path.expanduser(ipdir))
338 338
339 339 if os.path.exists(ipdir) and not _writable_dir(ipdir):
340 340 # ipdir exists, but is not writable
341 341 warn.warn("IPython dir '%s' is not a writable location,"
342 342 " using a temp directory."%ipdir)
343 343 ipdir = tempfile.mkdtemp()
344 344 elif not os.path.exists(ipdir):
345 345 parent = ipdir.rsplit(os.path.sep, 1)[0]
346 346 if not _writable_dir(parent):
347 347 # ipdir does not exist and parent isn't writable
348 348 warn.warn("IPython parent '%s' is not a writable location,"
349 349 " using a temp directory."%parent)
350 350 ipdir = tempfile.mkdtemp()
351 351
352 352 return py3compat.cast_unicode(ipdir, fs_encoding)
353 353
354 354
355 355 def get_ipython_package_dir():
356 356 """Get the base directory where IPython itself is installed."""
357 357 ipdir = os.path.dirname(IPython.__file__)
358 358 return py3compat.cast_unicode(ipdir, fs_encoding)
359 359
360 360
361 361 def get_ipython_module_path(module_str):
362 362 """Find the path to an IPython module in this version of IPython.
363 363
364 364 This will always find the version of the module that is in this importable
365 365 IPython package. This will always return the path to the ``.py``
366 366 version of the module.
367 367 """
368 368 if module_str == 'IPython':
369 369 return os.path.join(get_ipython_package_dir(), '__init__.py')
370 370 mod = import_item(module_str)
371 371 the_path = mod.__file__.replace('.pyc', '.py')
372 372 the_path = the_path.replace('.pyo', '.py')
373 373 return py3compat.cast_unicode(the_path, fs_encoding)
374 374
375 def locate_profile(profile='default'):
376 """Find the path to the folder associated with a given profile.
377
378 I.e. find $IPYTHON_DIR/profile_whatever.
379 """
380 from IPython.core.profiledir import ProfileDir, ProfileDirError
381 try:
382 pd = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), profile)
383 except ProfileDirError:
384 # IOError makes more sense when people are expecting a path
385 raise IOError("Couldn't find profile %r" % profile)
386 return pd.location
375 387
376 388 def expand_path(s):
377 389 """Expand $VARS and ~names in a string, like a shell
378 390
379 391 :Examples:
380 392
381 393 In [2]: os.environ['FOO']='test'
382 394
383 395 In [3]: expand_path('variable FOO is $FOO')
384 396 Out[3]: 'variable FOO is test'
385 397 """
386 398 # This is a pretty subtle hack. When expand user is given a UNC path
387 399 # on Windows (\\server\share$\%username%), os.path.expandvars, removes
388 400 # the $ to get (\\server\share\%username%). I think it considered $
389 401 # alone an empty var. But, we need the $ to remains there (it indicates
390 402 # a hidden share).
391 403 if os.name=='nt':
392 404 s = s.replace('$\\', 'IPYTHON_TEMP')
393 405 s = os.path.expandvars(os.path.expanduser(s))
394 406 if os.name=='nt':
395 407 s = s.replace('IPYTHON_TEMP', '$\\')
396 408 return s
397 409
398 410
399 411 def target_outdated(target,deps):
400 412 """Determine whether a target is out of date.
401 413
402 414 target_outdated(target,deps) -> 1/0
403 415
404 416 deps: list of filenames which MUST exist.
405 417 target: single filename which may or may not exist.
406 418
407 419 If target doesn't exist or is older than any file listed in deps, return
408 420 true, otherwise return false.
409 421 """
410 422 try:
411 423 target_time = os.path.getmtime(target)
412 424 except os.error:
413 425 return 1
414 426 for dep in deps:
415 427 dep_time = os.path.getmtime(dep)
416 428 if dep_time > target_time:
417 429 #print "For target",target,"Dep failed:",dep # dbg
418 430 #print "times (dep,tar):",dep_time,target_time # dbg
419 431 return 1
420 432 return 0
421 433
422 434
423 435 def target_update(target,deps,cmd):
424 436 """Update a target with a given command given a list of dependencies.
425 437
426 438 target_update(target,deps,cmd) -> runs cmd if target is outdated.
427 439
428 440 This is just a wrapper around target_outdated() which calls the given
429 441 command if target is outdated."""
430 442
431 443 if target_outdated(target,deps):
432 444 system(cmd)
433 445
434 446 def filehash(path):
435 447 """Make an MD5 hash of a file, ignoring any differences in line
436 448 ending characters."""
437 449 with open(path, "rU") as f:
438 450 return md5(py3compat.str_to_bytes(f.read())).hexdigest()
439 451
440 452 # If the config is unmodified from the default, we'll just delete it.
441 453 # These are consistent for 0.10.x, thankfully. We're not going to worry about
442 454 # older versions.
443 455 old_config_md5 = {'ipy_user_conf.py': 'fc108bedff4b9a00f91fa0a5999140d3',
444 456 'ipythonrc': '12a68954f3403eea2eec09dc8fe5a9b5'}
445 457
446 458 def check_for_old_config(ipython_dir=None):
447 459 """Check for old config files, and present a warning if they exist.
448 460
449 461 A link to the docs of the new config is included in the message.
450 462
451 463 This should mitigate confusion with the transition to the new
452 464 config system in 0.11.
453 465 """
454 466 if ipython_dir is None:
455 467 ipython_dir = get_ipython_dir()
456 468
457 469 old_configs = ['ipy_user_conf.py', 'ipythonrc', 'ipython_config.py']
458 470 warned = False
459 471 for cfg in old_configs:
460 472 f = os.path.join(ipython_dir, cfg)
461 473 if os.path.exists(f):
462 474 if filehash(f) == old_config_md5.get(cfg, ''):
463 475 os.unlink(f)
464 476 else:
465 477 warn.warn("Found old IPython config file %r (modified by user)"%f)
466 478 warned = True
467 479
468 480 if warned:
469 481 warn.info("""
470 482 The IPython configuration system has changed as of 0.11, and these files will
471 483 be ignored. See http://ipython.github.com/ipython-doc/dev/config for details
472 484 of the new config system.
473 485 To start configuring IPython, do `ipython profile create`, and edit
474 486 `ipython_config.py` in <ipython_dir>/profile_default.
475 487 If you need to leave the old config files in place for an older version of
476 488 IPython and want to suppress this warning message, set
477 489 `c.InteractiveShellApp.ignore_old_config=True` in the new config.""")
478 490
General Comments 0
You need to be logged in to leave comments. Login now