##// END OF EJS Templates
update to warnings
jgors -
Show More
@@ -1,586 +1,586 b''
1 1 # encoding: utf-8
2 2 """
3 3 Utilities for path handling.
4 4 """
5 5
6 6 # Copyright (c) IPython Development Team.
7 7 # Distributed under the terms of the Modified BSD License.
8 8
9 9 import os
10 10 import sys
11 11 import errno
12 12 import shutil
13 13 import random
14 14 import tempfile
15 import warnings
16 from hashlib import md5
17 15 import glob
16 from warnings import warn
17 from hashlib import md5
18 18
19 19 import IPython
20 20 from IPython.testing.skipdoctest import skip_doctest
21 21 from IPython.utils.process import system
22 22 from IPython.utils.importstring import import_item
23 23 from IPython.utils import py3compat
24 24 #-----------------------------------------------------------------------------
25 25 # Code
26 26 #-----------------------------------------------------------------------------
27 27
28 28 fs_encoding = sys.getfilesystemencoding()
29 29
30 30 def _get_long_path_name(path):
31 31 """Dummy no-op."""
32 32 return path
33 33
34 34 def _writable_dir(path):
35 35 """Whether `path` is a directory, to which the user has write access."""
36 36 return os.path.isdir(path) and os.access(path, os.W_OK)
37 37
38 38 if sys.platform == 'win32':
39 39 @skip_doctest
40 40 def _get_long_path_name(path):
41 41 """Get a long path name (expand ~) on Windows using ctypes.
42 42
43 43 Examples
44 44 --------
45 45
46 46 >>> get_long_path_name('c:\\docume~1')
47 47 u'c:\\\\Documents and Settings'
48 48
49 49 """
50 50 try:
51 51 import ctypes
52 52 except ImportError:
53 53 raise ImportError('you need to have ctypes installed for this to work')
54 54 _GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
55 55 _GetLongPathName.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p,
56 56 ctypes.c_uint ]
57 57
58 58 buf = ctypes.create_unicode_buffer(260)
59 59 rv = _GetLongPathName(path, buf, 260)
60 60 if rv == 0 or rv > 260:
61 61 return path
62 62 else:
63 63 return buf.value
64 64
65 65
66 66 def get_long_path_name(path):
67 67 """Expand a path into its long form.
68 68
69 69 On Windows this expands any ~ in the paths. On other platforms, it is
70 70 a null operation.
71 71 """
72 72 return _get_long_path_name(path)
73 73
74 74
75 75 def unquote_filename(name, win32=(sys.platform=='win32')):
76 76 """ On Windows, remove leading and trailing quotes from filenames.
77 77 """
78 78 if win32:
79 79 if name.startswith(("'", '"')) and name.endswith(("'", '"')):
80 80 name = name[1:-1]
81 81 return name
82 82
83 83 def compress_user(path):
84 84 """Reverse of :func:`os.path.expanduser`
85 85 """
86 86 home = os.path.expanduser('~')
87 87 if path.startswith(home):
88 88 path = "~" + path[len(home):]
89 89 return path
90 90
91 91 def get_py_filename(name, force_win32=None):
92 92 """Return a valid python filename in the current directory.
93 93
94 94 If the given name is not a file, it adds '.py' and searches again.
95 95 Raises IOError with an informative message if the file isn't found.
96 96
97 97 On Windows, apply Windows semantics to the filename. In particular, remove
98 98 any quoting that has been applied to it. This option can be forced for
99 99 testing purposes.
100 100 """
101 101
102 102 name = os.path.expanduser(name)
103 103 if force_win32 is None:
104 104 win32 = (sys.platform == 'win32')
105 105 else:
106 106 win32 = force_win32
107 107 name = unquote_filename(name, win32=win32)
108 108 if not os.path.isfile(name) and not name.endswith('.py'):
109 109 name += '.py'
110 110 if os.path.isfile(name):
111 111 return name
112 112 else:
113 113 raise IOError('File `%r` not found.' % name)
114 114
115 115
116 116 def filefind(filename, path_dirs=None):
117 117 """Find a file by looking through a sequence of paths.
118 118
119 119 This iterates through a sequence of paths looking for a file and returns
120 120 the full, absolute path of the first occurence of the file. If no set of
121 121 path dirs is given, the filename is tested as is, after running through
122 122 :func:`expandvars` and :func:`expanduser`. Thus a simple call::
123 123
124 124 filefind('myfile.txt')
125 125
126 126 will find the file in the current working dir, but::
127 127
128 128 filefind('~/myfile.txt')
129 129
130 130 Will find the file in the users home directory. This function does not
131 131 automatically try any paths, such as the cwd or the user's home directory.
132 132
133 133 Parameters
134 134 ----------
135 135 filename : str
136 136 The filename to look for.
137 137 path_dirs : str, None or sequence of str
138 138 The sequence of paths to look for the file in. If None, the filename
139 139 need to be absolute or be in the cwd. If a string, the string is
140 140 put into a sequence and the searched. If a sequence, walk through
141 141 each element and join with ``filename``, calling :func:`expandvars`
142 142 and :func:`expanduser` before testing for existence.
143 143
144 144 Returns
145 145 -------
146 146 Raises :exc:`IOError` or returns absolute path to file.
147 147 """
148 148
149 149 # If paths are quoted, abspath gets confused, strip them...
150 150 filename = filename.strip('"').strip("'")
151 151 # If the input is an absolute path, just check it exists
152 152 if os.path.isabs(filename) and os.path.isfile(filename):
153 153 return filename
154 154
155 155 if path_dirs is None:
156 156 path_dirs = ("",)
157 157 elif isinstance(path_dirs, py3compat.string_types):
158 158 path_dirs = (path_dirs,)
159 159
160 160 for path in path_dirs:
161 161 if path == '.': path = py3compat.getcwd()
162 162 testname = expand_path(os.path.join(path, filename))
163 163 if os.path.isfile(testname):
164 164 return os.path.abspath(testname)
165 165
166 166 raise IOError("File %r does not exist in any of the search paths: %r" %
167 167 (filename, path_dirs) )
168 168
169 169
170 170 class HomeDirError(Exception):
171 171 pass
172 172
173 173
174 174 def get_home_dir(require_writable=False):
175 175 """Return the 'home' directory, as a unicode string.
176 176
177 177 Uses os.path.expanduser('~'), and checks for writability.
178 178
179 179 See stdlib docs for how this is determined.
180 180 $HOME is first priority on *ALL* platforms.
181 181
182 182 Parameters
183 183 ----------
184 184
185 185 require_writable : bool [default: False]
186 186 if True:
187 187 guarantees the return value is a writable directory, otherwise
188 188 raises HomeDirError
189 189 if False:
190 190 The path is resolved, but it is not guaranteed to exist or be writable.
191 191 """
192 192
193 193 homedir = os.path.expanduser('~')
194 194 # Next line will make things work even when /home/ is a symlink to
195 195 # /usr/home as it is on FreeBSD, for example
196 196 homedir = os.path.realpath(homedir)
197 197
198 198 if not _writable_dir(homedir) and os.name == 'nt':
199 199 # expanduser failed, use the registry to get the 'My Documents' folder.
200 200 try:
201 201 try:
202 202 import winreg as wreg # Py 3
203 203 except ImportError:
204 204 import _winreg as wreg # Py 2
205 205 key = wreg.OpenKey(
206 206 wreg.HKEY_CURRENT_USER,
207 207 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
208 208 )
209 209 homedir = wreg.QueryValueEx(key,'Personal')[0]
210 210 key.Close()
211 211 except:
212 212 pass
213 213
214 214 if (not require_writable) or _writable_dir(homedir):
215 215 return py3compat.cast_unicode(homedir, fs_encoding)
216 216 else:
217 217 raise HomeDirError('%s is not a writable dir, '
218 218 'set $HOME environment variable to override' % homedir)
219 219
220 220 def get_xdg_dir():
221 221 """Return the XDG_CONFIG_HOME, if it is defined and exists, else None.
222 222
223 223 This is only for non-OS X posix (Linux,Unix,etc.) systems.
224 224 """
225 225
226 226 env = os.environ
227 227
228 228 if os.name == 'posix' and sys.platform != 'darwin':
229 229 # Linux, Unix, AIX, etc.
230 230 # use ~/.config if empty OR not set
231 231 xdg = env.get("XDG_CONFIG_HOME", None) or os.path.join(get_home_dir(), '.config')
232 232 if xdg and _writable_dir(xdg):
233 233 return py3compat.cast_unicode(xdg, fs_encoding)
234 234
235 235 return None
236 236
237 237
238 238 def get_xdg_cache_dir():
239 239 """Return the XDG_CACHE_HOME, if it is defined and exists, else None.
240 240
241 241 This is only for non-OS X posix (Linux,Unix,etc.) systems.
242 242 """
243 243
244 244 env = os.environ
245 245
246 246 if os.name == 'posix' and sys.platform != 'darwin':
247 247 # Linux, Unix, AIX, etc.
248 248 # use ~/.cache if empty OR not set
249 249 xdg = env.get("XDG_CACHE_HOME", None) or os.path.join(get_home_dir(), '.cache')
250 250 if xdg and _writable_dir(xdg):
251 251 return py3compat.cast_unicode(xdg, fs_encoding)
252 252
253 253 return None
254 254
255 255
256 256 def get_ipython_dir():
257 257 """Get the IPython directory for this platform and user.
258 258
259 259 This uses the logic in `get_home_dir` to find the home directory
260 260 and then adds .ipython to the end of the path.
261 261 """
262 262
263 263 env = os.environ
264 264 pjoin = os.path.join
265 265
266 266
267 267 ipdir_def = '.ipython'
268 268
269 269 home_dir = get_home_dir()
270 270 xdg_dir = get_xdg_dir()
271 271
272 272 # import pdb; pdb.set_trace() # dbg
273 273 if 'IPYTHON_DIR' in env:
274 warnings.warn('The environment variable IPYTHON_DIR is deprecated. '
275 'Please use IPYTHONDIR instead.')
274 warn('The environment variable IPYTHON_DIR is deprecated. '
275 'Please use IPYTHONDIR instead.')
276 276 ipdir = env.get('IPYTHONDIR', env.get('IPYTHON_DIR', None))
277 277 if ipdir is None:
278 278 # not set explicitly, use ~/.ipython
279 279 ipdir = pjoin(home_dir, ipdir_def)
280 280 if xdg_dir:
281 281 # Several IPython versions (up to 1.x) defaulted to .config/ipython
282 282 # on Linux. We have decided to go back to using .ipython everywhere
283 283 xdg_ipdir = pjoin(xdg_dir, 'ipython')
284 284
285 285 if _writable_dir(xdg_ipdir):
286 286 cu = compress_user
287 287 if os.path.exists(ipdir):
288 warnings.warn(('Ignoring {0} in favour of {1}. Remove {0} '
289 'to get rid of this message').format(cu(xdg_ipdir), cu(ipdir)))
288 warn(('Ignoring {0} in favour of {1}. Remove {0} to '
289 'get rid of this message').format(cu(xdg_ipdir), cu(ipdir)))
290 290 elif os.path.islink(xdg_ipdir):
291 warnings.warn(('{0} is deprecated. Move link to {1} '
292 'to get rid of this message').format(cu(xdg_ipdir), cu(ipdir)))
291 warn(('{0} is deprecated. Move link to {1} to '
292 'get rid of this message').format(cu(xdg_ipdir), cu(ipdir)))
293 293 else:
294 warnings.warn('Moving {0} to {1}'.format(cu(xdg_ipdir), cu(ipdir)))
294 warn('Moving {0} to {1}'.format(cu(xdg_ipdir), cu(ipdir)))
295 295 shutil.move(xdg_ipdir, ipdir)
296 296
297 297 ipdir = os.path.normpath(os.path.expanduser(ipdir))
298 298
299 299 if os.path.exists(ipdir) and not _writable_dir(ipdir):
300 300 # ipdir exists, but is not writable
301 warnings.warn("IPython dir '%s' is not a writable location,"
302 " using a temp directory."%ipdir)
301 warn("IPython dir '{0}' is not a writable location,"
302 " using a temp directory.".format(ipdir))
303 303 ipdir = tempfile.mkdtemp()
304 304 elif not os.path.exists(ipdir):
305 305 parent = os.path.dirname(ipdir)
306 306 if not _writable_dir(parent):
307 307 # ipdir does not exist and parent isn't writable
308 warnings.warn("IPython parent '%s' is not a writable location,"
309 " using a temp directory."%parent)
308 warn("IPython parent '{0}' is not a writable location,"
309 " using a temp directory.".format(parent))
310 310 ipdir = tempfile.mkdtemp()
311 311
312 312 return py3compat.cast_unicode(ipdir, fs_encoding)
313 313
314 314
315 315 def get_ipython_cache_dir():
316 316 """Get the cache directory it is created if it does not exist."""
317 317 xdgdir = get_xdg_cache_dir()
318 318 if xdgdir is None:
319 319 return get_ipython_dir()
320 320 ipdir = os.path.join(xdgdir, "ipython")
321 321 if not os.path.exists(ipdir) and _writable_dir(xdgdir):
322 322 ensure_dir_exists(ipdir)
323 323 elif not _writable_dir(xdgdir):
324 324 return get_ipython_dir()
325 325
326 326 return py3compat.cast_unicode(ipdir, fs_encoding)
327 327
328 328
329 329 def get_ipython_package_dir():
330 330 """Get the base directory where IPython itself is installed."""
331 331 ipdir = os.path.dirname(IPython.__file__)
332 332 return py3compat.cast_unicode(ipdir, fs_encoding)
333 333
334 334
335 335 def get_ipython_module_path(module_str):
336 336 """Find the path to an IPython module in this version of IPython.
337 337
338 338 This will always find the version of the module that is in this importable
339 339 IPython package. This will always return the path to the ``.py``
340 340 version of the module.
341 341 """
342 342 if module_str == 'IPython':
343 343 return os.path.join(get_ipython_package_dir(), '__init__.py')
344 344 mod = import_item(module_str)
345 345 the_path = mod.__file__.replace('.pyc', '.py')
346 346 the_path = the_path.replace('.pyo', '.py')
347 347 return py3compat.cast_unicode(the_path, fs_encoding)
348 348
349 349 def locate_profile(profile='default'):
350 350 """Find the path to the folder associated with a given profile.
351 351
352 352 I.e. find $IPYTHONDIR/profile_whatever.
353 353 """
354 354 from IPython.core.profiledir import ProfileDir, ProfileDirError
355 355 try:
356 356 pd = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), profile)
357 357 except ProfileDirError:
358 358 # IOError makes more sense when people are expecting a path
359 359 raise IOError("Couldn't find profile %r" % profile)
360 360 return pd.location
361 361
362 362 def expand_path(s):
363 363 """Expand $VARS and ~names in a string, like a shell
364 364
365 365 :Examples:
366 366
367 367 In [2]: os.environ['FOO']='test'
368 368
369 369 In [3]: expand_path('variable FOO is $FOO')
370 370 Out[3]: 'variable FOO is test'
371 371 """
372 372 # This is a pretty subtle hack. When expand user is given a UNC path
373 373 # on Windows (\\server\share$\%username%), os.path.expandvars, removes
374 374 # the $ to get (\\server\share\%username%). I think it considered $
375 375 # alone an empty var. But, we need the $ to remains there (it indicates
376 376 # a hidden share).
377 377 if os.name=='nt':
378 378 s = s.replace('$\\', 'IPYTHON_TEMP')
379 379 s = os.path.expandvars(os.path.expanduser(s))
380 380 if os.name=='nt':
381 381 s = s.replace('IPYTHON_TEMP', '$\\')
382 382 return s
383 383
384 384
385 385 def unescape_glob(string):
386 386 """Unescape glob pattern in `string`."""
387 387 def unescape(s):
388 388 for pattern in '*[]!?':
389 389 s = s.replace(r'\{0}'.format(pattern), pattern)
390 390 return s
391 391 return '\\'.join(map(unescape, string.split('\\\\')))
392 392
393 393
394 394 def shellglob(args):
395 395 """
396 396 Do glob expansion for each element in `args` and return a flattened list.
397 397
398 398 Unmatched glob pattern will remain as-is in the returned list.
399 399
400 400 """
401 401 expanded = []
402 402 # Do not unescape backslash in Windows as it is interpreted as
403 403 # path separator:
404 404 unescape = unescape_glob if sys.platform != 'win32' else lambda x: x
405 405 for a in args:
406 406 expanded.extend(glob.glob(a) or [unescape(a)])
407 407 return expanded
408 408
409 409
410 410 def target_outdated(target,deps):
411 411 """Determine whether a target is out of date.
412 412
413 413 target_outdated(target,deps) -> 1/0
414 414
415 415 deps: list of filenames which MUST exist.
416 416 target: single filename which may or may not exist.
417 417
418 418 If target doesn't exist or is older than any file listed in deps, return
419 419 true, otherwise return false.
420 420 """
421 421 try:
422 422 target_time = os.path.getmtime(target)
423 423 except os.error:
424 424 return 1
425 425 for dep in deps:
426 426 dep_time = os.path.getmtime(dep)
427 427 if dep_time > target_time:
428 428 #print "For target",target,"Dep failed:",dep # dbg
429 429 #print "times (dep,tar):",dep_time,target_time # dbg
430 430 return 1
431 431 return 0
432 432
433 433
434 434 def target_update(target,deps,cmd):
435 435 """Update a target with a given command given a list of dependencies.
436 436
437 437 target_update(target,deps,cmd) -> runs cmd if target is outdated.
438 438
439 439 This is just a wrapper around target_outdated() which calls the given
440 440 command if target is outdated."""
441 441
442 442 if target_outdated(target,deps):
443 443 system(cmd)
444 444
445 445 def filehash(path):
446 446 """Make an MD5 hash of a file, ignoring any differences in line
447 447 ending characters."""
448 448 with open(path, "rU") as f:
449 449 return md5(py3compat.str_to_bytes(f.read())).hexdigest()
450 450
451 451 # If the config is unmodified from the default, we'll just delete it.
452 452 # These are consistent for 0.10.x, thankfully. We're not going to worry about
453 453 # older versions.
454 454 old_config_md5 = {'ipy_user_conf.py': 'fc108bedff4b9a00f91fa0a5999140d3',
455 455 'ipythonrc': '12a68954f3403eea2eec09dc8fe5a9b5'}
456 456
457 457 def check_for_old_config(ipython_dir=None):
458 458 """Check for old config files, and present a warning if they exist.
459 459
460 460 A link to the docs of the new config is included in the message.
461 461
462 462 This should mitigate confusion with the transition to the new
463 463 config system in 0.11.
464 464 """
465 465 if ipython_dir is None:
466 466 ipython_dir = get_ipython_dir()
467 467
468 468 old_configs = ['ipy_user_conf.py', 'ipythonrc', 'ipython_config.py']
469 469 warned = False
470 470 for cfg in old_configs:
471 471 f = os.path.join(ipython_dir, cfg)
472 472 if os.path.exists(f):
473 473 if filehash(f) == old_config_md5.get(cfg, ''):
474 474 os.unlink(f)
475 475 else:
476 warnings.warn("Found old IPython config file %r (modified by user)"%f)
476 warn("Found old IPython config file {0} (modified by user)".format(f))
477 477 warned = True
478 478
479 479 if warned:
480 warnings.warn("""
480 warn("""
481 481 The IPython configuration system has changed as of 0.11, and these files will
482 482 be ignored. See http://ipython.github.com/ipython-doc/dev/config for details
483 483 of the new config system.
484 484 To start configuring IPython, do `ipython profile create`, and edit
485 485 `ipython_config.py` in <ipython_dir>/profile_default.
486 486 If you need to leave the old config files in place for an older version of
487 487 IPython and want to suppress this warning message, set
488 488 `c.InteractiveShellApp.ignore_old_config=True` in the new config.""")
489 489
490 490 def get_security_file(filename, profile='default'):
491 491 """Return the absolute path of a security file given by filename and profile
492 492
493 493 This allows users and developers to find security files without
494 494 knowledge of the IPython directory structure. The search path
495 495 will be ['.', profile.security_dir]
496 496
497 497 Parameters
498 498 ----------
499 499
500 500 filename : str
501 501 The file to be found. If it is passed as an absolute path, it will
502 502 simply be returned.
503 503 profile : str [default: 'default']
504 504 The name of the profile to search. Leaving this unspecified
505 505 The file to be found. If it is passed as an absolute path, fname will
506 506 simply be returned.
507 507
508 508 Returns
509 509 -------
510 510 Raises :exc:`IOError` if file not found or returns absolute path to file.
511 511 """
512 512 # import here, because profiledir also imports from utils.path
513 513 from IPython.core.profiledir import ProfileDir
514 514 try:
515 515 pd = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), profile)
516 516 except Exception:
517 517 # will raise ProfileDirError if no such profile
518 518 raise IOError("Profile %r not found")
519 519 return filefind(filename, ['.', pd.security_dir])
520 520
521 521
522 522 ENOLINK = 1998
523 523
524 524 def link(src, dst):
525 525 """Hard links ``src`` to ``dst``, returning 0 or errno.
526 526
527 527 Note that the special errno ``ENOLINK`` will be returned if ``os.link`` isn't
528 528 supported by the operating system.
529 529 """
530 530
531 531 if not hasattr(os, "link"):
532 532 return ENOLINK
533 533 link_errno = 0
534 534 try:
535 535 os.link(src, dst)
536 536 except OSError as e:
537 537 link_errno = e.errno
538 538 return link_errno
539 539
540 540
541 541 def link_or_copy(src, dst):
542 542 """Attempts to hardlink ``src`` to ``dst``, copying if the link fails.
543 543
544 544 Attempts to maintain the semantics of ``shutil.copy``.
545 545
546 546 Because ``os.link`` does not overwrite files, a unique temporary file
547 547 will be used if the target already exists, then that file will be moved
548 548 into place.
549 549 """
550 550
551 551 if os.path.isdir(dst):
552 552 dst = os.path.join(dst, os.path.basename(src))
553 553
554 554 link_errno = link(src, dst)
555 555 if link_errno == errno.EEXIST:
556 556 new_dst = dst + "-temp-%04X" %(random.randint(1, 16**4), )
557 557 try:
558 558 link_or_copy(src, new_dst)
559 559 except:
560 560 try:
561 561 os.remove(new_dst)
562 562 except OSError:
563 563 pass
564 564 raise
565 565 os.rename(new_dst, dst)
566 566 elif link_errno != 0:
567 567 # Either link isn't supported, or the filesystem doesn't support
568 568 # linking, or 'src' and 'dst' are on different filesystems.
569 569 shutil.copy(src, dst)
570 570
571 571 def ensure_dir_exists(path, mode=0o755):
572 572 """ensure that a directory exists
573 573
574 574 If it doesn't exist, try to create it and protect against a race condition
575 575 if another process is doing the same.
576 576
577 577 The default permissions are 755, which differ from os.makedirs default of 777.
578 578 """
579 579 if not os.path.exists(path):
580 580 try:
581 581 os.makedirs(path, mode=mode)
582 582 except OSError as e:
583 583 if e.errno != errno.EEXIST:
584 584 raise
585 585 elif not os.path.isdir(path):
586 586 raise IOError("%r exists but is not a directory" % path)
General Comments 0
You need to be logged in to leave comments. Login now