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