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