##// END OF EJS Templates
create [nb]extensions dirs in skeleton IPYTHONDIR
MinRK -
Show More
@@ -1,373 +1,383 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 handling configuration and creating componenets.
6 handling configuration and creating configurables.
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 # Copyright (C) 2008-2011 The IPython Development Team
20 # Copyright (C) 2008 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 import errno
31 32 import glob
32 33 import logging
33 34 import os
34 35 import shutil
35 36 import sys
36 37
37 38 from IPython.config.application import Application, catch_config_error
38 39 from IPython.config.loader import ConfigFileNotFound
39 40 from IPython.core import release, crashhandler
40 41 from IPython.core.profiledir import ProfileDir, ProfileDirError
41 42 from IPython.utils.path import get_ipython_dir, get_ipython_package_dir
42 43 from IPython.utils.traitlets import List, Unicode, Type, Bool, Dict, Set, Instance
43 44
44 45 #-----------------------------------------------------------------------------
45 46 # Classes and functions
46 47 #-----------------------------------------------------------------------------
47 48
48 49
49 50 #-----------------------------------------------------------------------------
50 51 # Base Application Class
51 52 #-----------------------------------------------------------------------------
52 53
53 54 # aliases and flags
54 55
55 56 base_aliases = {
56 57 'profile-dir' : 'ProfileDir.location',
57 58 'profile' : 'BaseIPythonApplication.profile',
58 59 'ipython-dir' : 'BaseIPythonApplication.ipython_dir',
59 60 'log-level' : 'Application.log_level',
60 61 'config' : 'BaseIPythonApplication.extra_config_file',
61 62 }
62 63
63 64 base_flags = dict(
64 65 debug = ({'Application' : {'log_level' : logging.DEBUG}},
65 66 "set log level to logging.DEBUG (maximize logging output)"),
66 67 quiet = ({'Application' : {'log_level' : logging.CRITICAL}},
67 68 "set log level to logging.CRITICAL (minimize logging output)"),
68 69 init = ({'BaseIPythonApplication' : {
69 70 'copy_config_files' : True,
70 71 'auto_create' : True}
71 72 }, """Initialize profile with default config files. This is equivalent
72 73 to running `ipython profile create <profile>` prior to startup.
73 74 """)
74 75 )
75 76
76 77
77 78 class BaseIPythonApplication(Application):
78 79
79 80 name = Unicode(u'ipython')
80 81 description = Unicode(u'IPython: an enhanced interactive Python shell.')
81 82 version = Unicode(release.version)
82 83
83 84 aliases = Dict(base_aliases)
84 85 flags = Dict(base_flags)
85 86 classes = List([ProfileDir])
86 87
87 88 # Track whether the config_file has changed,
88 89 # because some logic happens only if we aren't using the default.
89 90 config_file_specified = Set()
90 91
91 92 config_file_name = Unicode()
92 93 def _config_file_name_default(self):
93 94 return self.name.replace('-','_') + u'_config.py'
94 95 def _config_file_name_changed(self, name, old, new):
95 96 if new != old:
96 97 self.config_file_specified.add(new)
97 98
98 99 # The directory that contains IPython's builtin profiles.
99 100 builtin_profile_dir = Unicode(
100 101 os.path.join(get_ipython_package_dir(), u'config', u'profile', u'default')
101 102 )
102 103
103 104 config_file_paths = List(Unicode)
104 105 def _config_file_paths_default(self):
105 106 return [os.getcwdu()]
106 107
107 108 extra_config_file = Unicode(config=True,
108 109 help="""Path to an extra config file to load.
109 110
110 111 If specified, load this config file in addition to any other IPython config.
111 112 """)
112 113 def _extra_config_file_changed(self, name, old, new):
113 114 try:
114 115 self.config_files.remove(old)
115 116 except ValueError:
116 117 pass
117 118 self.config_file_specified.add(new)
118 119 self.config_files.append(new)
119 120
120 121 profile = Unicode(u'default', config=True,
121 122 help="""The IPython profile to use."""
122 123 )
123 124
124 125 def _profile_changed(self, name, old, new):
125 126 self.builtin_profile_dir = os.path.join(
126 127 get_ipython_package_dir(), u'config', u'profile', new
127 128 )
128 129
129 ipython_dir = Unicode(get_ipython_dir(), config=True,
130 ipython_dir = Unicode(config=True,
130 131 help="""
131 132 The name of the IPython directory. This directory is used for logging
132 133 configuration (through profiles), history storage, etc. The default
133 134 is usually $HOME/.ipython. This options can also be specified through
134 135 the environment variable IPYTHONDIR.
135 136 """
136 137 )
138 def _ipython_dir_default(self):
139 d = get_ipython_dir()
140 self._ipython_dir_changed('ipython_dir', '', d)
141 return d
142
137 143 _in_init_profile_dir = False
138 144 profile_dir = Instance(ProfileDir)
139 145 def _profile_dir_default(self):
140 146 # avoid recursion
141 147 if self._in_init_profile_dir:
142 148 return
143 149 # profile_dir requested early, force initialization
144 150 self.init_profile_dir()
145 151 return self.profile_dir
146 152
147 153 overwrite = Bool(False, config=True,
148 154 help="""Whether to overwrite existing config files when copying""")
149 155 auto_create = Bool(False, config=True,
150 156 help="""Whether to create profile dir if it doesn't exist""")
151 157
152 158 config_files = List(Unicode)
153 159 def _config_files_default(self):
154 160 return [self.config_file_name]
155 161
156 162 copy_config_files = Bool(False, config=True,
157 163 help="""Whether to install the default config files into the profile dir.
158 164 If a new profile is being created, and IPython contains config files for that
159 165 profile, then they will be staged into the new directory. Otherwise,
160 166 default config files will be automatically generated.
161 167 """)
162 168
163 169 verbose_crash = Bool(False, config=True,
164 170 help="""Create a massive crash report when IPython encounters what may be an
165 171 internal error. The default is to append a short message to the
166 172 usual traceback""")
167 173
168 174 # The class to use as the crash handler.
169 175 crash_handler_class = Type(crashhandler.CrashHandler)
170 176
171 177 @catch_config_error
172 178 def __init__(self, **kwargs):
173 179 super(BaseIPythonApplication, self).__init__(**kwargs)
174 180 # ensure current working directory exists
175 181 try:
176 182 directory = os.getcwdu()
177 183 except:
178 184 # raise exception
179 185 self.log.error("Current working directory doesn't exist.")
180 186 raise
181 187
182 # ensure even default IPYTHONDIR exists
183 if not os.path.exists(self.ipython_dir):
184 self._ipython_dir_changed('ipython_dir', self.ipython_dir, self.ipython_dir)
185
186 188 #-------------------------------------------------------------------------
187 189 # Various stages of Application creation
188 190 #-------------------------------------------------------------------------
189 191
190 192 def init_crash_handler(self):
191 193 """Create a crash handler, typically setting sys.excepthook to it."""
192 194 self.crash_handler = self.crash_handler_class(self)
193 195 sys.excepthook = self.excepthook
194 196 def unset_crashhandler():
195 197 sys.excepthook = sys.__excepthook__
196 198 atexit.register(unset_crashhandler)
197 199
198 200 def excepthook(self, etype, evalue, tb):
199 201 """this is sys.excepthook after init_crashhandler
200 202
201 203 set self.verbose_crash=True to use our full crashhandler, instead of
202 204 a regular traceback with a short message (crash_handler_lite)
203 205 """
204 206
205 207 if self.verbose_crash:
206 208 return self.crash_handler(etype, evalue, tb)
207 209 else:
208 210 return crashhandler.crash_handler_lite(etype, evalue, tb)
209 211
210 212 def _ipython_dir_changed(self, name, old, new):
211 213 if old in sys.path:
212 214 sys.path.remove(old)
213 215 sys.path.append(os.path.abspath(new))
214 216 if not os.path.isdir(new):
215 217 os.makedirs(new, mode=0o777)
216 218 readme = os.path.join(new, 'README')
217 if not os.path.exists(readme):
218 path = os.path.join(get_ipython_package_dir(), u'config', u'profile')
219 shutil.copy(os.path.join(path, 'README'), readme)
219 readme_src = os.path.join(get_ipython_package_dir(), u'config', u'profile', 'README')
220 if not os.path.exists(readme) and os.path.exists(readme_src):
221 shutil.copy(readme_src, readme)
222 for d in ('extensions', 'nbextensions'):
223 path = os.path.join(new, d)
224 if not os.path.exists(path):
225 try:
226 os.mkdir(path)
227 except OSError as e:
228 if e.errno != errno.EEXIST:
229 self.log.error("couldn't create path %s: %s", path, e)
220 230 self.log.debug("IPYTHONDIR set to: %s" % new)
221 231
222 232 def load_config_file(self, suppress_errors=True):
223 233 """Load the config file.
224 234
225 235 By default, errors in loading config are handled, and a warning
226 236 printed on screen. For testing, the suppress_errors option is set
227 237 to False, so errors will make tests fail.
228 238 """
229 239 self.log.debug("Searching path %s for config files", self.config_file_paths)
230 240 base_config = 'ipython_config.py'
231 241 self.log.debug("Attempting to load config file: %s" %
232 242 base_config)
233 243 try:
234 244 Application.load_config_file(
235 245 self,
236 246 base_config,
237 247 path=self.config_file_paths
238 248 )
239 249 except ConfigFileNotFound:
240 250 # ignore errors loading parent
241 251 self.log.debug("Config file %s not found", base_config)
242 252 pass
243 253
244 254 for config_file_name in self.config_files:
245 255 if not config_file_name or config_file_name == base_config:
246 256 continue
247 257 self.log.debug("Attempting to load config file: %s" %
248 258 self.config_file_name)
249 259 try:
250 260 Application.load_config_file(
251 261 self,
252 262 config_file_name,
253 263 path=self.config_file_paths
254 264 )
255 265 except ConfigFileNotFound:
256 266 # Only warn if the default config file was NOT being used.
257 267 if config_file_name in self.config_file_specified:
258 268 msg = self.log.warn
259 269 else:
260 270 msg = self.log.debug
261 271 msg("Config file not found, skipping: %s", config_file_name)
262 272 except:
263 273 # For testing purposes.
264 274 if not suppress_errors:
265 275 raise
266 276 self.log.warn("Error loading config file: %s" %
267 277 self.config_file_name, exc_info=True)
268 278
269 279 def init_profile_dir(self):
270 280 """initialize the profile dir"""
271 281 self._in_init_profile_dir = True
272 282 if self.profile_dir is not None:
273 283 # already ran
274 284 return
275 285 try:
276 286 # location explicitly specified:
277 287 location = self.config.ProfileDir.location
278 288 except AttributeError:
279 289 # location not specified, find by profile name
280 290 try:
281 291 p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
282 292 except ProfileDirError:
283 293 # not found, maybe create it (always create default profile)
284 294 if self.auto_create or self.profile == 'default':
285 295 try:
286 296 p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
287 297 except ProfileDirError:
288 298 self.log.fatal("Could not create profile: %r"%self.profile)
289 299 self.exit(1)
290 300 else:
291 301 self.log.info("Created profile dir: %r"%p.location)
292 302 else:
293 303 self.log.fatal("Profile %r not found."%self.profile)
294 304 self.exit(1)
295 305 else:
296 306 self.log.info("Using existing profile dir: %r"%p.location)
297 307 else:
298 308 # location is fully specified
299 309 try:
300 310 p = ProfileDir.find_profile_dir(location, self.config)
301 311 except ProfileDirError:
302 312 # not found, maybe create it
303 313 if self.auto_create:
304 314 try:
305 315 p = ProfileDir.create_profile_dir(location, self.config)
306 316 except ProfileDirError:
307 317 self.log.fatal("Could not create profile directory: %r"%location)
308 318 self.exit(1)
309 319 else:
310 320 self.log.info("Creating new profile dir: %r"%location)
311 321 else:
312 322 self.log.fatal("Profile directory %r not found."%location)
313 323 self.exit(1)
314 324 else:
315 325 self.log.info("Using existing profile dir: %r"%location)
316 326
317 327 self.profile_dir = p
318 328 self.config_file_paths.append(p.location)
319 329 self._in_init_profile_dir = False
320 330
321 331 def init_config_files(self):
322 332 """[optionally] copy default config files into profile dir."""
323 333 # copy config files
324 334 path = self.builtin_profile_dir
325 335 if self.copy_config_files:
326 336 src = self.profile
327 337
328 338 cfg = self.config_file_name
329 339 if path and os.path.exists(os.path.join(path, cfg)):
330 340 self.log.warn("Staging %r from %s into %r [overwrite=%s]"%(
331 341 cfg, src, self.profile_dir.location, self.overwrite)
332 342 )
333 343 self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite)
334 344 else:
335 345 self.stage_default_config_file()
336 346 else:
337 347 # Still stage *bundled* config files, but not generated ones
338 348 # This is necessary for `ipython profile=sympy` to load the profile
339 349 # on the first go
340 350 files = glob.glob(os.path.join(path, '*.py'))
341 351 for fullpath in files:
342 352 cfg = os.path.basename(fullpath)
343 353 if self.profile_dir.copy_config_file(cfg, path=path, overwrite=False):
344 354 # file was copied
345 355 self.log.warn("Staging bundled %s from %s into %r"%(
346 356 cfg, self.profile, self.profile_dir.location)
347 357 )
348 358
349 359
350 360 def stage_default_config_file(self):
351 361 """auto generate default config file, and stage it into the profile."""
352 362 s = self.generate_config_file()
353 363 fname = os.path.join(self.profile_dir.location, self.config_file_name)
354 364 if self.overwrite or not os.path.exists(fname):
355 365 self.log.warn("Generating default config file: %r"%(fname))
356 366 with open(fname, 'w') as f:
357 367 f.write(s)
358 368
359 369 @catch_config_error
360 370 def initialize(self, argv=None):
361 371 # don't hook up crash handler before parsing command-line
362 372 self.parse_command_line(argv)
363 373 self.init_crash_handler()
364 374 if self.subapp is not None:
365 375 # stop here if subapp is taking over
366 376 return
367 377 cl_config = self.config
368 378 self.init_profile_dir()
369 379 self.init_config_files()
370 380 self.load_config_file()
371 381 # enforce cl-opts override configfile opts:
372 382 self.update_config(cl_config)
373 383
General Comments 0
You need to be logged in to leave comments. Login now