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