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