Show More
This diff has been collapsed as it changes many lines, (719 lines changed) Show them Hide them | |||
@@ -12,6 +12,7 b' Authors:' | |||
|
12 | 12 | |
|
13 | 13 | * Brian Granger |
|
14 | 14 | * Fernando Perez |
|
15 | * Min RK | |
|
15 | 16 | |
|
16 | 17 | Notes |
|
17 | 18 | ----- |
@@ -30,439 +31,399 b' Notes' | |||
|
30 | 31 | |
|
31 | 32 | import logging |
|
32 | 33 | import os |
|
34 | import shutil | |
|
33 | 35 | import sys |
|
34 | 36 | |
|
37 | from IPython.config.application import Application | |
|
38 | from IPython.config.configurable import Configurable | |
|
39 | from IPython.config.loader import Config | |
|
35 | 40 | from IPython.core import release, crashhandler |
|
36 | from IPython.utils.path import get_ipython_dir, get_ipython_package_dir | |
|
37 | from IPython.config.loader import ( | |
|
38 | PyFileConfigLoader, | |
|
39 | ArgParseConfigLoader, | |
|
40 | Config, | |
|
41 | ) | |
|
41 | from IPython.utils.path import get_ipython_dir, get_ipython_package_dir, expand_path | |
|
42 | from IPython.utils.traitlets import List, Unicode, Type, Bool, Dict | |
|
42 | 43 | |
|
43 | 44 | #----------------------------------------------------------------------------- |
|
44 | 45 | # Classes and functions |
|
45 | 46 | #----------------------------------------------------------------------------- |
|
46 | 47 | |
|
47 | class ApplicationError(Exception): | |
|
48 | ||
|
49 | #----------------------------------------------------------------------------- | |
|
50 | # Module errors | |
|
51 | #----------------------------------------------------------------------------- | |
|
52 | ||
|
53 | class ProfileDirError(Exception): | |
|
48 | 54 | pass |
|
49 | 55 | |
|
50 | 56 | |
|
51 | class BaseAppConfigLoader(ArgParseConfigLoader): | |
|
52 | """Default command line options for IPython based applications.""" | |
|
53 | ||
|
54 | def _add_ipython_dir(self, parser): | |
|
55 | """Add the --ipython-dir option to the parser.""" | |
|
56 | paa = parser.add_argument | |
|
57 | paa('--ipython-dir', | |
|
58 | dest='Global.ipython_dir',type=unicode, | |
|
59 | help= | |
|
60 | """Set to override default location of the IPython directory | |
|
61 | IPYTHON_DIR, stored as Global.ipython_dir. This can also be | |
|
62 | specified through the environment variable IPYTHON_DIR.""", | |
|
63 | metavar='Global.ipython_dir') | |
|
64 | ||
|
65 | def _add_log_level(self, parser): | |
|
66 | """Add the --log-level option to the parser.""" | |
|
67 | paa = parser.add_argument | |
|
68 | paa('--log-level', | |
|
69 | dest="Global.log_level",type=int, | |
|
70 | help='Set the log level (0,10,20,30,40,50). Default is 30.', | |
|
71 | metavar='Global.log_level') | |
|
72 | ||
|
73 | def _add_version(self, parser): | |
|
74 | """Add the --version option to the parser.""" | |
|
75 | parser.add_argument('--version', action="version", | |
|
76 | version=self.version) | |
|
77 | ||
|
78 | def _add_arguments(self): | |
|
79 | self._add_ipython_dir(self.parser) | |
|
80 | self._add_log_level(self.parser) | |
|
81 | try: # Old versions of argparse don't have a version action | |
|
82 | self._add_version(self.parser) | |
|
83 | except Exception: | |
|
84 | pass | |
|
85 | ||
|
86 | ||
|
87 | class Application(object): | |
|
88 | """Load a config, construct configurables and set them running. | |
|
89 | ||
|
90 | The configuration of an application can be done via three different Config | |
|
91 | objects, which are loaded and ultimately merged into a single one used | |
|
92 | from that point on by the app. These are: | |
|
93 | ||
|
94 | 1. default_config: internal defaults, implemented in code. | |
|
95 | 2. file_config: read from the filesystem. | |
|
96 | 3. command_line_config: read from the system's command line flags. | |
|
97 | ||
|
98 | During initialization, 3 is actually read before 2, since at the | |
|
99 | command-line one may override the location of the file to be read. But the | |
|
100 | above is the order in which the merge is made. | |
|
101 | """ | |
|
57 | #----------------------------------------------------------------------------- | |
|
58 | # Class for managing profile directories | |
|
59 | #----------------------------------------------------------------------------- | |
|
102 | 60 | |
|
103 | name = u'ipython' | |
|
104 | description = 'IPython: an enhanced interactive Python shell.' | |
|
105 | #: Usage message printed by argparse. If None, auto-generate | |
|
106 | usage = None | |
|
107 | #: The command line config loader. Subclass of ArgParseConfigLoader. | |
|
108 | command_line_loader = BaseAppConfigLoader | |
|
109 | #: The name of the config file to load, determined at runtime | |
|
110 | config_file_name = None | |
|
111 | #: The name of the default config file. Track separately from the actual | |
|
112 | #: name because some logic happens only if we aren't using the default. | |
|
113 | default_config_file_name = u'ipython_config.py' | |
|
114 | default_log_level = logging.WARN | |
|
115 | #: Set by --profile option | |
|
116 | profile_name = None | |
|
117 | #: User's ipython directory, typically ~/.ipython or ~/.config/ipython/ | |
|
118 | ipython_dir = None | |
|
119 | #: Internal defaults, implemented in code. | |
|
120 | default_config = None | |
|
121 | #: Read from the filesystem. | |
|
122 | file_config = None | |
|
123 | #: Read from the system's command line flags. | |
|
124 | command_line_config = None | |
|
125 | #: The final config that will be passed to the main object. | |
|
126 | master_config = None | |
|
127 | #: A reference to the argv to be used (typically ends up being sys.argv[1:]) | |
|
128 | argv = None | |
|
129 | #: extra arguments computed by the command-line loader | |
|
130 | extra_args = None | |
|
131 | #: The class to use as the crash handler. | |
|
132 | crash_handler_class = crashhandler.CrashHandler | |
|
133 | ||
|
134 | # Private attributes | |
|
135 | _exiting = False | |
|
136 | _initialized = False | |
|
137 | ||
|
138 | def __init__(self, argv=None): | |
|
139 | self.argv = sys.argv[1:] if argv is None else argv | |
|
140 | self.init_logger() | |
|
141 | ||
|
142 | def init_logger(self): | |
|
143 | self.log = logging.getLogger(self.__class__.__name__) | |
|
144 | # This is used as the default until the command line arguments are read. | |
|
145 | self.log.setLevel(self.default_log_level) | |
|
146 | self._log_handler = logging.StreamHandler() | |
|
147 | self._log_formatter = logging.Formatter("[%(name)s] %(message)s") | |
|
148 | self._log_handler.setFormatter(self._log_formatter) | |
|
149 | self.log.addHandler(self._log_handler) | |
|
150 | ||
|
151 | def _set_log_level(self, level): | |
|
152 | self.log.setLevel(level) | |
|
153 | ||
|
154 | def _get_log_level(self): | |
|
155 | return self.log.level | |
|
156 | ||
|
157 | log_level = property(_get_log_level, _set_log_level) | |
|
158 | ||
|
159 | def initialize(self): | |
|
160 | """Initialize the application. | |
|
161 | ||
|
162 | Loads all configuration information and sets all application state, but | |
|
163 | does not start any relevant processing (typically some kind of event | |
|
164 | loop). | |
|
165 | ||
|
166 | Once this method has been called, the application is flagged as | |
|
167 | initialized and the method becomes a no-op.""" | |
|
168 | ||
|
169 | if self._initialized: | |
|
170 | return | |
|
61 | class ProfileDir(Configurable): | |
|
62 | """An object to manage the profile directory and its resources. | |
|
171 | 63 |
|
|
172 | # The first part is protected with an 'attempt' wrapper, that will log | |
|
173 | # failures with the basic system traceback machinery. Once our crash | |
|
174 | # handler is in place, we can let any subsequent exception propagate, | |
|
175 | # as our handler will log it with much better detail than the default. | |
|
176 | self.attempt(self.create_crash_handler) | |
|
177 | ||
|
178 | # Configuration phase | |
|
179 | # Default config (internally hardwired in application code) | |
|
180 | self.create_default_config() | |
|
181 | self.log_default_config() | |
|
182 | self.set_default_config_log_level() | |
|
183 | ||
|
184 | # Command-line config | |
|
185 | self.pre_load_command_line_config() | |
|
186 | self.load_command_line_config() | |
|
187 | self.set_command_line_config_log_level() | |
|
188 | self.post_load_command_line_config() | |
|
189 | self.log_command_line_config() | |
|
190 | ||
|
191 | # Find resources needed for filesystem access, using information from | |
|
192 | # the above two | |
|
193 | self.find_ipython_dir() | |
|
194 | self.find_resources() | |
|
195 | self.find_config_file_name() | |
|
196 | self.find_config_file_paths() | |
|
197 | ||
|
198 | # File-based config | |
|
199 | self.pre_load_file_config() | |
|
200 | self.load_file_config() | |
|
201 | self.set_file_config_log_level() | |
|
202 | self.post_load_file_config() | |
|
203 | self.log_file_config() | |
|
204 | ||
|
205 | # Merge all config objects into a single one the app can then use | |
|
206 | self.merge_configs() | |
|
207 | self.log_master_config() | |
|
208 | ||
|
209 | # Construction phase | |
|
210 | self.pre_construct() | |
|
211 | self.construct() | |
|
212 | self.post_construct() | |
|
213 | ||
|
214 | # Done, flag as such and | |
|
215 | self._initialized = True | |
|
216 | ||
|
217 | def start(self): | |
|
218 | """Start the application.""" | |
|
219 | self.initialize() | |
|
220 | self.start_app() | |
|
64 | The profile directory is used by all IPython applications, to manage | |
|
65 | configuration, logging and security. | |
|
221 | 66 |
|
|
222 | #------------------------------------------------------------------------- | |
|
223 | # Various stages of Application creation | |
|
224 | #------------------------------------------------------------------------- | |
|
67 | This object knows how to find, create and manage these directories. This | |
|
68 | should be used by any code that wants to handle profiles. | |
|
69 | """ | |
|
225 | 70 | |
|
226 | def create_crash_handler(self): | |
|
227 | """Create a crash handler, typically setting sys.excepthook to it.""" | |
|
228 | self.crash_handler = self.crash_handler_class(self) | |
|
229 | sys.excepthook = self.crash_handler | |
|
71 | security_dir_name = Unicode('security') | |
|
72 | log_dir_name = Unicode('log') | |
|
73 | pid_dir_name = Unicode('pid') | |
|
74 | security_dir = Unicode(u'') | |
|
75 | log_dir = Unicode(u'') | |
|
76 | pid_dir = Unicode(u'') | |
|
77 | ||
|
78 | location = Unicode(u'', config=True, | |
|
79 | help="""Set the profile location directly. This overrides the logic used by the | |
|
80 | `profile` option.""", | |
|
81 | ) | |
|
230 | 82 | |
|
231 | def create_default_config(self): | |
|
232 | """Create defaults that can't be set elsewhere. | |
|
83 | _location_isset = Bool(False) # flag for detecting multiply set location | |
|
233 | 84 | |
|
234 | For the most part, we try to set default in the class attributes | |
|
235 | of Configurables. But, defaults the top-level Application (which is | |
|
236 | not a HasTraits or Configurables) are not set in this way. Instead | |
|
237 | we set them here. The Global section is for variables like this that | |
|
238 | don't belong to a particular configurable. | |
|
239 | """ | |
|
240 | c = Config() | |
|
241 | c.Global.ipython_dir = get_ipython_dir() | |
|
242 | c.Global.log_level = self.log_level | |
|
243 | self.default_config = c | |
|
85 | def _location_changed(self, name, old, new): | |
|
86 | if self._location_isset: | |
|
87 | raise RuntimeError("Cannot set profile location more than once.") | |
|
88 | self._location_isset = True | |
|
89 | if not os.path.isdir(new): | |
|
90 | os.makedirs(new) | |
|
91 | ||
|
92 | # ensure config files exist: | |
|
93 | self.security_dir = os.path.join(new, self.security_dir_name) | |
|
94 | self.log_dir = os.path.join(new, self.log_dir_name) | |
|
95 | self.pid_dir = os.path.join(new, self.pid_dir_name) | |
|
96 | self.check_dirs() | |
|
244 | 97 | |
|
245 |
def |
|
|
246 | self.log.debug('Default config loaded:') | |
|
247 | self.log.debug(repr(self.default_config)) | |
|
98 | def _log_dir_changed(self, name, old, new): | |
|
99 | self.check_log_dir() | |
|
248 | 100 | |
|
249 |
def |
|
|
250 | try: | |
|
251 | self.log_level = self.default_config.Global.log_level | |
|
252 | except AttributeError: | |
|
253 | # Fallback to the default_log_level class attribute | |
|
254 | pass | |
|
255 | ||
|
256 | def create_command_line_config(self): | |
|
257 | """Create and return a command line config loader.""" | |
|
258 | return self.command_line_loader( | |
|
259 | self.argv, | |
|
260 | description=self.description, | |
|
261 | version=release.version, | |
|
262 | usage=self.usage | |
|
263 | ) | |
|
101 | def check_log_dir(self): | |
|
102 | if not os.path.isdir(self.log_dir): | |
|
103 | os.mkdir(self.log_dir) | |
|
264 | 104 | |
|
265 | def pre_load_command_line_config(self): | |
|
266 | """Do actions just before loading the command line config.""" | |
|
267 | pass | |
|
105 | def _security_dir_changed(self, name, old, new): | |
|
106 | self.check_security_dir() | |
|
268 | 107 | |
|
269 | def load_command_line_config(self): | |
|
270 | """Load the command line config.""" | |
|
271 | loader = self.create_command_line_config() | |
|
272 | self.command_line_config = loader.load_config() | |
|
273 | self.extra_args = loader.get_extra_args() | |
|
108 | def check_security_dir(self): | |
|
109 | if not os.path.isdir(self.security_dir): | |
|
110 | os.mkdir(self.security_dir, 0700) | |
|
111 | else: | |
|
112 | os.chmod(self.security_dir, 0700) | |
|
274 | 113 | |
|
275 | def set_command_line_config_log_level(self): | |
|
276 | try: | |
|
277 | self.log_level = self.command_line_config.Global.log_level | |
|
278 | except AttributeError: | |
|
279 | pass | |
|
114 | def _pid_dir_changed(self, name, old, new): | |
|
115 | self.check_pid_dir() | |
|
280 | 116 | |
|
281 | def post_load_command_line_config(self): | |
|
282 | """Do actions just after loading the command line config.""" | |
|
283 | pass | |
|
117 | def check_pid_dir(self): | |
|
118 | if not os.path.isdir(self.pid_dir): | |
|
119 | os.mkdir(self.pid_dir, 0700) | |
|
120 | else: | |
|
121 | os.chmod(self.pid_dir, 0700) | |
|
284 | 122 | |
|
285 | def log_command_line_config(self): | |
|
286 | self.log.debug("Command line config loaded:") | |
|
287 | self.log.debug(repr(self.command_line_config)) | |
|
123 | def check_dirs(self): | |
|
124 | self.check_security_dir() | |
|
125 | self.check_log_dir() | |
|
126 | self.check_pid_dir() | |
|
288 | 127 | |
|
289 | def find_ipython_dir(self): | |
|
290 | """Set the IPython directory. | |
|
128 | def copy_config_file(self, config_file, path=None, overwrite=False): | |
|
129 | """Copy a default config file into the active profile directory. | |
|
291 | 130 | |
|
292 | This sets ``self.ipython_dir``, but the actual value that is passed to | |
|
293 | the application is kept in either ``self.default_config`` or | |
|
294 | ``self.command_line_config``. This also adds ``self.ipython_dir`` to | |
|
295 | ``sys.path`` so config files there can be referenced by other config | |
|
296 | files. | |
|
131 | Default configuration files are kept in :mod:`IPython.config.default`. | |
|
132 | This function moves these from that location to the working profile | |
|
133 | directory. | |
|
297 | 134 | """ |
|
298 | ||
|
299 | try: | |
|
300 | self.ipython_dir = self.command_line_config.Global.ipython_dir | |
|
301 | except AttributeError: | |
|
302 | self.ipython_dir = self.default_config.Global.ipython_dir | |
|
303 | sys.path.append(os.path.abspath(self.ipython_dir)) | |
|
304 | if not os.path.isdir(self.ipython_dir): | |
|
305 | os.makedirs(self.ipython_dir, mode=0777) | |
|
306 | self.log.debug("IPYTHON_DIR set to: %s" % self.ipython_dir) | |
|
307 | ||
|
308 | def find_resources(self): | |
|
309 | """Find other resources that need to be in place. | |
|
310 | ||
|
311 | Things like cluster directories need to be in place to find the | |
|
312 | config file. These happen right after the IPython directory has | |
|
313 | been set. | |
|
135 | dst = os.path.join(self.location, config_file) | |
|
136 | if os.path.isfile(dst) and not overwrite: | |
|
137 | return | |
|
138 | if path is None: | |
|
139 | path = os.path.join(get_ipython_package_dir(), u'config', u'profile', u'default') | |
|
140 | src = os.path.join(path, config_file) | |
|
141 | shutil.copy(src, dst) | |
|
142 | ||
|
143 | @classmethod | |
|
144 | def create_profile_dir(cls, profile_dir, config=None): | |
|
145 | """Create a new profile directory given a full path. | |
|
146 | ||
|
147 | Parameters | |
|
148 | ---------- | |
|
149 | profile_dir : str | |
|
150 | The full path to the profile directory. If it does exist, it will | |
|
151 | be used. If not, it will be created. | |
|
152 | """ | |
|
153 | return cls(location=profile_dir, config=config) | |
|
154 | ||
|
155 | @classmethod | |
|
156 | def create_profile_dir_by_name(cls, path, name=u'default', config=None): | |
|
157 | """Create a profile dir by profile name and path. | |
|
158 | ||
|
159 | Parameters | |
|
160 | ---------- | |
|
161 | path : unicode | |
|
162 | The path (directory) to put the profile directory in. | |
|
163 | name : unicode | |
|
164 | The name of the profile. The name of the profile directory will | |
|
165 | be "profile_<profile>". | |
|
314 | 166 | """ |
|
315 | pass | |
|
167 | if not os.path.isdir(path): | |
|
168 | raise ProfileDirError('Directory not found: %s' % path) | |
|
169 | profile_dir = os.path.join(path, u'profile_' + name) | |
|
170 | return cls(location=profile_dir, config=config) | |
|
171 | ||
|
172 | @classmethod | |
|
173 | def find_profile_dir_by_name(cls, ipython_dir, name=u'default', config=None): | |
|
174 | """Find an existing profile dir by profile name, return its ProfileDir. | |
|
175 | ||
|
176 | This searches through a sequence of paths for a profile dir. If it | |
|
177 | is not found, a :class:`ProfileDirError` exception will be raised. | |
|
178 | ||
|
179 | The search path algorithm is: | |
|
180 | 1. ``os.getcwd()`` | |
|
181 | 2. ``ipython_dir`` | |
|
182 | ||
|
183 | Parameters | |
|
184 | ---------- | |
|
185 | ipython_dir : unicode or str | |
|
186 | The IPython directory to use. | |
|
187 | name : unicode or str | |
|
188 | The name of the profile. The name of the profile directory | |
|
189 | will be "profile_<profile>". | |
|
190 | """ | |
|
191 | dirname = u'profile_' + name | |
|
192 | paths = [os.getcwdu(), ipython_dir] | |
|
193 | for p in paths: | |
|
194 | profile_dir = os.path.join(p, dirname) | |
|
195 | if os.path.isdir(profile_dir): | |
|
196 | return cls(location=profile_dir, config=config) | |
|
197 | else: | |
|
198 | raise ProfileDirError('Profile directory not found in paths: %s' % dirname) | |
|
316 | 199 | |
|
317 | def find_config_file_name(self): | |
|
318 | """Find the config file name for this application. | |
|
200 | @classmethod | |
|
201 | def find_profile_dir(cls, profile_dir, config=None): | |
|
202 | """Find/create a profile dir and return its ProfileDir. | |
|
319 | 203 | |
|
320 | This must set ``self.config_file_name`` to the filename of the | |
|
321 | config file to use (just the filename). The search paths for the | |
|
322 | config file are set in :meth:`find_config_file_paths` and then passed | |
|
323 | to the config file loader where they are resolved to an absolute path. | |
|
204 | This will create the profile directory if it doesn't exist. | |
|
324 | 205 | |
|
325 | If a profile has been set at the command line, this will resolve it. | |
|
206 | Parameters | |
|
207 | ---------- | |
|
208 | profile_dir : unicode or str | |
|
209 | The path of the profile directory. This is expanded using | |
|
210 | :func:`IPython.utils.genutils.expand_path`. | |
|
326 | 211 | """ |
|
327 | try: | |
|
328 | self.config_file_name = self.command_line_config.Global.config_file | |
|
329 | except AttributeError: | |
|
330 | pass | |
|
331 | else: | |
|
332 | return | |
|
212 | profile_dir = expand_path(profile_dir) | |
|
213 | if not os.path.isdir(profile_dir): | |
|
214 | raise ProfileDirError('Profile directory not found: %s' % profile_dir) | |
|
215 | return cls(location=profile_dir, config=config) | |
|
333 | 216 | |
|
334 | try: | |
|
335 | self.profile_name = self.command_line_config.Global.profile | |
|
336 | except AttributeError: | |
|
337 | # Just use the default as there is no profile | |
|
338 | self.config_file_name = self.default_config_file_name | |
|
339 | else: | |
|
340 | # Use the default config file name and profile name if set | |
|
341 | # to determine the used config file name. | |
|
342 | name_parts = self.default_config_file_name.split('.') | |
|
343 | name_parts.insert(1, u'_' + self.profile_name + u'.') | |
|
344 | self.config_file_name = ''.join(name_parts) | |
|
345 | 217 | |
|
346 | def find_config_file_paths(self): | |
|
347 | """Set the search paths for resolving the config file. | |
|
218 | #----------------------------------------------------------------------------- | |
|
219 | # Base Application Class | |
|
220 | #----------------------------------------------------------------------------- | |
|
221 | ||
|
222 | # aliases and flags | |
|
223 | ||
|
224 | base_aliases = dict( | |
|
225 | profile='BaseIPythonApplication.profile', | |
|
226 | ipython_dir='BaseIPythonApplication.ipython_dir', | |
|
227 | ) | |
|
348 | 228 | |
|
349 | This must set ``self.config_file_paths`` to a sequence of search | |
|
350 | paths to pass to the config file loader. | |
|
229 | base_flags = dict( | |
|
230 | debug = ({'Application' : {'log_level' : logging.DEBUG}}, | |
|
231 | "set log level to logging.DEBUG (maximize logging output)"), | |
|
232 | quiet = ({'Application' : {'log_level' : logging.CRITICAL}}, | |
|
233 | "set log level to logging.CRITICAL (minimize logging output)"), | |
|
234 | init = ({'BaseIPythonApplication' : { | |
|
235 | 'copy_config_files' : True, | |
|
236 | 'auto_create' : True} | |
|
237 | }, "Initialize profile with default config files") | |
|
238 | ) | |
|
239 | ||
|
240 | ||
|
241 | class BaseIPythonApplication(Application): | |
|
242 | ||
|
243 | name = Unicode(u'ipython') | |
|
244 | description = Unicode(u'IPython: an enhanced interactive Python shell.') | |
|
245 | version = Unicode(release.version) | |
|
246 | ||
|
247 | aliases = Dict(base_aliases) | |
|
248 | flags = Dict(base_flags) | |
|
249 | ||
|
250 | # Track whether the config_file has changed, | |
|
251 | # because some logic happens only if we aren't using the default. | |
|
252 | config_file_specified = Bool(False) | |
|
253 | ||
|
254 | config_file_name = Unicode(u'ipython_config.py') | |
|
255 | def _config_file_name_changed(self, name, old, new): | |
|
256 | if new != old: | |
|
257 | self.config_file_specified = True | |
|
258 | ||
|
259 | # The directory that contains IPython's builtin profiles. | |
|
260 | builtin_profile_dir = Unicode( | |
|
261 | os.path.join(get_ipython_package_dir(), u'config', u'profile', u'default') | |
|
262 | ) | |
|
263 | ||
|
264 | config_file_paths = List(Unicode) | |
|
265 | def _config_file_paths_default(self): | |
|
266 | return [os.getcwdu()] | |
|
267 | ||
|
268 | profile = Unicode(u'default', config=True, | |
|
269 | help="""The IPython profile to use.""" | |
|
270 | ) | |
|
271 | def _profile_changed(self, name, old, new): | |
|
272 | self.builtin_profile_dir = os.path.join( | |
|
273 | get_ipython_package_dir(), u'config', u'profile', new | |
|
274 | ) | |
|
275 | ||
|
276 | ||
|
277 | ipython_dir = Unicode(get_ipython_dir(), config=True, | |
|
278 | help=""" | |
|
279 | The name of the IPython directory. This directory is used for logging | |
|
280 | configuration (through profiles), history storage, etc. The default | |
|
281 | is usually $HOME/.ipython. This options can also be specified through | |
|
282 | the environment variable IPYTHON_DIR. | |
|
351 | 283 |
|
|
352 | # Include our own profiles directory last, so that users can still find | |
|
353 | # our shipped copies of builtin profiles even if they don't have them | |
|
354 | # in their local ipython directory. | |
|
355 | prof_dir = os.path.join(get_ipython_package_dir(), 'config', 'profile') | |
|
356 | self.config_file_paths = (os.getcwdu(), self.ipython_dir, prof_dir) | |
|
284 | ) | |
|
285 | ||
|
286 | overwrite = Bool(False, config=True, | |
|
287 | help="""Whether to overwrite existing config files when copying""") | |
|
288 | auto_create = Bool(False, config=True, | |
|
289 | help="""Whether to create profile dir if it doesn't exist""") | |
|
290 | ||
|
291 | config_files = List(Unicode) | |
|
292 | def _config_files_default(self): | |
|
293 | return [u'ipython_config.py'] | |
|
294 | ||
|
295 | copy_config_files = Bool(False, config=True, | |
|
296 | help="""Whether to copy the default config files into the profile dir.""") | |
|
297 | ||
|
298 | # The class to use as the crash handler. | |
|
299 | crash_handler_class = Type(crashhandler.CrashHandler) | |
|
300 | ||
|
301 | def __init__(self, **kwargs): | |
|
302 | super(BaseIPythonApplication, self).__init__(**kwargs) | |
|
303 | # ensure even default IPYTHON_DIR exists | |
|
304 | if not os.path.exists(self.ipython_dir): | |
|
305 | self._ipython_dir_changed('ipython_dir', self.ipython_dir, self.ipython_dir) | |
|
306 | ||
|
307 | #------------------------------------------------------------------------- | |
|
308 | # Various stages of Application creation | |
|
309 | #------------------------------------------------------------------------- | |
|
357 | 310 | |
|
358 |
def |
|
|
359 | """Do actions before the config file is loaded.""" | |
|
360 | pass | |
|
311 | def init_crash_handler(self): | |
|
312 | """Create a crash handler, typically setting sys.excepthook to it.""" | |
|
313 | self.crash_handler = self.crash_handler_class(self) | |
|
314 | sys.excepthook = self.crash_handler | |
|
361 | 315 | |
|
362 | def load_file_config(self, suppress_errors=True): | |
|
316 | def _ipython_dir_changed(self, name, old, new): | |
|
317 | if old in sys.path: | |
|
318 | sys.path.remove(old) | |
|
319 | sys.path.append(os.path.abspath(new)) | |
|
320 | if not os.path.isdir(new): | |
|
321 | os.makedirs(new, mode=0777) | |
|
322 | readme = os.path.join(new, 'README') | |
|
323 | if not os.path.exists(readme): | |
|
324 | path = os.path.join(get_ipython_package_dir(), u'config', u'profile') | |
|
325 | shutil.copy(os.path.join(path, 'README'), readme) | |
|
326 | self.log.debug("IPYTHON_DIR set to: %s" % new) | |
|
327 | ||
|
328 | def load_config_file(self, suppress_errors=True): | |
|
363 | 329 | """Load the config file. |
|
364 | ||
|
365 | This tries to load the config file from disk. If successful, the | |
|
366 | ``CONFIG_FILE`` config variable is set to the resolved config file | |
|
367 | location. If not successful, an empty config is used. | |
|
368 | ||
|
330 | ||
|
369 | 331 | By default, errors in loading config are handled, and a warning |
|
370 | 332 | printed on screen. For testing, the suppress_errors option is set |
|
371 | 333 | to False, so errors will make tests fail. |
|
372 | 334 | """ |
|
373 | 335 | self.log.debug("Attempting to load config file: %s" % |
|
374 | 336 | self.config_file_name) |
|
375 | loader = PyFileConfigLoader(self.config_file_name, | |
|
376 | path=self.config_file_paths) | |
|
377 | 337 | try: |
|
378 |
|
|
|
379 | self.file_config.Global.config_file = loader.full_filename | |
|
338 | Application.load_config_file( | |
|
339 | self, | |
|
340 | self.config_file_name, | |
|
341 | path=self.config_file_paths | |
|
342 | ) | |
|
380 | 343 | except IOError: |
|
381 | 344 | # Only warn if the default config file was NOT being used. |
|
382 |
if |
|
|
345 | if self.config_file_specified: | |
|
383 | 346 | self.log.warn("Config file not found, skipping: %s" % |
|
384 |
self.config_file_name |
|
|
385 | self.file_config = Config() | |
|
347 | self.config_file_name) | |
|
386 | 348 | except: |
|
387 |
|
|
|
349 | # For testing purposes. | |
|
350 | if not suppress_errors: | |
|
388 | 351 | raise |
|
389 | 352 | self.log.warn("Error loading config file: %s" % |
|
390 | 353 | self.config_file_name, exc_info=True) |
|
391 | self.file_config = Config() | |
|
392 | 354 | |
|
393 |
def |
|
|
394 | # We need to keeep self.log_level updated. But we only use the value | |
|
395 | # of the file_config if a value was not specified at the command | |
|
396 | # line, because the command line overrides everything. | |
|
397 | if not hasattr(self.command_line_config.Global, 'log_level'): | |
|
355 | def init_profile_dir(self): | |
|
356 | """initialize the profile dir""" | |
|
357 | try: | |
|
358 | # location explicitly specified: | |
|
359 | location = self.config.ProfileDir.location | |
|
360 | except AttributeError: | |
|
361 | # location not specified, find by profile name | |
|
398 | 362 | try: |
|
399 | self.log_level = self.file_config.Global.log_level | |
|
400 |
except |
|
|
401 | pass # Use existing value | |
|
402 | ||
|
403 | def post_load_file_config(self): | |
|
404 | """Do actions after the config file is loaded.""" | |
|
405 | pass | |
|
406 | ||
|
407 | def log_file_config(self): | |
|
408 | if hasattr(self.file_config.Global, 'config_file'): | |
|
409 | self.log.debug("Config file loaded: %s" % | |
|
410 | self.file_config.Global.config_file) | |
|
411 | self.log.debug(repr(self.file_config)) | |
|
412 | ||
|
413 | def merge_configs(self): | |
|
414 | """Merge the default, command line and file config objects.""" | |
|
415 | config = Config() | |
|
416 | config._merge(self.default_config) | |
|
417 | config._merge(self.file_config) | |
|
418 | config._merge(self.command_line_config) | |
|
419 | ||
|
420 | # XXX fperez - propose to Brian we rename master_config to simply | |
|
421 | # config, I think this is going to be heavily used in examples and | |
|
422 | # application code and the name is shorter/easier to find/remember. | |
|
423 | # For now, just alias it... | |
|
424 | self.master_config = config | |
|
425 | self.config = config | |
|
426 | ||
|
427 | def log_master_config(self): | |
|
428 | self.log.debug("Master config created:") | |
|
429 | self.log.debug(repr(self.master_config)) | |
|
430 | ||
|
431 | def pre_construct(self): | |
|
432 | """Do actions after the config has been built, but before construct.""" | |
|
433 | pass | |
|
434 | ||
|
435 | def construct(self): | |
|
436 | """Construct the main objects that make up this app.""" | |
|
437 | self.log.debug("Constructing main objects for application") | |
|
438 | ||
|
439 | def post_construct(self): | |
|
440 | """Do actions after construct, but before starting the app.""" | |
|
441 | pass | |
|
442 | ||
|
443 | def start_app(self): | |
|
444 | """Actually start the app.""" | |
|
445 | self.log.debug("Starting application") | |
|
446 | ||
|
447 | #------------------------------------------------------------------------- | |
|
448 | # Utility methods | |
|
449 | #------------------------------------------------------------------------- | |
|
450 | ||
|
451 | def exit(self, exit_status=0): | |
|
452 | if self._exiting: | |
|
453 | pass | |
|
363 | p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config) | |
|
364 | except ProfileDirError: | |
|
365 | # not found, maybe create it (always create default profile) | |
|
366 | if self.auto_create or self.profile=='default': | |
|
367 | try: | |
|
368 | p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config) | |
|
369 | except ProfileDirError: | |
|
370 | self.log.fatal("Could not create profile: %r"%self.profile) | |
|
371 | self.exit(1) | |
|
372 | else: | |
|
373 | self.log.info("Created profile dir: %r"%p.location) | |
|
374 | else: | |
|
375 | self.log.fatal("Profile %r not found."%self.profile) | |
|
376 | self.exit(1) | |
|
377 | else: | |
|
378 | self.log.info("Using existing profile dir: %r"%p.location) | |
|
454 | 379 | else: |
|
455 | self.log.debug("Exiting application: %s" % self.name) | |
|
456 | self._exiting = True | |
|
457 | sys.exit(exit_status) | |
|
458 | ||
|
459 | def attempt(self, func): | |
|
460 | try: | |
|
461 | func() | |
|
462 | except SystemExit: | |
|
463 | raise | |
|
464 | except: | |
|
465 | self.log.critical("Aborting application: %s" % self.name, | |
|
466 |
|
|
|
467 | self.exit(0) | |
|
380 | # location is fully specified | |
|
381 | try: | |
|
382 | p = ProfileDir.find_profile_dir(location, self.config) | |
|
383 | except ProfileDirError: | |
|
384 | # not found, maybe create it | |
|
385 | if self.auto_create: | |
|
386 | try: | |
|
387 | p = ProfileDir.create_profile_dir(location, self.config) | |
|
388 | except ProfileDirError: | |
|
389 | self.log.fatal("Could not create profile directory: %r"%location) | |
|
390 | self.exit(1) | |
|
391 | else: | |
|
392 | self.log.info("Creating new profile dir: %r"%location) | |
|
393 | else: | |
|
394 | self.log.fatal("Profile directory %r not found."%location) | |
|
395 | self.exit(1) | |
|
396 | else: | |
|
397 | self.log.info("Using existing profile dir: %r"%location) | |
|
398 | ||
|
399 | self.profile_dir = p | |
|
400 | self.config_file_paths.append(p.location) | |
|
401 | ||
|
402 | def init_config_files(self): | |
|
403 | """[optionally] copy default config files into profile dir.""" | |
|
404 | # copy config files | |
|
405 | if self.copy_config_files: | |
|
406 | path = self.builtin_profile_dir | |
|
407 | src = self.profile | |
|
408 | if not os.path.exists(path): | |
|
409 | # use default if new profile doesn't have a preset | |
|
410 | path = None | |
|
411 | src = 'default' | |
|
412 | ||
|
413 | self.log.debug("Staging %s config files into %r [overwrite=%s]"%( | |
|
414 | src, self.profile_dir.location, self.overwrite) | |
|
415 | ) | |
|
416 | ||
|
417 | for cfg in self.config_files: | |
|
418 | self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite) | |
|
419 | ||
|
420 | def initialize(self, argv=None): | |
|
421 | self.init_crash_handler() | |
|
422 | self.parse_command_line(argv) | |
|
423 | cl_config = self.config | |
|
424 | self.init_profile_dir() | |
|
425 | self.init_config_files() | |
|
426 | self.load_config_file() | |
|
427 | # enforce cl-opts override configfile opts: | |
|
428 | self.update_config(cl_config) | |
|
468 | 429 |
@@ -54,7 +54,7 b' from IPython.core.inputsplitter import IPythonInputSplitter' | |||
|
54 | 54 | from IPython.core.logger import Logger |
|
55 | 55 | from IPython.core.macro import Macro |
|
56 | 56 | from IPython.core.magic import Magic |
|
57 |
from IPython.core. |
|
|
57 | from IPython.core.application import ProfileDir | |
|
58 | 58 | from IPython.core.payload import PayloadManager |
|
59 | 59 | from IPython.core.plugin import PluginManager |
|
60 | 60 | from IPython.core.prefilter import PrefilterManager, ESC_MAGIC |
@@ -344,7 +344,7 b' class InteractiveShell(SingletonConfigurable, Magic):' | |||
|
344 | 344 | payload_manager = Instance('IPython.core.payload.PayloadManager') |
|
345 | 345 | history_manager = Instance('IPython.core.history.HistoryManager') |
|
346 | 346 | |
|
347 |
profile_dir = Instance('IPython.core. |
|
|
347 | profile_dir = Instance('IPython.core.application.ProfileDir') | |
|
348 | 348 | @property |
|
349 | 349 | def profile(self): |
|
350 | 350 | if self.profile_dir is not None: |
@@ -46,7 +46,7 b' from IPython.core import debugger, oinspect' | |||
|
46 | 46 | from IPython.core.error import TryNext |
|
47 | 47 | from IPython.core.error import UsageError |
|
48 | 48 | from IPython.core.fakemodule import FakeModule |
|
49 |
from IPython.core. |
|
|
49 | from IPython.core.application import ProfileDir | |
|
50 | 50 | from IPython.core.macro import Macro |
|
51 | 51 | from IPython.core import page |
|
52 | 52 | from IPython.core.prefilter import ESC_MAGIC |
@@ -1,7 +1,7 b'' | |||
|
1 | 1 | #!/usr/bin/env python |
|
2 | 2 | # encoding: utf-8 |
|
3 | 3 | """ |
|
4 |
A mixin for :class:`~IPython.core. |
|
|
4 | A mixin for :class:`~IPython.core.application.Application` classes that | |
|
5 | 5 | launch InteractiveShell instances, load extensions, etc. |
|
6 | 6 | |
|
7 | 7 | Authors |
@@ -4,7 +4,7 b'' | |||
|
4 | 4 | import os |
|
5 | 5 | import tempfile |
|
6 | 6 | |
|
7 | from IPython.core.application import Application | |
|
7 | from IPython.core.application import BaseIPythonApplication | |
|
8 | 8 | from IPython.testing import decorators as testdec |
|
9 | 9 | |
|
10 | 10 | @testdec.onlyif_unicode_paths |
@@ -16,22 +16,11 b' def test_unicode_cwd():' | |||
|
16 | 16 | os.chdir(wd) |
|
17 | 17 | #raise Exception(repr(os.getcwd())) |
|
18 | 18 | try: |
|
19 | app = Application() | |
|
19 | app = BaseIPythonApplication() | |
|
20 | 20 | # The lines below are copied from Application.initialize() |
|
21 | app.create_default_config() | |
|
22 |
app. |
|
|
23 | app.set_default_config_log_level() | |
|
24 | ||
|
25 | # Find resources needed for filesystem access, using information from | |
|
26 | # the above two | |
|
27 | app.find_ipython_dir() | |
|
28 | app.find_resources() | |
|
29 | app.find_config_file_name() | |
|
30 | app.find_config_file_paths() | |
|
31 | ||
|
32 | # File-based config | |
|
33 | app.pre_load_file_config() | |
|
34 | app.load_file_config(suppress_errors=False) | |
|
21 | app.init_profile_dir() | |
|
22 | app.init_config_files() | |
|
23 | app.load_config_file(suppress_errors=False) | |
|
35 | 24 | finally: |
|
36 | 25 | os.chdir(old_wd) |
|
37 | 26 | |
@@ -48,22 +37,11 b' def test_unicode_ipdir():' | |||
|
48 | 37 | old_ipdir2 = os.environ.pop("IPYTHON_DIR", None) |
|
49 | 38 | os.environ["IPYTHONDIR"] = ipdir.encode("utf-8") |
|
50 | 39 | try: |
|
51 | app = Application() | |
|
40 | app = BaseIPythonApplication() | |
|
52 | 41 | # The lines below are copied from Application.initialize() |
|
53 | app.create_default_config() | |
|
54 |
app. |
|
|
55 | app.set_default_config_log_level() | |
|
56 | ||
|
57 | # Find resources needed for filesystem access, using information from | |
|
58 | # the above two | |
|
59 | app.find_ipython_dir() | |
|
60 | app.find_resources() | |
|
61 | app.find_config_file_name() | |
|
62 | app.find_config_file_paths() | |
|
63 | ||
|
64 | # File-based config | |
|
65 | app.pre_load_file_config() | |
|
66 | app.load_file_config(suppress_errors=False) | |
|
42 | app.init_profile_dir() | |
|
43 | app.init_config_files() | |
|
44 | app.load_config_file(suppress_errors=False) | |
|
67 | 45 | finally: |
|
68 | 46 | if old_ipdir1: |
|
69 | 47 | os.environ["IPYTHONDIR"] = old_ipdir1 |
@@ -27,7 +27,7 b' from pygments.styles import get_all_styles' | |||
|
27 | 27 | |
|
28 | 28 | # Local imports |
|
29 | 29 | from IPython.config.application import boolean_flag |
|
30 |
from IPython.core. |
|
|
30 | from IPython.core.application import ProfileDir, BaseIPythonApplication | |
|
31 | 31 | from IPython.frontend.qt.console.frontend_widget import FrontendWidget |
|
32 | 32 | from IPython.frontend.qt.console.ipython_widget import IPythonWidget |
|
33 | 33 | from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget |
@@ -1,7 +1,7 b'' | |||
|
1 | 1 | #!/usr/bin/env python |
|
2 | 2 | # encoding: utf-8 |
|
3 | 3 | """ |
|
4 |
The :class:`~IPython.core. |
|
|
4 | The :class:`~IPython.core.application.Application` object for the command | |
|
5 | 5 | line :command:`ipython` program. |
|
6 | 6 | |
|
7 | 7 | Authors |
@@ -37,7 +37,7 b' from IPython.core import release' | |||
|
37 | 37 | from IPython.core import usage |
|
38 | 38 | from IPython.core.crashhandler import CrashHandler |
|
39 | 39 | from IPython.core.formatters import PlainTextFormatter |
|
40 |
from IPython.core. |
|
|
40 | from IPython.core.application import ( | |
|
41 | 41 | ProfileDir, BaseIPythonApplication, base_flags, base_aliases |
|
42 | 42 | ) |
|
43 | 43 | from IPython.core.shellapp import ( |
@@ -32,7 +32,7 b' from subprocess import Popen, PIPE' | |||
|
32 | 32 | |
|
33 | 33 | from IPython.core import release |
|
34 | 34 | from IPython.core.crashhandler import CrashHandler |
|
35 |
from IPython.core. |
|
|
35 | from IPython.core.application import ( | |
|
36 | 36 | BaseIPythonApplication, |
|
37 | 37 | base_aliases as base_ip_aliases, |
|
38 | 38 | base_flags as base_ip_flags |
@@ -33,7 +33,7 b' from zmq.eventloop import ioloop' | |||
|
33 | 33 | |
|
34 | 34 | from IPython.config.application import Application, boolean_flag |
|
35 | 35 | from IPython.config.loader import Config |
|
36 |
from IPython.core. |
|
|
36 | from IPython.core.application import BaseIPythonApplication, ProfileDir | |
|
37 | 37 | from IPython.utils.daemonize import daemonize |
|
38 | 38 | from IPython.utils.importstring import import_item |
|
39 | 39 | from IPython.utils.traitlets import Int, Unicode, Bool, CFloat, Dict, List |
@@ -37,7 +37,7 b' from zmq.log.handlers import PUBHandler' | |||
|
37 | 37 | from zmq.utils import jsonapi as json |
|
38 | 38 | |
|
39 | 39 | from IPython.config.application import boolean_flag |
|
40 |
from IPython.core. |
|
|
40 | from IPython.core.application import ProfileDir | |
|
41 | 41 | |
|
42 | 42 | from IPython.parallel.apps.baseapp import ( |
|
43 | 43 | BaseParallelApplication, |
@@ -28,7 +28,7 b' import sys' | |||
|
28 | 28 | import zmq |
|
29 | 29 | from zmq.eventloop import ioloop |
|
30 | 30 | |
|
31 |
from IPython.core. |
|
|
31 | from IPython.core.application import ProfileDir | |
|
32 | 32 | from IPython.parallel.apps.baseapp import BaseParallelApplication |
|
33 | 33 | from IPython.zmq.log import EnginePUBHandler |
|
34 | 34 |
@@ -25,7 +25,7 b' import sys' | |||
|
25 | 25 | |
|
26 | 26 | import zmq |
|
27 | 27 | |
|
28 |
from IPython.core. |
|
|
28 | from IPython.core.application import ProfileDir | |
|
29 | 29 | from IPython.utils.traitlets import Bool, Dict, Unicode |
|
30 | 30 | |
|
31 | 31 | from IPython.parallel.apps.baseapp import ( |
@@ -40,7 +40,7 b' from IPython.parallel import util' | |||
|
40 | 40 | from IPython.zmq.session import Session, Message |
|
41 | 41 | |
|
42 | 42 | from .asyncresult import AsyncResult, AsyncHubResult |
|
43 |
from IPython.core. |
|
|
43 | from IPython.core.application import ProfileDir, ProfileDirError | |
|
44 | 44 | from .view import DirectView, LoadBalancedView |
|
45 | 45 | |
|
46 | 46 | #-------------------------------------------------------------------------- |
@@ -141,7 +141,7 b' class SQLiteDB(BaseDB):' | |||
|
141 | 141 | self.table = '_'+self.session.replace('-','_') |
|
142 | 142 | if not self.location: |
|
143 | 143 | # get current profile |
|
144 |
from IPython.core. |
|
|
144 | from IPython.core.application import BaseIPythonApplication | |
|
145 | 145 | if BaseIPythonApplication.initialized(): |
|
146 | 146 | app = BaseIPythonApplication.instance() |
|
147 | 147 | if app.profile_dir is not None: |
@@ -28,7 +28,7 b' import zmq' | |||
|
28 | 28 | # Local imports. |
|
29 | 29 | from IPython.config.configurable import Configurable |
|
30 | 30 | from IPython.config.application import boolean_flag |
|
31 |
from IPython.core. |
|
|
31 | from IPython.core.application import ProfileDir | |
|
32 | 32 | from IPython.core.shellapp import ( |
|
33 | 33 | InteractiveShellApp, shell_flags, shell_aliases |
|
34 | 34 | ) |
@@ -25,7 +25,7 b' import zmq' | |||
|
25 | 25 | |
|
26 | 26 | # IPython imports. |
|
27 | 27 | from IPython.core.ultratb import FormattedTB |
|
28 |
from IPython.core. |
|
|
28 | from IPython.core.application import ( | |
|
29 | 29 | BaseIPythonApplication, base_flags, base_aliases |
|
30 | 30 | ) |
|
31 | 31 | from IPython.utils import io |
|
1 | NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now