##// END OF EJS Templates
removed filehash in IPython/utils/path.py #10616
Aditya Sathe -
Show More
@@ -1,446 +1,438 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 glob
15 15 from warnings import warn
16 from hashlib import md5
17 16
18 17 from IPython.utils.process import system
19 18 from IPython.utils import py3compat
20 19 from IPython.utils.decorators import undoc
21 20
22 21 #-----------------------------------------------------------------------------
23 22 # Code
24 23 #-----------------------------------------------------------------------------
25 24
26 25 fs_encoding = sys.getfilesystemencoding()
27 26
28 27 def _writable_dir(path):
29 28 """Whether `path` is a directory, to which the user has write access."""
30 29 return os.path.isdir(path) and os.access(path, os.W_OK)
31 30
32 31 if sys.platform == 'win32':
33 32 def _get_long_path_name(path):
34 33 """Get a long path name (expand ~) on Windows using ctypes.
35 34
36 35 Examples
37 36 --------
38 37
39 38 >>> get_long_path_name('c:\\docume~1')
40 39 'c:\\\\Documents and Settings'
41 40
42 41 """
43 42 try:
44 43 import ctypes
45 44 except ImportError:
46 45 raise ImportError('you need to have ctypes installed for this to work')
47 46 _GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
48 47 _GetLongPathName.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p,
49 48 ctypes.c_uint ]
50 49
51 50 buf = ctypes.create_unicode_buffer(260)
52 51 rv = _GetLongPathName(path, buf, 260)
53 52 if rv == 0 or rv > 260:
54 53 return path
55 54 else:
56 55 return buf.value
57 56 else:
58 57 def _get_long_path_name(path):
59 58 """Dummy no-op."""
60 59 return path
61 60
62 61
63 62
64 63 def get_long_path_name(path):
65 64 """Expand a path into its long form.
66 65
67 66 On Windows this expands any ~ in the paths. On other platforms, it is
68 67 a null operation.
69 68 """
70 69 return _get_long_path_name(path)
71 70
72 71
73 72 def unquote_filename(name, win32=(sys.platform=='win32')):
74 73 """ On Windows, remove leading and trailing quotes from filenames.
75 74
76 75 This function has been deprecated and should not be used any more:
77 76 unquoting is now taken care of by :func:`IPython.utils.process.arg_split`.
78 77 """
79 78 warn("'unquote_filename' is deprecated since IPython 5.0 and should not "
80 79 "be used anymore", DeprecationWarning, stacklevel=2)
81 80 if win32:
82 81 if name.startswith(("'", '"')) and name.endswith(("'", '"')):
83 82 name = name[1:-1]
84 83 return name
85 84
86 85
87 86 def compress_user(path):
88 87 """Reverse of :func:`os.path.expanduser`
89 88 """
90 89 home = os.path.expanduser('~')
91 90 if path.startswith(home):
92 91 path = "~" + path[len(home):]
93 92 return path
94 93
95 94 def get_py_filename(name, force_win32=None):
96 95 """Return a valid python filename in the current directory.
97 96
98 97 If the given name is not a file, it adds '.py' and searches again.
99 98 Raises IOError with an informative message if the file isn't found.
100 99 """
101 100
102 101 name = os.path.expanduser(name)
103 102 if force_win32 is not None:
104 103 warn("The 'force_win32' argument to 'get_py_filename' is deprecated "
105 104 "since IPython 5.0 and should not be used anymore",
106 105 DeprecationWarning, stacklevel=2)
107 106 if not os.path.isfile(name) and not name.endswith('.py'):
108 107 name += '.py'
109 108 if os.path.isfile(name):
110 109 return name
111 110 else:
112 111 raise IOError('File `%r` not found.' % name)
113 112
114 113
115 114 def filefind(filename, path_dirs=None):
116 115 """Find a file by looking through a sequence of paths.
117 116
118 117 This iterates through a sequence of paths looking for a file and returns
119 118 the full, absolute path of the first occurence of the file. If no set of
120 119 path dirs is given, the filename is tested as is, after running through
121 120 :func:`expandvars` and :func:`expanduser`. Thus a simple call::
122 121
123 122 filefind('myfile.txt')
124 123
125 124 will find the file in the current working dir, but::
126 125
127 126 filefind('~/myfile.txt')
128 127
129 128 Will find the file in the users home directory. This function does not
130 129 automatically try any paths, such as the cwd or the user's home directory.
131 130
132 131 Parameters
133 132 ----------
134 133 filename : str
135 134 The filename to look for.
136 135 path_dirs : str, None or sequence of str
137 136 The sequence of paths to look for the file in. If None, the filename
138 137 need to be absolute or be in the cwd. If a string, the string is
139 138 put into a sequence and the searched. If a sequence, walk through
140 139 each element and join with ``filename``, calling :func:`expandvars`
141 140 and :func:`expanduser` before testing for existence.
142 141
143 142 Returns
144 143 -------
145 144 Raises :exc:`IOError` or returns absolute path to file.
146 145 """
147 146
148 147 # If paths are quoted, abspath gets confused, strip them...
149 148 filename = filename.strip('"').strip("'")
150 149 # If the input is an absolute path, just check it exists
151 150 if os.path.isabs(filename) and os.path.isfile(filename):
152 151 return filename
153 152
154 153 if path_dirs is None:
155 154 path_dirs = ("",)
156 155 elif isinstance(path_dirs, str):
157 156 path_dirs = (path_dirs,)
158 157
159 158 for path in path_dirs:
160 159 if path == '.': path = os.getcwd()
161 160 testname = expand_path(os.path.join(path, filename))
162 161 if os.path.isfile(testname):
163 162 return os.path.abspath(testname)
164 163
165 164 raise IOError("File %r does not exist in any of the search paths: %r" %
166 165 (filename, path_dirs) )
167 166
168 167
169 168 class HomeDirError(Exception):
170 169 pass
171 170
172 171
173 172 def get_home_dir(require_writable=False):
174 173 """Return the 'home' directory, as a unicode string.
175 174
176 175 Uses os.path.expanduser('~'), and checks for writability.
177 176
178 177 See stdlib docs for how this is determined.
179 178 $HOME is first priority on *ALL* platforms.
180 179
181 180 Parameters
182 181 ----------
183 182
184 183 require_writable : bool [default: False]
185 184 if True:
186 185 guarantees the return value is a writable directory, otherwise
187 186 raises HomeDirError
188 187 if False:
189 188 The path is resolved, but it is not guaranteed to exist or be writable.
190 189 """
191 190
192 191 homedir = os.path.expanduser('~')
193 192 # Next line will make things work even when /home/ is a symlink to
194 193 # /usr/home as it is on FreeBSD, for example
195 194 homedir = os.path.realpath(homedir)
196 195
197 196 if not _writable_dir(homedir) and os.name == 'nt':
198 197 # expanduser failed, use the registry to get the 'My Documents' folder.
199 198 try:
200 199 try:
201 200 import winreg as wreg # Py 3
202 201 except ImportError:
203 202 import _winreg as wreg # Py 2
204 203 key = wreg.OpenKey(
205 204 wreg.HKEY_CURRENT_USER,
206 205 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
207 206 )
208 207 homedir = wreg.QueryValueEx(key,'Personal')[0]
209 208 key.Close()
210 209 except:
211 210 pass
212 211
213 212 if (not require_writable) or _writable_dir(homedir):
214 213 return py3compat.cast_unicode(homedir, fs_encoding)
215 214 else:
216 215 raise HomeDirError('%s is not a writable dir, '
217 216 'set $HOME environment variable to override' % homedir)
218 217
219 218 def get_xdg_dir():
220 219 """Return the XDG_CONFIG_HOME, if it is defined and exists, else None.
221 220
222 221 This is only for non-OS X posix (Linux,Unix,etc.) systems.
223 222 """
224 223
225 224 env = os.environ
226 225
227 226 if os.name == 'posix' and sys.platform != 'darwin':
228 227 # Linux, Unix, AIX, etc.
229 228 # use ~/.config if empty OR not set
230 229 xdg = env.get("XDG_CONFIG_HOME", None) or os.path.join(get_home_dir(), '.config')
231 230 if xdg and _writable_dir(xdg):
232 231 return py3compat.cast_unicode(xdg, fs_encoding)
233 232
234 233 return None
235 234
236 235
237 236 def get_xdg_cache_dir():
238 237 """Return the XDG_CACHE_HOME, if it is defined and exists, else None.
239 238
240 239 This is only for non-OS X posix (Linux,Unix,etc.) systems.
241 240 """
242 241
243 242 env = os.environ
244 243
245 244 if os.name == 'posix' and sys.platform != 'darwin':
246 245 # Linux, Unix, AIX, etc.
247 246 # use ~/.cache if empty OR not set
248 247 xdg = env.get("XDG_CACHE_HOME", None) or os.path.join(get_home_dir(), '.cache')
249 248 if xdg and _writable_dir(xdg):
250 249 return py3compat.cast_unicode(xdg, fs_encoding)
251 250
252 251 return None
253 252
254 253
255 254 @undoc
256 255 def get_ipython_dir():
257 256 warn("get_ipython_dir has moved to the IPython.paths module since IPython 4.0.", stacklevel=2)
258 257 from IPython.paths import get_ipython_dir
259 258 return get_ipython_dir()
260 259
261 260 @undoc
262 261 def get_ipython_cache_dir():
263 262 warn("get_ipython_cache_dir has moved to the IPython.paths module since IPython 4.0.", stacklevel=2)
264 263 from IPython.paths import get_ipython_cache_dir
265 264 return get_ipython_cache_dir()
266 265
267 266 @undoc
268 267 def get_ipython_package_dir():
269 268 warn("get_ipython_package_dir has moved to the IPython.paths module since IPython 4.0.", stacklevel=2)
270 269 from IPython.paths import get_ipython_package_dir
271 270 return get_ipython_package_dir()
272 271
273 272 @undoc
274 273 def get_ipython_module_path(module_str):
275 274 warn("get_ipython_module_path has moved to the IPython.paths module since IPython 4.0.", stacklevel=2)
276 275 from IPython.paths import get_ipython_module_path
277 276 return get_ipython_module_path(module_str)
278 277
279 278 @undoc
280 279 def locate_profile(profile='default'):
281 280 warn("locate_profile has moved to the IPython.paths module since IPython 4.0.", stacklevel=2)
282 281 from IPython.paths import locate_profile
283 282 return locate_profile(profile=profile)
284 283
285 284 def expand_path(s):
286 285 """Expand $VARS and ~names in a string, like a shell
287 286
288 287 :Examples:
289 288
290 289 In [2]: os.environ['FOO']='test'
291 290
292 291 In [3]: expand_path('variable FOO is $FOO')
293 292 Out[3]: 'variable FOO is test'
294 293 """
295 294 # This is a pretty subtle hack. When expand user is given a UNC path
296 295 # on Windows (\\server\share$\%username%), os.path.expandvars, removes
297 296 # the $ to get (\\server\share\%username%). I think it considered $
298 297 # alone an empty var. But, we need the $ to remains there (it indicates
299 298 # a hidden share).
300 299 if os.name=='nt':
301 300 s = s.replace('$\\', 'IPYTHON_TEMP')
302 301 s = os.path.expandvars(os.path.expanduser(s))
303 302 if os.name=='nt':
304 303 s = s.replace('IPYTHON_TEMP', '$\\')
305 304 return s
306 305
307 306
308 307 def unescape_glob(string):
309 308 """Unescape glob pattern in `string`."""
310 309 def unescape(s):
311 310 for pattern in '*[]!?':
312 311 s = s.replace(r'\{0}'.format(pattern), pattern)
313 312 return s
314 313 return '\\'.join(map(unescape, string.split('\\\\')))
315 314
316 315
317 316 def shellglob(args):
318 317 """
319 318 Do glob expansion for each element in `args` and return a flattened list.
320 319
321 320 Unmatched glob pattern will remain as-is in the returned list.
322 321
323 322 """
324 323 expanded = []
325 324 # Do not unescape backslash in Windows as it is interpreted as
326 325 # path separator:
327 326 unescape = unescape_glob if sys.platform != 'win32' else lambda x: x
328 327 for a in args:
329 328 expanded.extend(glob.glob(a) or [unescape(a)])
330 329 return expanded
331 330
332 331
333 332 def target_outdated(target,deps):
334 333 """Determine whether a target is out of date.
335 334
336 335 target_outdated(target,deps) -> 1/0
337 336
338 337 deps: list of filenames which MUST exist.
339 338 target: single filename which may or may not exist.
340 339
341 340 If target doesn't exist or is older than any file listed in deps, return
342 341 true, otherwise return false.
343 342 """
344 343 try:
345 344 target_time = os.path.getmtime(target)
346 345 except os.error:
347 346 return 1
348 347 for dep in deps:
349 348 dep_time = os.path.getmtime(dep)
350 349 if dep_time > target_time:
351 350 #print "For target",target,"Dep failed:",dep # dbg
352 351 #print "times (dep,tar):",dep_time,target_time # dbg
353 352 return 1
354 353 return 0
355 354
356 355
357 356 def target_update(target,deps,cmd):
358 357 """Update a target with a given command given a list of dependencies.
359 358
360 359 target_update(target,deps,cmd) -> runs cmd if target is outdated.
361 360
362 361 This is just a wrapper around target_outdated() which calls the given
363 362 command if target is outdated."""
364 363
365 364 if target_outdated(target,deps):
366 365 system(cmd)
367 366
368 @undoc
369 def filehash(path):
370 """Make an MD5 hash of a file, ignoring any differences in line
371 ending characters."""
372 warn("filehash() is deprecated since IPython 4.0", DeprecationWarning, stacklevel=2)
373 with open(path, "rU") as f:
374 return md5(py3compat.str_to_bytes(f.read())).hexdigest()
375 367
376 368 ENOLINK = 1998
377 369
378 370 def link(src, dst):
379 371 """Hard links ``src`` to ``dst``, returning 0 or errno.
380 372
381 373 Note that the special errno ``ENOLINK`` will be returned if ``os.link`` isn't
382 374 supported by the operating system.
383 375 """
384 376
385 377 if not hasattr(os, "link"):
386 378 return ENOLINK
387 379 link_errno = 0
388 380 try:
389 381 os.link(src, dst)
390 382 except OSError as e:
391 383 link_errno = e.errno
392 384 return link_errno
393 385
394 386
395 387 def link_or_copy(src, dst):
396 388 """Attempts to hardlink ``src`` to ``dst``, copying if the link fails.
397 389
398 390 Attempts to maintain the semantics of ``shutil.copy``.
399 391
400 392 Because ``os.link`` does not overwrite files, a unique temporary file
401 393 will be used if the target already exists, then that file will be moved
402 394 into place.
403 395 """
404 396
405 397 if os.path.isdir(dst):
406 398 dst = os.path.join(dst, os.path.basename(src))
407 399
408 400 link_errno = link(src, dst)
409 401 if link_errno == errno.EEXIST:
410 402 if os.stat(src).st_ino == os.stat(dst).st_ino:
411 403 # dst is already a hard link to the correct file, so we don't need
412 404 # to do anything else. If we try to link and rename the file
413 405 # anyway, we get duplicate files - see http://bugs.python.org/issue21876
414 406 return
415 407
416 408 new_dst = dst + "-temp-%04X" %(random.randint(1, 16**4), )
417 409 try:
418 410 link_or_copy(src, new_dst)
419 411 except:
420 412 try:
421 413 os.remove(new_dst)
422 414 except OSError:
423 415 pass
424 416 raise
425 417 os.rename(new_dst, dst)
426 418 elif link_errno != 0:
427 419 # Either link isn't supported, or the filesystem doesn't support
428 420 # linking, or 'src' and 'dst' are on different filesystems.
429 421 shutil.copy(src, dst)
430 422
431 423 def ensure_dir_exists(path, mode=0o755):
432 424 """ensure that a directory exists
433 425
434 426 If it doesn't exist, try to create it and protect against a race condition
435 427 if another process is doing the same.
436 428
437 429 The default permissions are 755, which differ from os.makedirs default of 777.
438 430 """
439 431 if not os.path.exists(path):
440 432 try:
441 433 os.makedirs(path, mode=mode)
442 434 except OSError as e:
443 435 if e.errno != errno.EEXIST:
444 436 raise
445 437 elif not os.path.isdir(path):
446 438 raise IOError("%r exists but is not a directory" % path)
General Comments 0
You need to be logged in to leave comments. Login now