##// END OF EJS Templates
check for writable dirs, not just existence, in utils.path...
MinRK -
Show More
@@ -1,450 +1,455 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 from hashlib import md5
19 from hashlib import md5
20
20
21 import IPython
21 import IPython
22 from IPython.utils import warn
22 from IPython.utils import warn
23 from IPython.utils.process import system
23 from IPython.utils.process import system
24 from IPython.utils.importstring import import_item
24 from IPython.utils.importstring import import_item
25
25
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 # Code
27 # Code
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29
29
30 fs_encoding = sys.getfilesystemencoding()
30 fs_encoding = sys.getfilesystemencoding()
31
31
32 def _cast_unicode(s, enc=None):
32 def _cast_unicode(s, enc=None):
33 """Turn 8-bit strings into unicode."""
33 """Turn 8-bit strings into unicode."""
34 if isinstance(s, bytes):
34 if isinstance(s, bytes):
35 enc = enc or sys.getdefaultencoding()
35 enc = enc or sys.getdefaultencoding()
36 return s.decode(enc)
36 return s.decode(enc)
37 return s
37 return s
38
38
39
39
40 def _get_long_path_name(path):
40 def _get_long_path_name(path):
41 """Dummy no-op."""
41 """Dummy no-op."""
42 return path
42 return path
43
43
44 def _writable_dir(path):
45 """Whether `path` is a directory, to which the user has write access."""
46 return os.path.isdir(path) and os.access(path, os.W_OK)
47
44 if sys.platform == 'win32':
48 if sys.platform == 'win32':
45 def _get_long_path_name(path):
49 def _get_long_path_name(path):
46 """Get a long path name (expand ~) on Windows using ctypes.
50 """Get a long path name (expand ~) on Windows using ctypes.
47
51
48 Examples
52 Examples
49 --------
53 --------
50
54
51 >>> get_long_path_name('c:\\docume~1')
55 >>> get_long_path_name('c:\\docume~1')
52 u'c:\\\\Documents and Settings'
56 u'c:\\\\Documents and Settings'
53
57
54 """
58 """
55 try:
59 try:
56 import ctypes
60 import ctypes
57 except ImportError:
61 except ImportError:
58 raise ImportError('you need to have ctypes installed for this to work')
62 raise ImportError('you need to have ctypes installed for this to work')
59 _GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
63 _GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
60 _GetLongPathName.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p,
64 _GetLongPathName.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p,
61 ctypes.c_uint ]
65 ctypes.c_uint ]
62
66
63 buf = ctypes.create_unicode_buffer(260)
67 buf = ctypes.create_unicode_buffer(260)
64 rv = _GetLongPathName(path, buf, 260)
68 rv = _GetLongPathName(path, buf, 260)
65 if rv == 0 or rv > 260:
69 if rv == 0 or rv > 260:
66 return path
70 return path
67 else:
71 else:
68 return buf.value
72 return buf.value
69
73
70
74
71 def get_long_path_name(path):
75 def get_long_path_name(path):
72 """Expand a path into its long form.
76 """Expand a path into its long form.
73
77
74 On Windows this expands any ~ in the paths. On other platforms, it is
78 On Windows this expands any ~ in the paths. On other platforms, it is
75 a null operation.
79 a null operation.
76 """
80 """
77 return _get_long_path_name(path)
81 return _get_long_path_name(path)
78
82
79
83
80 def get_py_filename(name):
84 def get_py_filename(name):
81 """Return a valid python filename in the current directory.
85 """Return a valid python filename in the current directory.
82
86
83 If the given name is not a file, it adds '.py' and searches again.
87 If the given name is not a file, it adds '.py' and searches again.
84 Raises IOError with an informative message if the file isn't found."""
88 Raises IOError with an informative message if the file isn't found."""
85
89
86 name = os.path.expanduser(name)
90 name = os.path.expanduser(name)
87 if not os.path.isfile(name) and not name.endswith('.py'):
91 if not os.path.isfile(name) and not name.endswith('.py'):
88 name += '.py'
92 name += '.py'
89 if os.path.isfile(name):
93 if os.path.isfile(name):
90 return name
94 return name
91 else:
95 else:
92 raise IOError,'File `%s` not found.' % name
96 raise IOError,'File `%s` not found.' % name
93
97
94
98
95 def filefind(filename, path_dirs=None):
99 def filefind(filename, path_dirs=None):
96 """Find a file by looking through a sequence of paths.
100 """Find a file by looking through a sequence of paths.
97
101
98 This iterates through a sequence of paths looking for a file and returns
102 This iterates through a sequence of paths looking for a file and returns
99 the full, absolute path of the first occurence of the file. If no set of
103 the full, absolute path of the first occurence of the file. If no set of
100 path dirs is given, the filename is tested as is, after running through
104 path dirs is given, the filename is tested as is, after running through
101 :func:`expandvars` and :func:`expanduser`. Thus a simple call::
105 :func:`expandvars` and :func:`expanduser`. Thus a simple call::
102
106
103 filefind('myfile.txt')
107 filefind('myfile.txt')
104
108
105 will find the file in the current working dir, but::
109 will find the file in the current working dir, but::
106
110
107 filefind('~/myfile.txt')
111 filefind('~/myfile.txt')
108
112
109 Will find the file in the users home directory. This function does not
113 Will find the file in the users home directory. This function does not
110 automatically try any paths, such as the cwd or the user's home directory.
114 automatically try any paths, such as the cwd or the user's home directory.
111
115
112 Parameters
116 Parameters
113 ----------
117 ----------
114 filename : str
118 filename : str
115 The filename to look for.
119 The filename to look for.
116 path_dirs : str, None or sequence of str
120 path_dirs : str, None or sequence of str
117 The sequence of paths to look for the file in. If None, the filename
121 The sequence of paths to look for the file in. If None, the filename
118 need to be absolute or be in the cwd. If a string, the string is
122 need to be absolute or be in the cwd. If a string, the string is
119 put into a sequence and the searched. If a sequence, walk through
123 put into a sequence and the searched. If a sequence, walk through
120 each element and join with ``filename``, calling :func:`expandvars`
124 each element and join with ``filename``, calling :func:`expandvars`
121 and :func:`expanduser` before testing for existence.
125 and :func:`expanduser` before testing for existence.
122
126
123 Returns
127 Returns
124 -------
128 -------
125 Raises :exc:`IOError` or returns absolute path to file.
129 Raises :exc:`IOError` or returns absolute path to file.
126 """
130 """
127
131
128 # If paths are quoted, abspath gets confused, strip them...
132 # If paths are quoted, abspath gets confused, strip them...
129 filename = filename.strip('"').strip("'")
133 filename = filename.strip('"').strip("'")
130 # If the input is an absolute path, just check it exists
134 # If the input is an absolute path, just check it exists
131 if os.path.isabs(filename) and os.path.isfile(filename):
135 if os.path.isabs(filename) and os.path.isfile(filename):
132 return filename
136 return filename
133
137
134 if path_dirs is None:
138 if path_dirs is None:
135 path_dirs = ("",)
139 path_dirs = ("",)
136 elif isinstance(path_dirs, basestring):
140 elif isinstance(path_dirs, basestring):
137 path_dirs = (path_dirs,)
141 path_dirs = (path_dirs,)
138
142
139 for path in path_dirs:
143 for path in path_dirs:
140 if path == '.': path = os.getcwdu()
144 if path == '.': path = os.getcwdu()
141 testname = expand_path(os.path.join(path, filename))
145 testname = expand_path(os.path.join(path, filename))
142 if os.path.isfile(testname):
146 if os.path.isfile(testname):
143 return os.path.abspath(testname)
147 return os.path.abspath(testname)
144
148
145 raise IOError("File %r does not exist in any of the search paths: %r" %
149 raise IOError("File %r does not exist in any of the search paths: %r" %
146 (filename, path_dirs) )
150 (filename, path_dirs) )
147
151
148
152
149 class HomeDirError(Exception):
153 class HomeDirError(Exception):
150 pass
154 pass
151
155
152
156
153 def get_home_dir():
157 def get_home_dir():
154 """Return the closest possible equivalent to a 'home' directory.
158 """Return the closest possible equivalent to a 'home' directory.
155
159
156 * On POSIX, we try $HOME.
160 * On POSIX, we try $HOME.
157 * On Windows we try:
161 * On Windows we try:
158 - %HOMESHARE%
162 - %HOMESHARE%
159 - %HOMEDRIVE\%HOMEPATH%
163 - %HOMEDRIVE\%HOMEPATH%
160 - %USERPROFILE%
164 - %USERPROFILE%
161 - Registry hack for My Documents
165 - Registry hack for My Documents
162 - %HOME%: rare, but some people with unix-like setups may have defined it
166 - %HOME%: rare, but some people with unix-like setups may have defined it
163 * On Dos C:\
167 * On Dos C:\
164
168
165 Currently only Posix and NT are implemented, a HomeDirError exception is
169 Currently only Posix and NT are implemented, a HomeDirError exception is
166 raised for all other OSes.
170 raised for all other OSes.
167 """
171 """
168
172
169 isdir = os.path.isdir
173 isdir = _writable_dir
170 env = os.environ
174 env = os.environ
171
175
172 # first, check py2exe distribution root directory for _ipython.
176 # first, check py2exe distribution root directory for _ipython.
173 # This overrides all. Normally does not exist.
177 # This overrides all. Normally does not exist.
174
178
175 if hasattr(sys, "frozen"): #Is frozen by py2exe
179 if hasattr(sys, "frozen"): #Is frozen by py2exe
176 if '\\library.zip\\' in IPython.__file__.lower():#libraries compressed to zip-file
180 if '\\library.zip\\' in IPython.__file__.lower():#libraries compressed to zip-file
177 root, rest = IPython.__file__.lower().split('library.zip')
181 root, rest = IPython.__file__.lower().split('library.zip')
178 else:
182 else:
179 root=os.path.join(os.path.split(IPython.__file__)[0],"../../")
183 root=os.path.join(os.path.split(IPython.__file__)[0],"../../")
180 root=os.path.abspath(root).rstrip('\\')
184 root=os.path.abspath(root).rstrip('\\')
181 if isdir(os.path.join(root, '_ipython')):
185 if isdir(os.path.join(root, '_ipython')):
182 os.environ["IPYKITROOT"] = root
186 os.environ["IPYKITROOT"] = root
183 return _cast_unicode(root, fs_encoding)
187 return _cast_unicode(root, fs_encoding)
184
188
185 if os.name == 'posix':
189 if os.name == 'posix':
186 # Linux, Unix, AIX, OS X
190 # Linux, Unix, AIX, OS X
187 try:
191 try:
188 homedir = env['HOME']
192 homedir = env['HOME']
189 except KeyError:
193 except KeyError:
190 # Last-ditch attempt at finding a suitable $HOME, on systems where
194 # Last-ditch attempt at finding a suitable $HOME, on systems where
191 # it may not be defined in the environment but the system shell
195 # it may not be defined in the environment but the system shell
192 # still knows it - reported once as:
196 # still knows it - reported once as:
193 # https://github.com/ipython/ipython/issues/154
197 # https://github.com/ipython/ipython/issues/154
194 from subprocess import Popen, PIPE
198 from subprocess import Popen, PIPE
195 homedir = Popen('echo $HOME', shell=True,
199 homedir = Popen('echo $HOME', shell=True,
196 stdout=PIPE).communicate()[0].strip()
200 stdout=PIPE).communicate()[0].strip()
197 if homedir:
201 if homedir:
198 return _cast_unicode(homedir, fs_encoding)
202 return _cast_unicode(homedir, fs_encoding)
199 else:
203 else:
200 raise HomeDirError('Undefined $HOME, IPython cannot proceed.')
204 raise HomeDirError('Undefined $HOME, IPython cannot proceed.')
201 else:
205 else:
202 return _cast_unicode(homedir, fs_encoding)
206 return _cast_unicode(homedir, fs_encoding)
203 elif os.name == 'nt':
207 elif os.name == 'nt':
204 # Now for win9x, XP, Vista, 7?
208 # Now for win9x, XP, Vista, 7?
205 # For some strange reason all of these return 'nt' for os.name.
209 # For some strange reason all of these return 'nt' for os.name.
206 # First look for a network home directory. This will return the UNC
210 # First look for a network home directory. This will return the UNC
207 # path (\\server\\Users\%username%) not the mapped path (Z:\). This
211 # path (\\server\\Users\%username%) not the mapped path (Z:\). This
208 # is needed when running IPython on cluster where all paths have to
212 # is needed when running IPython on cluster where all paths have to
209 # be UNC.
213 # be UNC.
210 try:
214 try:
211 homedir = env['HOMESHARE']
215 homedir = env['HOMESHARE']
212 except KeyError:
216 except KeyError:
213 pass
217 pass
214 else:
218 else:
215 if isdir(homedir):
219 if isdir(homedir):
216 return _cast_unicode(homedir, fs_encoding)
220 return _cast_unicode(homedir, fs_encoding)
217
221
218 # Now look for a local home directory
222 # Now look for a local home directory
219 try:
223 try:
220 homedir = os.path.join(env['HOMEDRIVE'],env['HOMEPATH'])
224 homedir = os.path.join(env['HOMEDRIVE'],env['HOMEPATH'])
221 except KeyError:
225 except KeyError:
222 pass
226 pass
223 else:
227 else:
224 if isdir(homedir):
228 if isdir(homedir):
225 return _cast_unicode(homedir, fs_encoding)
229 return _cast_unicode(homedir, fs_encoding)
226
230
227 # Now the users profile directory
231 # Now the users profile directory
228 try:
232 try:
229 homedir = os.path.join(env['USERPROFILE'])
233 homedir = os.path.join(env['USERPROFILE'])
230 except KeyError:
234 except KeyError:
231 pass
235 pass
232 else:
236 else:
233 if isdir(homedir):
237 if isdir(homedir):
234 return _cast_unicode(homedir, fs_encoding)
238 return _cast_unicode(homedir, fs_encoding)
235
239
236 # Use the registry to get the 'My Documents' folder.
240 # Use the registry to get the 'My Documents' folder.
237 try:
241 try:
238 import _winreg as wreg
242 import _winreg as wreg
239 key = wreg.OpenKey(
243 key = wreg.OpenKey(
240 wreg.HKEY_CURRENT_USER,
244 wreg.HKEY_CURRENT_USER,
241 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
245 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
242 )
246 )
243 homedir = wreg.QueryValueEx(key,'Personal')[0]
247 homedir = wreg.QueryValueEx(key,'Personal')[0]
244 key.Close()
248 key.Close()
245 except:
249 except:
246 pass
250 pass
247 else:
251 else:
248 if isdir(homedir):
252 if isdir(homedir):
249 return _cast_unicode(homedir, fs_encoding)
253 return _cast_unicode(homedir, fs_encoding)
250
254
251 # A user with a lot of unix tools in win32 may have defined $HOME.
255 # A user with a lot of unix tools in win32 may have defined $HOME.
252 # Try this as a last ditch option.
256 # Try this as a last ditch option.
253 try:
257 try:
254 homedir = env['HOME']
258 homedir = env['HOME']
255 except KeyError:
259 except KeyError:
256 pass
260 pass
257 else:
261 else:
258 if isdir(homedir):
262 if isdir(homedir):
259 return _cast_unicode(homedir, fs_encoding)
263 return _cast_unicode(homedir, fs_encoding)
260
264
261 # If all else fails, raise HomeDirError
265 # If all else fails, raise HomeDirError
262 raise HomeDirError('No valid home directory could be found')
266 raise HomeDirError('No valid home directory could be found')
263 elif os.name == 'dos':
267 elif os.name == 'dos':
264 # Desperate, may do absurd things in classic MacOS. May work under DOS.
268 # Desperate, may do absurd things in classic MacOS. May work under DOS.
265 return u'C:\\'
269 return u'C:\\'
266 else:
270 else:
267 raise HomeDirError('No valid home directory could be found for your OS')
271 raise HomeDirError('No valid home directory could be found for your OS')
268
272
269 def get_xdg_dir():
273 def get_xdg_dir():
270 """Return the XDG_CONFIG_HOME, if it is defined and exists, else None.
274 """Return the XDG_CONFIG_HOME, if it is defined and exists, else None.
271
275
272 This is only for posix (Linux,Unix,OS X, etc) systems.
276 This is only for posix (Linux,Unix,OS X, etc) systems.
273 """
277 """
274
278
275 isdir = os.path.isdir
279 isdir = _writable_dir
276 env = os.environ
280 env = os.environ
277
281
278 if os.name == 'posix':
282 if os.name == 'posix':
279 # Linux, Unix, AIX, OS X
283 # Linux, Unix, AIX, OS X
280 # use ~/.config if not set OR empty
284 # use ~/.config if not set OR empty
281 xdg = env.get("XDG_CONFIG_HOME", None) or os.path.join(get_home_dir(), '.config')
285 xdg = env.get("XDG_CONFIG_HOME", None) or os.path.join(get_home_dir(), '.config')
282 if xdg and isdir(xdg):
286 if xdg and isdir(xdg):
283 return _cast_unicode(xdg, fs_encoding)
287 return _cast_unicode(xdg, fs_encoding)
284
288
285 return None
289 return None
286
290
287
291
288 def get_ipython_dir():
292 def get_ipython_dir():
289 """Get the IPython directory for this platform and user.
293 """Get the IPython directory for this platform and user.
290
294
291 This uses the logic in `get_home_dir` to find the home directory
295 This uses the logic in `get_home_dir` to find the home directory
292 and the adds .ipython to the end of the path.
296 and the adds .ipython to the end of the path.
293 """
297 """
294
298
295 env = os.environ
299 env = os.environ
296 pjoin = os.path.join
300 pjoin = os.path.join
297 exists = os.path.exists
301 isdir = _writable_dir
302
298
303
299 ipdir_def = '.ipython'
304 ipdir_def = '.ipython'
300 xdg_def = 'ipython'
305 xdg_def = 'ipython'
301
306
302 home_dir = get_home_dir()
307 home_dir = get_home_dir()
303 xdg_dir = get_xdg_dir()
308 xdg_dir = get_xdg_dir()
304 # import pdb; pdb.set_trace() # dbg
309 # import pdb; pdb.set_trace() # dbg
305 ipdir = env.get('IPYTHON_DIR', env.get('IPYTHONDIR', None))
310 ipdir = env.get('IPYTHON_DIR', env.get('IPYTHONDIR', None))
306 if ipdir is None:
311 if ipdir is None:
307 # not set explicitly, use XDG_CONFIG_HOME or HOME
312 # not set explicitly, use XDG_CONFIG_HOME or HOME
308 home_ipdir = pjoin(home_dir, ipdir_def)
313 home_ipdir = pjoin(home_dir, ipdir_def)
309 if xdg_dir:
314 if xdg_dir:
310 # use XDG, as long as the user isn't already
315 # use XDG, as long as the user isn't already
311 # using $HOME/.ipython and *not* XDG/ipython
316 # using $HOME/.ipython and *not* XDG/ipython
312
317
313 xdg_ipdir = pjoin(xdg_dir, xdg_def)
318 xdg_ipdir = pjoin(xdg_dir, xdg_def)
314
319
315 if exists(xdg_ipdir) or not exists(home_ipdir):
320 if isdir(xdg_ipdir) or not isdir(home_ipdir):
316 ipdir = xdg_ipdir
321 ipdir = xdg_ipdir
317
322
318 if ipdir is None:
323 if ipdir is None:
319 # not using XDG
324 # not using XDG
320 ipdir = home_ipdir
325 ipdir = home_ipdir
321
326
322 ipdir = os.path.normpath(os.path.expanduser(ipdir))
327 ipdir = os.path.normpath(os.path.expanduser(ipdir))
323
328
324 return _cast_unicode(ipdir, fs_encoding)
329 return _cast_unicode(ipdir, fs_encoding)
325
330
326
331
327 def get_ipython_package_dir():
332 def get_ipython_package_dir():
328 """Get the base directory where IPython itself is installed."""
333 """Get the base directory where IPython itself is installed."""
329 ipdir = os.path.dirname(IPython.__file__)
334 ipdir = os.path.dirname(IPython.__file__)
330 return _cast_unicode(ipdir, fs_encoding)
335 return _cast_unicode(ipdir, fs_encoding)
331
336
332
337
333 def get_ipython_module_path(module_str):
338 def get_ipython_module_path(module_str):
334 """Find the path to an IPython module in this version of IPython.
339 """Find the path to an IPython module in this version of IPython.
335
340
336 This will always find the version of the module that is in this importable
341 This will always find the version of the module that is in this importable
337 IPython package. This will always return the path to the ``.py``
342 IPython package. This will always return the path to the ``.py``
338 version of the module.
343 version of the module.
339 """
344 """
340 if module_str == 'IPython':
345 if module_str == 'IPython':
341 return os.path.join(get_ipython_package_dir(), '__init__.py')
346 return os.path.join(get_ipython_package_dir(), '__init__.py')
342 mod = import_item(module_str)
347 mod = import_item(module_str)
343 the_path = mod.__file__.replace('.pyc', '.py')
348 the_path = mod.__file__.replace('.pyc', '.py')
344 the_path = the_path.replace('.pyo', '.py')
349 the_path = the_path.replace('.pyo', '.py')
345 return _cast_unicode(the_path, fs_encoding)
350 return _cast_unicode(the_path, fs_encoding)
346
351
347
352
348 def expand_path(s):
353 def expand_path(s):
349 """Expand $VARS and ~names in a string, like a shell
354 """Expand $VARS and ~names in a string, like a shell
350
355
351 :Examples:
356 :Examples:
352
357
353 In [2]: os.environ['FOO']='test'
358 In [2]: os.environ['FOO']='test'
354
359
355 In [3]: expand_path('variable FOO is $FOO')
360 In [3]: expand_path('variable FOO is $FOO')
356 Out[3]: 'variable FOO is test'
361 Out[3]: 'variable FOO is test'
357 """
362 """
358 # This is a pretty subtle hack. When expand user is given a UNC path
363 # This is a pretty subtle hack. When expand user is given a UNC path
359 # on Windows (\\server\share$\%username%), os.path.expandvars, removes
364 # on Windows (\\server\share$\%username%), os.path.expandvars, removes
360 # the $ to get (\\server\share\%username%). I think it considered $
365 # the $ to get (\\server\share\%username%). I think it considered $
361 # alone an empty var. But, we need the $ to remains there (it indicates
366 # alone an empty var. But, we need the $ to remains there (it indicates
362 # a hidden share).
367 # a hidden share).
363 if os.name=='nt':
368 if os.name=='nt':
364 s = s.replace('$\\', 'IPYTHON_TEMP')
369 s = s.replace('$\\', 'IPYTHON_TEMP')
365 s = os.path.expandvars(os.path.expanduser(s))
370 s = os.path.expandvars(os.path.expanduser(s))
366 if os.name=='nt':
371 if os.name=='nt':
367 s = s.replace('IPYTHON_TEMP', '$\\')
372 s = s.replace('IPYTHON_TEMP', '$\\')
368 return s
373 return s
369
374
370
375
371 def target_outdated(target,deps):
376 def target_outdated(target,deps):
372 """Determine whether a target is out of date.
377 """Determine whether a target is out of date.
373
378
374 target_outdated(target,deps) -> 1/0
379 target_outdated(target,deps) -> 1/0
375
380
376 deps: list of filenames which MUST exist.
381 deps: list of filenames which MUST exist.
377 target: single filename which may or may not exist.
382 target: single filename which may or may not exist.
378
383
379 If target doesn't exist or is older than any file listed in deps, return
384 If target doesn't exist or is older than any file listed in deps, return
380 true, otherwise return false.
385 true, otherwise return false.
381 """
386 """
382 try:
387 try:
383 target_time = os.path.getmtime(target)
388 target_time = os.path.getmtime(target)
384 except os.error:
389 except os.error:
385 return 1
390 return 1
386 for dep in deps:
391 for dep in deps:
387 dep_time = os.path.getmtime(dep)
392 dep_time = os.path.getmtime(dep)
388 if dep_time > target_time:
393 if dep_time > target_time:
389 #print "For target",target,"Dep failed:",dep # dbg
394 #print "For target",target,"Dep failed:",dep # dbg
390 #print "times (dep,tar):",dep_time,target_time # dbg
395 #print "times (dep,tar):",dep_time,target_time # dbg
391 return 1
396 return 1
392 return 0
397 return 0
393
398
394
399
395 def target_update(target,deps,cmd):
400 def target_update(target,deps,cmd):
396 """Update a target with a given command given a list of dependencies.
401 """Update a target with a given command given a list of dependencies.
397
402
398 target_update(target,deps,cmd) -> runs cmd if target is outdated.
403 target_update(target,deps,cmd) -> runs cmd if target is outdated.
399
404
400 This is just a wrapper around target_outdated() which calls the given
405 This is just a wrapper around target_outdated() which calls the given
401 command if target is outdated."""
406 command if target is outdated."""
402
407
403 if target_outdated(target,deps):
408 if target_outdated(target,deps):
404 system(cmd)
409 system(cmd)
405
410
406 def filehash(path):
411 def filehash(path):
407 """Make an MD5 hash of a file, ignoring any differences in line
412 """Make an MD5 hash of a file, ignoring any differences in line
408 ending characters."""
413 ending characters."""
409 with open(path, "rU") as f:
414 with open(path, "rU") as f:
410 return md5(f.read()).hexdigest()
415 return md5(f.read()).hexdigest()
411
416
412 # If the config is unmodified from the default, we'll just delete it.
417 # If the config is unmodified from the default, we'll just delete it.
413 # These are consistent for 0.10.x, thankfully. We're not going to worry about
418 # These are consistent for 0.10.x, thankfully. We're not going to worry about
414 # older versions.
419 # older versions.
415 old_config_md5 = {'ipy_user_conf.py': 'fc108bedff4b9a00f91fa0a5999140d3',
420 old_config_md5 = {'ipy_user_conf.py': 'fc108bedff4b9a00f91fa0a5999140d3',
416 'ipythonrc': '12a68954f3403eea2eec09dc8fe5a9b5'}
421 'ipythonrc': '12a68954f3403eea2eec09dc8fe5a9b5'}
417
422
418 def check_for_old_config(ipython_dir=None):
423 def check_for_old_config(ipython_dir=None):
419 """Check for old config files, and present a warning if they exist.
424 """Check for old config files, and present a warning if they exist.
420
425
421 A link to the docs of the new config is included in the message.
426 A link to the docs of the new config is included in the message.
422
427
423 This should mitigate confusion with the transition to the new
428 This should mitigate confusion with the transition to the new
424 config system in 0.11.
429 config system in 0.11.
425 """
430 """
426 if ipython_dir is None:
431 if ipython_dir is None:
427 ipython_dir = get_ipython_dir()
432 ipython_dir = get_ipython_dir()
428
433
429 old_configs = ['ipy_user_conf.py', 'ipythonrc', 'ipython_config.py']
434 old_configs = ['ipy_user_conf.py', 'ipythonrc', 'ipython_config.py']
430 warned = False
435 warned = False
431 for cfg in old_configs:
436 for cfg in old_configs:
432 f = os.path.join(ipython_dir, cfg)
437 f = os.path.join(ipython_dir, cfg)
433 if os.path.exists(f):
438 if os.path.exists(f):
434 if filehash(f) == old_config_md5.get(cfg, ''):
439 if filehash(f) == old_config_md5.get(cfg, ''):
435 os.unlink(f)
440 os.unlink(f)
436 else:
441 else:
437 warn.warn("Found old IPython config file %r (modified by user)"%f)
442 warn.warn("Found old IPython config file %r (modified by user)"%f)
438 warned = True
443 warned = True
439
444
440 if warned:
445 if warned:
441 warn.info("""
446 warn.info("""
442 The IPython configuration system has changed as of 0.11, and these files will
447 The IPython configuration system has changed as of 0.11, and these files will
443 be ignored. See http://ipython.github.com/ipython-doc/dev/config for details
448 be ignored. See http://ipython.github.com/ipython-doc/dev/config for details
444 of the new config system.
449 of the new config system.
445 To start configuring IPython, do `ipython profile create`, and edit
450 To start configuring IPython, do `ipython profile create`, and edit
446 `ipython_config.py` in <ipython_dir>/profile_default.
451 `ipython_config.py` in <ipython_dir>/profile_default.
447 If you need to leave the old config files in place for an older version of
452 If you need to leave the old config files in place for an older version of
448 IPython and want to suppress this warning message, set
453 IPython and want to suppress this warning message, set
449 `c.InteractiveShellApp.ignore_old_config=True` in the new config.""")
454 `c.InteractiveShellApp.ignore_old_config=True` in the new config.""")
450
455
General Comments 0
You need to be logged in to leave comments. Login now