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