##// END OF EJS Templates
Prefer IPYTHONDIR over IPYTHON_DIR.
Bradley M. Froehle -
Show More
@@ -1,336 +1,336 b''
1 1 # encoding: utf-8
2 2 """
3 3 An application for IPython.
4 4
5 5 All top-level applications should use the classes in this module for
6 6 handling configuration and creating componenets.
7 7
8 8 The job of an :class:`Application` is to create the master configuration
9 9 object and then create the configurable objects, passing the config to them.
10 10
11 11 Authors:
12 12
13 13 * Brian Granger
14 14 * Fernando Perez
15 15 * Min RK
16 16
17 17 """
18 18
19 19 #-----------------------------------------------------------------------------
20 20 # Copyright (C) 2008-2011 The IPython Development Team
21 21 #
22 22 # Distributed under the terms of the BSD License. The full license is in
23 23 # the file COPYING, distributed as part of this software.
24 24 #-----------------------------------------------------------------------------
25 25
26 26 #-----------------------------------------------------------------------------
27 27 # Imports
28 28 #-----------------------------------------------------------------------------
29 29
30 30 import atexit
31 31 import glob
32 32 import logging
33 33 import os
34 34 import shutil
35 35 import sys
36 36
37 37 from IPython.config.application import Application, catch_config_error
38 38 from IPython.config.configurable import Configurable
39 39 from IPython.config.loader import Config, ConfigFileNotFound
40 40 from IPython.core import release, crashhandler
41 41 from IPython.core.profiledir import ProfileDir, ProfileDirError
42 42 from IPython.utils.path import get_ipython_dir, get_ipython_package_dir
43 43 from IPython.utils.traitlets import List, Unicode, Type, Bool, Dict
44 44 from IPython.utils import py3compat
45 45
46 46 #-----------------------------------------------------------------------------
47 47 # Classes and functions
48 48 #-----------------------------------------------------------------------------
49 49
50 50
51 51 #-----------------------------------------------------------------------------
52 52 # Base Application Class
53 53 #-----------------------------------------------------------------------------
54 54
55 55 # aliases and flags
56 56
57 57 base_aliases = {
58 58 'profile' : 'BaseIPythonApplication.profile',
59 59 'ipython-dir' : 'BaseIPythonApplication.ipython_dir',
60 60 'log-level' : 'Application.log_level',
61 61 }
62 62
63 63 base_flags = dict(
64 64 debug = ({'Application' : {'log_level' : logging.DEBUG}},
65 65 "set log level to logging.DEBUG (maximize logging output)"),
66 66 quiet = ({'Application' : {'log_level' : logging.CRITICAL}},
67 67 "set log level to logging.CRITICAL (minimize logging output)"),
68 68 init = ({'BaseIPythonApplication' : {
69 69 'copy_config_files' : True,
70 70 'auto_create' : True}
71 71 }, """Initialize profile with default config files. This is equivalent
72 72 to running `ipython profile create <profile>` prior to startup.
73 73 """)
74 74 )
75 75
76 76
77 77 class BaseIPythonApplication(Application):
78 78
79 79 name = Unicode(u'ipython')
80 80 description = Unicode(u'IPython: an enhanced interactive Python shell.')
81 81 version = Unicode(release.version)
82 82
83 83 aliases = Dict(base_aliases)
84 84 flags = Dict(base_flags)
85 85 classes = List([ProfileDir])
86 86
87 87 # Track whether the config_file has changed,
88 88 # because some logic happens only if we aren't using the default.
89 89 config_file_specified = Bool(False)
90 90
91 91 config_file_name = Unicode(u'ipython_config.py')
92 92 def _config_file_name_default(self):
93 93 return self.name.replace('-','_') + u'_config.py'
94 94 def _config_file_name_changed(self, name, old, new):
95 95 if new != old:
96 96 self.config_file_specified = True
97 97
98 98 # The directory that contains IPython's builtin profiles.
99 99 builtin_profile_dir = Unicode(
100 100 os.path.join(get_ipython_package_dir(), u'config', u'profile', u'default')
101 101 )
102 102
103 103 config_file_paths = List(Unicode)
104 104 def _config_file_paths_default(self):
105 105 return [os.getcwdu()]
106 106
107 107 profile = Unicode(u'default', config=True,
108 108 help="""The IPython profile to use."""
109 109 )
110 110
111 111 def _profile_changed(self, name, old, new):
112 112 self.builtin_profile_dir = os.path.join(
113 113 get_ipython_package_dir(), u'config', u'profile', new
114 114 )
115 115
116 116 ipython_dir = Unicode(get_ipython_dir(), config=True,
117 117 help="""
118 118 The name of the IPython directory. This directory is used for logging
119 119 configuration (through profiles), history storage, etc. The default
120 120 is usually $HOME/.ipython. This options can also be specified through
121 121 the environment variable IPYTHONDIR.
122 122 """
123 123 )
124 124
125 125 overwrite = Bool(False, config=True,
126 126 help="""Whether to overwrite existing config files when copying""")
127 127 auto_create = Bool(False, config=True,
128 128 help="""Whether to create profile dir if it doesn't exist""")
129 129
130 130 config_files = List(Unicode)
131 131 def _config_files_default(self):
132 132 return [u'ipython_config.py']
133 133
134 134 copy_config_files = Bool(False, config=True,
135 135 help="""Whether to install the default config files into the profile dir.
136 136 If a new profile is being created, and IPython contains config files for that
137 137 profile, then they will be staged into the new directory. Otherwise,
138 138 default config files will be automatically generated.
139 139 """)
140 140
141 141 verbose_crash = Bool(False, config=True,
142 142 help="""Create a massive crash report when IPython enconters what may be an
143 143 internal error. The default is to append a short message to the
144 144 usual traceback""")
145 145
146 146 # The class to use as the crash handler.
147 147 crash_handler_class = Type(crashhandler.CrashHandler)
148 148
149 149 def __init__(self, **kwargs):
150 150 super(BaseIPythonApplication, self).__init__(**kwargs)
151 151 # ensure even default IPYTHONDIR exists
152 152 if not os.path.exists(self.ipython_dir):
153 153 self._ipython_dir_changed('ipython_dir', self.ipython_dir, self.ipython_dir)
154 154
155 155 #-------------------------------------------------------------------------
156 156 # Various stages of Application creation
157 157 #-------------------------------------------------------------------------
158 158
159 159 def init_crash_handler(self):
160 160 """Create a crash handler, typically setting sys.excepthook to it."""
161 161 self.crash_handler = self.crash_handler_class(self)
162 162 sys.excepthook = self.excepthook
163 163 def unset_crashhandler():
164 164 sys.excepthook = sys.__excepthook__
165 165 atexit.register(unset_crashhandler)
166 166
167 167 def excepthook(self, etype, evalue, tb):
168 168 """this is sys.excepthook after init_crashhandler
169 169
170 170 set self.verbose_crash=True to use our full crashhandler, instead of
171 171 a regular traceback with a short message (crash_handler_lite)
172 172 """
173 173
174 174 if self.verbose_crash:
175 175 return self.crash_handler(etype, evalue, tb)
176 176 else:
177 177 return crashhandler.crash_handler_lite(etype, evalue, tb)
178 178
179 179 def _ipython_dir_changed(self, name, old, new):
180 180 if old in sys.path:
181 181 sys.path.remove(old)
182 182 sys.path.append(os.path.abspath(new))
183 183 if not os.path.isdir(new):
184 184 os.makedirs(new, mode=0777)
185 185 readme = os.path.join(new, 'README')
186 186 if not os.path.exists(readme):
187 187 path = os.path.join(get_ipython_package_dir(), u'config', u'profile')
188 188 shutil.copy(os.path.join(path, 'README'), readme)
189 self.log.debug("IPYTHON_DIR set to: %s" % new)
189 self.log.debug("IPYTHONDIR set to: %s" % new)
190 190
191 191 def load_config_file(self, suppress_errors=True):
192 192 """Load the config file.
193 193
194 194 By default, errors in loading config are handled, and a warning
195 195 printed on screen. For testing, the suppress_errors option is set
196 196 to False, so errors will make tests fail.
197 197 """
198 198 self.log.debug("Searching path %s for config files", self.config_file_paths)
199 199 base_config = 'ipython_config.py'
200 200 self.log.debug("Attempting to load config file: %s" %
201 201 base_config)
202 202 try:
203 203 Application.load_config_file(
204 204 self,
205 205 base_config,
206 206 path=self.config_file_paths
207 207 )
208 208 except ConfigFileNotFound:
209 209 # ignore errors loading parent
210 210 self.log.debug("Config file %s not found", base_config)
211 211 pass
212 212 if self.config_file_name == base_config:
213 213 # don't load secondary config
214 214 return
215 215 self.log.debug("Attempting to load config file: %s" %
216 216 self.config_file_name)
217 217 try:
218 218 Application.load_config_file(
219 219 self,
220 220 self.config_file_name,
221 221 path=self.config_file_paths
222 222 )
223 223 except ConfigFileNotFound:
224 224 # Only warn if the default config file was NOT being used.
225 225 if self.config_file_specified:
226 226 msg = self.log.warn
227 227 else:
228 228 msg = self.log.debug
229 229 msg("Config file not found, skipping: %s", self.config_file_name)
230 230 except:
231 231 # For testing purposes.
232 232 if not suppress_errors:
233 233 raise
234 234 self.log.warn("Error loading config file: %s" %
235 235 self.config_file_name, exc_info=True)
236 236
237 237 def init_profile_dir(self):
238 238 """initialize the profile dir"""
239 239 try:
240 240 # location explicitly specified:
241 241 location = self.config.ProfileDir.location
242 242 except AttributeError:
243 243 # location not specified, find by profile name
244 244 try:
245 245 p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
246 246 except ProfileDirError:
247 247 # not found, maybe create it (always create default profile)
248 248 if self.auto_create or self.profile=='default':
249 249 try:
250 250 p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
251 251 except ProfileDirError:
252 252 self.log.fatal("Could not create profile: %r"%self.profile)
253 253 self.exit(1)
254 254 else:
255 255 self.log.info("Created profile dir: %r"%p.location)
256 256 else:
257 257 self.log.fatal("Profile %r not found."%self.profile)
258 258 self.exit(1)
259 259 else:
260 260 self.log.info("Using existing profile dir: %r"%p.location)
261 261 else:
262 262 # location is fully specified
263 263 try:
264 264 p = ProfileDir.find_profile_dir(location, self.config)
265 265 except ProfileDirError:
266 266 # not found, maybe create it
267 267 if self.auto_create:
268 268 try:
269 269 p = ProfileDir.create_profile_dir(location, self.config)
270 270 except ProfileDirError:
271 271 self.log.fatal("Could not create profile directory: %r"%location)
272 272 self.exit(1)
273 273 else:
274 274 self.log.info("Creating new profile dir: %r"%location)
275 275 else:
276 276 self.log.fatal("Profile directory %r not found."%location)
277 277 self.exit(1)
278 278 else:
279 279 self.log.info("Using existing profile dir: %r"%location)
280 280
281 281 self.profile_dir = p
282 282 self.config_file_paths.append(p.location)
283 283
284 284 def init_config_files(self):
285 285 """[optionally] copy default config files into profile dir."""
286 286 # copy config files
287 287 path = self.builtin_profile_dir
288 288 if self.copy_config_files:
289 289 src = self.profile
290 290
291 291 cfg = self.config_file_name
292 292 if path and os.path.exists(os.path.join(path, cfg)):
293 293 self.log.warn("Staging %r from %s into %r [overwrite=%s]"%(
294 294 cfg, src, self.profile_dir.location, self.overwrite)
295 295 )
296 296 self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite)
297 297 else:
298 298 self.stage_default_config_file()
299 299 else:
300 300 # Still stage *bundled* config files, but not generated ones
301 301 # This is necessary for `ipython profile=sympy` to load the profile
302 302 # on the first go
303 303 files = glob.glob(os.path.join(path, '*.py'))
304 304 for fullpath in files:
305 305 cfg = os.path.basename(fullpath)
306 306 if self.profile_dir.copy_config_file(cfg, path=path, overwrite=False):
307 307 # file was copied
308 308 self.log.warn("Staging bundled %s from %s into %r"%(
309 309 cfg, self.profile, self.profile_dir.location)
310 310 )
311 311
312 312
313 313 def stage_default_config_file(self):
314 314 """auto generate default config file, and stage it into the profile."""
315 315 s = self.generate_config_file()
316 316 fname = os.path.join(self.profile_dir.location, self.config_file_name)
317 317 if self.overwrite or not os.path.exists(fname):
318 318 self.log.warn("Generating default config file: %r"%(fname))
319 319 with open(fname, 'w') as f:
320 320 f.write(s)
321 321
322 322 @catch_config_error
323 323 def initialize(self, argv=None):
324 324 # don't hook up crash handler before parsing command-line
325 325 self.parse_command_line(argv)
326 326 self.init_crash_handler()
327 327 if self.subapp is not None:
328 328 # stop here if subapp is taking over
329 329 return
330 330 cl_config = self.config
331 331 self.init_profile_dir()
332 332 self.init_config_files()
333 333 self.load_config_file()
334 334 # enforce cl-opts override configfile opts:
335 335 self.update_config(cl_config)
336 336
@@ -1,464 +1,464 b''
1 1 # encoding: utf-8
2 2 """
3 3 Utilities for path handling.
4 4 """
5 5
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (C) 2008-2011 The IPython Development Team
8 8 #
9 9 # Distributed under the terms of the BSD License. The full license is in
10 10 # the file COPYING, distributed as part of this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16
17 17 import os
18 18 import sys
19 19 import tempfile
20 20 import warnings
21 21 from hashlib import md5
22 22
23 23 import IPython
24 24 from IPython.utils.process import system
25 25 from IPython.utils.importstring import import_item
26 26 from IPython.utils import py3compat
27 27
28 28 #-----------------------------------------------------------------------------
29 29 # Code
30 30 #-----------------------------------------------------------------------------
31 31
32 32 fs_encoding = sys.getfilesystemencoding()
33 33
34 34 def _get_long_path_name(path):
35 35 """Dummy no-op."""
36 36 return path
37 37
38 38 def _writable_dir(path):
39 39 """Whether `path` is a directory, to which the user has write access."""
40 40 return os.path.isdir(path) and os.access(path, os.W_OK)
41 41
42 42 if sys.platform == 'win32':
43 43 def _get_long_path_name(path):
44 44 """Get a long path name (expand ~) on Windows using ctypes.
45 45
46 46 Examples
47 47 --------
48 48
49 49 >>> get_long_path_name('c:\\docume~1')
50 50 u'c:\\\\Documents and Settings'
51 51
52 52 """
53 53 try:
54 54 import ctypes
55 55 except ImportError:
56 56 raise ImportError('you need to have ctypes installed for this to work')
57 57 _GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
58 58 _GetLongPathName.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p,
59 59 ctypes.c_uint ]
60 60
61 61 buf = ctypes.create_unicode_buffer(260)
62 62 rv = _GetLongPathName(path, buf, 260)
63 63 if rv == 0 or rv > 260:
64 64 return path
65 65 else:
66 66 return buf.value
67 67
68 68
69 69 def get_long_path_name(path):
70 70 """Expand a path into its long form.
71 71
72 72 On Windows this expands any ~ in the paths. On other platforms, it is
73 73 a null operation.
74 74 """
75 75 return _get_long_path_name(path)
76 76
77 77
78 78 def unquote_filename(name, win32=(sys.platform=='win32')):
79 79 """ On Windows, remove leading and trailing quotes from filenames.
80 80 """
81 81 if win32:
82 82 if name.startswith(("'", '"')) and name.endswith(("'", '"')):
83 83 name = name[1:-1]
84 84 return name
85 85
86 86
87 87 def get_py_filename(name, force_win32=None):
88 88 """Return a valid python filename in the current directory.
89 89
90 90 If the given name is not a file, it adds '.py' and searches again.
91 91 Raises IOError with an informative message if the file isn't found.
92 92
93 93 On Windows, apply Windows semantics to the filename. In particular, remove
94 94 any quoting that has been applied to it. This option can be forced for
95 95 testing purposes.
96 96 """
97 97
98 98 name = os.path.expanduser(name)
99 99 if force_win32 is None:
100 100 win32 = (sys.platform == 'win32')
101 101 else:
102 102 win32 = force_win32
103 103 name = unquote_filename(name, win32=win32)
104 104 if not os.path.isfile(name) and not name.endswith('.py'):
105 105 name += '.py'
106 106 if os.path.isfile(name):
107 107 return name
108 108 else:
109 109 raise IOError,'File `%r` not found.' % name
110 110
111 111
112 112 def filefind(filename, path_dirs=None):
113 113 """Find a file by looking through a sequence of paths.
114 114
115 115 This iterates through a sequence of paths looking for a file and returns
116 116 the full, absolute path of the first occurence of the file. If no set of
117 117 path dirs is given, the filename is tested as is, after running through
118 118 :func:`expandvars` and :func:`expanduser`. Thus a simple call::
119 119
120 120 filefind('myfile.txt')
121 121
122 122 will find the file in the current working dir, but::
123 123
124 124 filefind('~/myfile.txt')
125 125
126 126 Will find the file in the users home directory. This function does not
127 127 automatically try any paths, such as the cwd or the user's home directory.
128 128
129 129 Parameters
130 130 ----------
131 131 filename : str
132 132 The filename to look for.
133 133 path_dirs : str, None or sequence of str
134 134 The sequence of paths to look for the file in. If None, the filename
135 135 need to be absolute or be in the cwd. If a string, the string is
136 136 put into a sequence and the searched. If a sequence, walk through
137 137 each element and join with ``filename``, calling :func:`expandvars`
138 138 and :func:`expanduser` before testing for existence.
139 139
140 140 Returns
141 141 -------
142 142 Raises :exc:`IOError` or returns absolute path to file.
143 143 """
144 144
145 145 # If paths are quoted, abspath gets confused, strip them...
146 146 filename = filename.strip('"').strip("'")
147 147 # If the input is an absolute path, just check it exists
148 148 if os.path.isabs(filename) and os.path.isfile(filename):
149 149 return filename
150 150
151 151 if path_dirs is None:
152 152 path_dirs = ("",)
153 153 elif isinstance(path_dirs, basestring):
154 154 path_dirs = (path_dirs,)
155 155
156 156 for path in path_dirs:
157 157 if path == '.': path = os.getcwdu()
158 158 testname = expand_path(os.path.join(path, filename))
159 159 if os.path.isfile(testname):
160 160 return os.path.abspath(testname)
161 161
162 162 raise IOError("File %r does not exist in any of the search paths: %r" %
163 163 (filename, path_dirs) )
164 164
165 165
166 166 class HomeDirError(Exception):
167 167 pass
168 168
169 169
170 170 def get_home_dir(require_writable=False):
171 171 """Return the 'home' directory, as a unicode string.
172 172
173 173 * First, check for frozen env in case of py2exe
174 174 * Otherwise, defer to os.path.expanduser('~')
175 175
176 176 See stdlib docs for how this is determined.
177 177 $HOME is first priority on *ALL* platforms.
178 178
179 179 Parameters
180 180 ----------
181 181
182 182 require_writable : bool [default: False]
183 183 if True:
184 184 guarantees the return value is a writable directory, otherwise
185 185 raises HomeDirError
186 186 if False:
187 187 The path is resolved, but it is not guaranteed to exist or be writable.
188 188 """
189 189
190 190 # first, check py2exe distribution root directory for _ipython.
191 191 # This overrides all. Normally does not exist.
192 192
193 193 if hasattr(sys, "frozen"): #Is frozen by py2exe
194 194 if '\\library.zip\\' in IPython.__file__.lower():#libraries compressed to zip-file
195 195 root, rest = IPython.__file__.lower().split('library.zip')
196 196 else:
197 197 root=os.path.join(os.path.split(IPython.__file__)[0],"../../")
198 198 root=os.path.abspath(root).rstrip('\\')
199 199 if _writable_dir(os.path.join(root, '_ipython')):
200 200 os.environ["IPYKITROOT"] = root
201 201 return py3compat.cast_unicode(root, fs_encoding)
202 202
203 203 homedir = os.path.expanduser('~')
204 204 # Next line will make things work even when /home/ is a symlink to
205 205 # /usr/home as it is on FreeBSD, for example
206 206 homedir = os.path.realpath(homedir)
207 207
208 208 if not _writable_dir(homedir) and os.name == 'nt':
209 209 # expanduser failed, use the registry to get the 'My Documents' folder.
210 210 try:
211 211 import _winreg as wreg
212 212 key = wreg.OpenKey(
213 213 wreg.HKEY_CURRENT_USER,
214 214 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
215 215 )
216 216 homedir = wreg.QueryValueEx(key,'Personal')[0]
217 217 key.Close()
218 218 except:
219 219 pass
220 220
221 221 if (not require_writable) or _writable_dir(homedir):
222 222 return py3compat.cast_unicode(homedir, fs_encoding)
223 223 else:
224 224 raise HomeDirError('%s is not a writable dir, '
225 225 'set $HOME environment variable to override' % homedir)
226 226
227 227 def get_xdg_dir():
228 228 """Return the XDG_CONFIG_HOME, if it is defined and exists, else None.
229 229
230 230 This is only for posix (Linux,Unix,OS X, etc) systems.
231 231 """
232 232
233 233 env = os.environ
234 234
235 235 if os.name == 'posix':
236 236 # Linux, Unix, AIX, OS X
237 237 # use ~/.config if empty OR not set
238 238 xdg = env.get("XDG_CONFIG_HOME", None) or os.path.join(get_home_dir(), '.config')
239 239 if xdg and _writable_dir(xdg):
240 240 return py3compat.cast_unicode(xdg, fs_encoding)
241 241
242 242 return None
243 243
244 244
245 245 def get_ipython_dir():
246 246 """Get the IPython directory for this platform and user.
247 247
248 248 This uses the logic in `get_home_dir` to find the home directory
249 249 and then adds .ipython to the end of the path.
250 250 """
251 251
252 252 env = os.environ
253 253 pjoin = os.path.join
254 254
255 255
256 256 ipdir_def = '.ipython'
257 257 xdg_def = 'ipython'
258 258
259 259 home_dir = get_home_dir()
260 260 xdg_dir = get_xdg_dir()
261 261
262 262 # import pdb; pdb.set_trace() # dbg
263 ipdir = env.get('IPYTHON_DIR', env.get('IPYTHONDIR', None))
263 ipdir = env.get('IPYTHONDIR', env.get('IPYTHON_DIR', None))
264 264 if ipdir is None:
265 265 # not set explicitly, use XDG_CONFIG_HOME or HOME
266 266 home_ipdir = pjoin(home_dir, ipdir_def)
267 267 if xdg_dir:
268 268 # use XDG, as long as the user isn't already
269 269 # using $HOME/.ipython and *not* XDG/ipython
270 270
271 271 xdg_ipdir = pjoin(xdg_dir, xdg_def)
272 272
273 273 if _writable_dir(xdg_ipdir) or not _writable_dir(home_ipdir):
274 274 ipdir = xdg_ipdir
275 275
276 276 if ipdir is None:
277 277 # not using XDG
278 278 ipdir = home_ipdir
279 279
280 280 ipdir = os.path.normpath(os.path.expanduser(ipdir))
281 281
282 282 if os.path.exists(ipdir) and not _writable_dir(ipdir):
283 283 # ipdir exists, but is not writable
284 284 warnings.warn("IPython dir '%s' is not a writable location,"
285 285 " using a temp directory."%ipdir)
286 286 ipdir = tempfile.mkdtemp()
287 287 elif not os.path.exists(ipdir):
288 288 parent = ipdir.rsplit(os.path.sep, 1)[0]
289 289 if not _writable_dir(parent):
290 290 # ipdir does not exist and parent isn't writable
291 291 warnings.warn("IPython parent '%s' is not a writable location,"
292 292 " using a temp directory."%parent)
293 293 ipdir = tempfile.mkdtemp()
294 294
295 295 return py3compat.cast_unicode(ipdir, fs_encoding)
296 296
297 297
298 298 def get_ipython_package_dir():
299 299 """Get the base directory where IPython itself is installed."""
300 300 ipdir = os.path.dirname(IPython.__file__)
301 301 return py3compat.cast_unicode(ipdir, fs_encoding)
302 302
303 303
304 304 def get_ipython_module_path(module_str):
305 305 """Find the path to an IPython module in this version of IPython.
306 306
307 307 This will always find the version of the module that is in this importable
308 308 IPython package. This will always return the path to the ``.py``
309 309 version of the module.
310 310 """
311 311 if module_str == 'IPython':
312 312 return os.path.join(get_ipython_package_dir(), '__init__.py')
313 313 mod = import_item(module_str)
314 314 the_path = mod.__file__.replace('.pyc', '.py')
315 315 the_path = the_path.replace('.pyo', '.py')
316 316 return py3compat.cast_unicode(the_path, fs_encoding)
317 317
318 318 def locate_profile(profile='default'):
319 319 """Find the path to the folder associated with a given profile.
320 320
321 321 I.e. find $IPYTHONDIR/profile_whatever.
322 322 """
323 323 from IPython.core.profiledir import ProfileDir, ProfileDirError
324 324 try:
325 325 pd = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), profile)
326 326 except ProfileDirError:
327 327 # IOError makes more sense when people are expecting a path
328 328 raise IOError("Couldn't find profile %r" % profile)
329 329 return pd.location
330 330
331 331 def expand_path(s):
332 332 """Expand $VARS and ~names in a string, like a shell
333 333
334 334 :Examples:
335 335
336 336 In [2]: os.environ['FOO']='test'
337 337
338 338 In [3]: expand_path('variable FOO is $FOO')
339 339 Out[3]: 'variable FOO is test'
340 340 """
341 341 # This is a pretty subtle hack. When expand user is given a UNC path
342 342 # on Windows (\\server\share$\%username%), os.path.expandvars, removes
343 343 # the $ to get (\\server\share\%username%). I think it considered $
344 344 # alone an empty var. But, we need the $ to remains there (it indicates
345 345 # a hidden share).
346 346 if os.name=='nt':
347 347 s = s.replace('$\\', 'IPYTHON_TEMP')
348 348 s = os.path.expandvars(os.path.expanduser(s))
349 349 if os.name=='nt':
350 350 s = s.replace('IPYTHON_TEMP', '$\\')
351 351 return s
352 352
353 353
354 354 def target_outdated(target,deps):
355 355 """Determine whether a target is out of date.
356 356
357 357 target_outdated(target,deps) -> 1/0
358 358
359 359 deps: list of filenames which MUST exist.
360 360 target: single filename which may or may not exist.
361 361
362 362 If target doesn't exist or is older than any file listed in deps, return
363 363 true, otherwise return false.
364 364 """
365 365 try:
366 366 target_time = os.path.getmtime(target)
367 367 except os.error:
368 368 return 1
369 369 for dep in deps:
370 370 dep_time = os.path.getmtime(dep)
371 371 if dep_time > target_time:
372 372 #print "For target",target,"Dep failed:",dep # dbg
373 373 #print "times (dep,tar):",dep_time,target_time # dbg
374 374 return 1
375 375 return 0
376 376
377 377
378 378 def target_update(target,deps,cmd):
379 379 """Update a target with a given command given a list of dependencies.
380 380
381 381 target_update(target,deps,cmd) -> runs cmd if target is outdated.
382 382
383 383 This is just a wrapper around target_outdated() which calls the given
384 384 command if target is outdated."""
385 385
386 386 if target_outdated(target,deps):
387 387 system(cmd)
388 388
389 389 def filehash(path):
390 390 """Make an MD5 hash of a file, ignoring any differences in line
391 391 ending characters."""
392 392 with open(path, "rU") as f:
393 393 return md5(py3compat.str_to_bytes(f.read())).hexdigest()
394 394
395 395 # If the config is unmodified from the default, we'll just delete it.
396 396 # These are consistent for 0.10.x, thankfully. We're not going to worry about
397 397 # older versions.
398 398 old_config_md5 = {'ipy_user_conf.py': 'fc108bedff4b9a00f91fa0a5999140d3',
399 399 'ipythonrc': '12a68954f3403eea2eec09dc8fe5a9b5'}
400 400
401 401 def check_for_old_config(ipython_dir=None):
402 402 """Check for old config files, and present a warning if they exist.
403 403
404 404 A link to the docs of the new config is included in the message.
405 405
406 406 This should mitigate confusion with the transition to the new
407 407 config system in 0.11.
408 408 """
409 409 if ipython_dir is None:
410 410 ipython_dir = get_ipython_dir()
411 411
412 412 old_configs = ['ipy_user_conf.py', 'ipythonrc', 'ipython_config.py']
413 413 warned = False
414 414 for cfg in old_configs:
415 415 f = os.path.join(ipython_dir, cfg)
416 416 if os.path.exists(f):
417 417 if filehash(f) == old_config_md5.get(cfg, ''):
418 418 os.unlink(f)
419 419 else:
420 420 warnings.warn("Found old IPython config file %r (modified by user)"%f)
421 421 warned = True
422 422
423 423 if warned:
424 424 warnings.warn("""
425 425 The IPython configuration system has changed as of 0.11, and these files will
426 426 be ignored. See http://ipython.github.com/ipython-doc/dev/config for details
427 427 of the new config system.
428 428 To start configuring IPython, do `ipython profile create`, and edit
429 429 `ipython_config.py` in <ipython_dir>/profile_default.
430 430 If you need to leave the old config files in place for an older version of
431 431 IPython and want to suppress this warning message, set
432 432 `c.InteractiveShellApp.ignore_old_config=True` in the new config.""")
433 433
434 434 def get_security_file(filename, profile='default'):
435 435 """Return the absolute path of a security file given by filename and profile
436 436
437 437 This allows users and developers to find security files without
438 438 knowledge of the IPython directory structure. The search path
439 439 will be ['.', profile.security_dir]
440 440
441 441 Parameters
442 442 ----------
443 443
444 444 filename : str
445 445 The file to be found. If it is passed as an absolute path, it will
446 446 simply be returned.
447 447 profile : str [default: 'default']
448 448 The name of the profile to search. Leaving this unspecified
449 449 The file to be found. If it is passed as an absolute path, fname will
450 450 simply be returned.
451 451
452 452 Returns
453 453 -------
454 454 Raises :exc:`IOError` if file not found or returns absolute path to file.
455 455 """
456 456 # import here, because profiledir also imports from utils.path
457 457 from IPython.core.profiledir import ProfileDir
458 458 try:
459 459 pd = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), profile)
460 460 except Exception:
461 461 # will raise ProfileDirError if no such profile
462 462 raise IOError("Profile %r not found")
463 463 return filefind(filename, ['.', pd.security_dir])
464 464
General Comments 0
You need to be logged in to leave comments. Login now