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