##// END OF EJS Templates
Fix bug tracker address to point to github
Fernando Perez -
Show More
@@ -1,665 +1,664 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 The :class:`~IPython.core.application.Application` object for the command
5 5 line :command:`ipython` program.
6 6
7 7 Authors
8 8 -------
9 9
10 10 * Brian Granger
11 11 * Fernando Perez
12 12 """
13 13
14 14 #-----------------------------------------------------------------------------
15 15 # Copyright (C) 2008-2010 The IPython Development Team
16 16 #
17 17 # Distributed under the terms of the BSD License. The full license is in
18 18 # the file COPYING, distributed as part of this software.
19 19 #-----------------------------------------------------------------------------
20 20
21 21 #-----------------------------------------------------------------------------
22 22 # Imports
23 23 #-----------------------------------------------------------------------------
24 24
25 25 from __future__ import absolute_import
26 26
27 27 import logging
28 28 import os
29 29 import sys
30 30
31 31 from IPython.core import release
32 32 from IPython.core.crashhandler import CrashHandler
33 33 from IPython.core.application import Application, BaseAppConfigLoader
34 34 from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell
35 35 from IPython.config.loader import (
36 36 Config,
37 37 PyFileConfigLoader
38 38 )
39 39 from IPython.lib import inputhook
40 40 from IPython.utils.path import filefind, get_ipython_dir
41 41 from IPython.core import usage
42 42
43 43 #-----------------------------------------------------------------------------
44 44 # Globals, utilities and helpers
45 45 #-----------------------------------------------------------------------------
46 46
47 47 #: The default config file name for this application.
48 48 default_config_file_name = u'ipython_config.py'
49 49
50 50
51 51 class IPAppConfigLoader(BaseAppConfigLoader):
52 52
53 53 def _add_arguments(self):
54 54 super(IPAppConfigLoader, self)._add_arguments()
55 55 paa = self.parser.add_argument
56 56 paa('-p',
57 57 '--profile', dest='Global.profile', type=unicode,
58 58 help=
59 59 """The string name of the ipython profile to be used. Assume that your
60 60 config file is ipython_config-<name>.py (looks in current dir first,
61 61 then in IPYTHON_DIR). This is a quick way to keep and load multiple
62 62 config files for different tasks, especially if include your basic one
63 63 in your more specialized ones. You can keep a basic
64 64 IPYTHON_DIR/ipython_config.py file and then have other 'profiles' which
65 65 include this one and load extra things for particular tasks.""",
66 66 metavar='Global.profile')
67 67 paa('--config-file',
68 68 dest='Global.config_file', type=unicode,
69 69 help=
70 70 """Set the config file name to override default. Normally IPython
71 71 loads ipython_config.py (from current directory) or
72 72 IPYTHON_DIR/ipython_config.py. If the loading of your config file
73 73 fails, IPython starts with a bare bones configuration (no modules
74 74 loaded at all).""",
75 75 metavar='Global.config_file')
76 76 paa('--autocall',
77 77 dest='InteractiveShell.autocall', type=int,
78 78 help=
79 79 """Make IPython automatically call any callable object even if you
80 80 didn't type explicit parentheses. For example, 'str 43' becomes
81 81 'str(43)' automatically. The value can be '0' to disable the feature,
82 82 '1' for 'smart' autocall, where it is not applied if there are no more
83 83 arguments on the line, and '2' for 'full' autocall, where all callable
84 84 objects are automatically called (even if no arguments are present).
85 85 The default is '1'.""",
86 86 metavar='InteractiveShell.autocall')
87 87 paa('--autoindent',
88 88 action='store_true', dest='InteractiveShell.autoindent',
89 89 help='Turn on autoindenting.')
90 90 paa('--no-autoindent',
91 91 action='store_false', dest='InteractiveShell.autoindent',
92 92 help='Turn off autoindenting.')
93 93 paa('--automagic',
94 94 action='store_true', dest='InteractiveShell.automagic',
95 95 help=
96 96 """Turn on the auto calling of magic commands. Type %%magic at the
97 97 IPython prompt for more information.""")
98 98 paa('--no-automagic',
99 99 action='store_false', dest='InteractiveShell.automagic',
100 100 help='Turn off the auto calling of magic commands.')
101 101 paa('--autoedit-syntax',
102 102 action='store_true', dest='TerminalInteractiveShell.autoedit_syntax',
103 103 help='Turn on auto editing of files with syntax errors.')
104 104 paa('--no-autoedit-syntax',
105 105 action='store_false', dest='TerminalInteractiveShell.autoedit_syntax',
106 106 help='Turn off auto editing of files with syntax errors.')
107 107 paa('--banner',
108 108 action='store_true', dest='Global.display_banner',
109 109 help='Display a banner upon starting IPython.')
110 110 paa('--no-banner',
111 111 action='store_false', dest='Global.display_banner',
112 112 help="Don't display a banner upon starting IPython.")
113 113 paa('--cache-size',
114 114 type=int, dest='InteractiveShell.cache_size',
115 115 help=
116 116 """Set the size of the output cache. The default is 1000, you can
117 117 change it permanently in your config file. Setting it to 0 completely
118 118 disables the caching system, and the minimum value accepted is 20 (if
119 119 you provide a value less than 20, it is reset to 0 and a warning is
120 120 issued). This limit is defined because otherwise you'll spend more
121 121 time re-flushing a too small cache than working""",
122 122 metavar='InteractiveShell.cache_size')
123 123 paa('--classic',
124 124 action='store_true', dest='Global.classic',
125 125 help="Gives IPython a similar feel to the classic Python prompt.")
126 126 paa('--colors',
127 127 type=str, dest='InteractiveShell.colors',
128 128 help="Set the color scheme (NoColor, Linux, and LightBG).",
129 129 metavar='InteractiveShell.colors')
130 130 paa('--color-info',
131 131 action='store_true', dest='InteractiveShell.color_info',
132 132 help=
133 133 """IPython can display information about objects via a set of func-
134 134 tions, and optionally can use colors for this, syntax highlighting
135 135 source code and various other elements. However, because this
136 136 information is passed through a pager (like 'less') and many pagers get
137 137 confused with color codes, this option is off by default. You can test
138 138 it and turn it on permanently in your ipython_config.py file if it
139 139 works for you. Test it and turn it on permanently if it works with
140 140 your system. The magic function %%color_info allows you to toggle this
141 141 inter- actively for testing.""")
142 142 paa('--no-color-info',
143 143 action='store_false', dest='InteractiveShell.color_info',
144 144 help="Disable using colors for info related things.")
145 145 paa('--confirm-exit',
146 146 action='store_true', dest='TerminalInteractiveShell.confirm_exit',
147 147 help=
148 148 """Set to confirm when you try to exit IPython with an EOF (Control-D
149 149 in Unix, Control-Z/Enter in Windows). By typing 'exit', 'quit' or
150 150 '%%Exit', you can force a direct exit without any confirmation.""")
151 151 paa('--no-confirm-exit',
152 152 action='store_false', dest='TerminalInteractiveShell.confirm_exit',
153 153 help="Don't prompt the user when exiting.")
154 154 paa('--deep-reload',
155 155 action='store_true', dest='InteractiveShell.deep_reload',
156 156 help=
157 157 """Enable deep (recursive) reloading by default. IPython can use the
158 158 deep_reload module which reloads changes in modules recursively (it
159 159 replaces the reload() function, so you don't need to change anything to
160 160 use it). deep_reload() forces a full reload of modules whose code may
161 161 have changed, which the default reload() function does not. When
162 162 deep_reload is off, IPython will use the normal reload(), but
163 163 deep_reload will still be available as dreload(). This fea- ture is off
164 164 by default [which means that you have both normal reload() and
165 165 dreload()].""")
166 166 paa('--no-deep-reload',
167 167 action='store_false', dest='InteractiveShell.deep_reload',
168 168 help="Disable deep (recursive) reloading by default.")
169 169 paa('--editor',
170 170 type=str, dest='TerminalInteractiveShell.editor',
171 171 help="Set the editor used by IPython (default to $EDITOR/vi/notepad).",
172 172 metavar='TerminalInteractiveShell.editor')
173 173 paa('--log','-l',
174 174 action='store_true', dest='InteractiveShell.logstart',
175 175 help="Start logging to the default log file (./ipython_log.py).")
176 176 paa('--logfile','-lf',
177 177 type=unicode, dest='InteractiveShell.logfile',
178 178 help="Start logging to logfile with this name.",
179 179 metavar='InteractiveShell.logfile')
180 180 paa('--log-append','-la',
181 181 type=unicode, dest='InteractiveShell.logappend',
182 182 help="Start logging to the given file in append mode.",
183 183 metavar='InteractiveShell.logfile')
184 184 paa('--pdb',
185 185 action='store_true', dest='InteractiveShell.pdb',
186 186 help="Enable auto calling the pdb debugger after every exception.")
187 187 paa('--no-pdb',
188 188 action='store_false', dest='InteractiveShell.pdb',
189 189 help="Disable auto calling the pdb debugger after every exception.")
190 190 paa('--pprint',
191 191 action='store_true', dest='InteractiveShell.pprint',
192 192 help="Enable auto pretty printing of results.")
193 193 paa('--no-pprint',
194 194 action='store_false', dest='InteractiveShell.pprint',
195 195 help="Disable auto auto pretty printing of results.")
196 196 paa('--prompt-in1','-pi1',
197 197 type=str, dest='InteractiveShell.prompt_in1',
198 198 help=
199 199 """Set the main input prompt ('In [\#]: '). Note that if you are using
200 200 numbered prompts, the number is represented with a '\#' in the string.
201 201 Don't forget to quote strings with spaces embedded in them. Most
202 202 bash-like escapes can be used to customize IPython's prompts, as well
203 203 as a few additional ones which are IPython-spe- cific. All valid
204 204 prompt escapes are described in detail in the Customization section of
205 205 the IPython manual.""",
206 206 metavar='InteractiveShell.prompt_in1')
207 207 paa('--prompt-in2','-pi2',
208 208 type=str, dest='InteractiveShell.prompt_in2',
209 209 help=
210 210 """Set the secondary input prompt (' .\D.: '). Similar to the previous
211 211 option, but used for the continuation prompts. The special sequence
212 212 '\D' is similar to '\#', but with all digits replaced by dots (so you
213 213 can have your continuation prompt aligned with your input prompt).
214 214 Default: ' .\D.: ' (note three spaces at the start for alignment with
215 215 'In [\#]')""",
216 216 metavar='InteractiveShell.prompt_in2')
217 217 paa('--prompt-out','-po',
218 218 type=str, dest='InteractiveShell.prompt_out',
219 219 help="Set the output prompt ('Out[\#]:')",
220 220 metavar='InteractiveShell.prompt_out')
221 221 paa('--quick',
222 222 action='store_true', dest='Global.quick',
223 223 help="Enable quick startup with no config files.")
224 224 paa('--readline',
225 225 action='store_true', dest='InteractiveShell.readline_use',
226 226 help="Enable readline for command line usage.")
227 227 paa('--no-readline',
228 228 action='store_false', dest='InteractiveShell.readline_use',
229 229 help="Disable readline for command line usage.")
230 230 paa('--screen-length','-sl',
231 231 type=int, dest='TerminalInteractiveShell.screen_length',
232 232 help=
233 233 """Number of lines of your screen, used to control printing of very
234 234 long strings. Strings longer than this number of lines will be sent
235 235 through a pager instead of directly printed. The default value for
236 236 this is 0, which means IPython will auto-detect your screen size every
237 237 time it needs to print certain potentially long strings (this doesn't
238 238 change the behavior of the 'print' keyword, it's only triggered
239 239 internally). If for some reason this isn't working well (it needs
240 240 curses support), specify it yourself. Otherwise don't change the
241 241 default.""",
242 242 metavar='TerminalInteractiveShell.screen_length')
243 243 paa('--separate-in','-si',
244 244 type=str, dest='InteractiveShell.separate_in',
245 245 help="Separator before input prompts. Default '\\n'.",
246 246 metavar='InteractiveShell.separate_in')
247 247 paa('--separate-out','-so',
248 248 type=str, dest='InteractiveShell.separate_out',
249 249 help="Separator before output prompts. Default 0 (nothing).",
250 250 metavar='InteractiveShell.separate_out')
251 251 paa('--separate-out2','-so2',
252 252 type=str, dest='InteractiveShell.separate_out2',
253 253 help="Separator after output prompts. Default 0 (nonight).",
254 254 metavar='InteractiveShell.separate_out2')
255 255 paa('--no-sep',
256 256 action='store_true', dest='Global.nosep',
257 257 help="Eliminate all spacing between prompts.")
258 258 paa('--term-title',
259 259 action='store_true', dest='TerminalInteractiveShell.term_title',
260 260 help="Enable auto setting the terminal title.")
261 261 paa('--no-term-title',
262 262 action='store_false', dest='TerminalInteractiveShell.term_title',
263 263 help="Disable auto setting the terminal title.")
264 264 paa('--xmode',
265 265 type=str, dest='InteractiveShell.xmode',
266 266 help=
267 267 """Exception reporting mode ('Plain','Context','Verbose'). Plain:
268 268 similar to python's normal traceback printing. Context: prints 5 lines
269 269 of context source code around each line in the traceback. Verbose:
270 270 similar to Context, but additionally prints the variables currently
271 271 visible where the exception happened (shortening their strings if too
272 272 long). This can potentially be very slow, if you happen to have a huge
273 273 data structure whose string representation is complex to compute.
274 274 Your computer may appear to freeze for a while with cpu usage at 100%%.
275 275 If this occurs, you can cancel the traceback with Ctrl-C (maybe hitting
276 276 it more than once).
277 277 """,
278 278 metavar='InteractiveShell.xmode')
279 279 paa('--ext',
280 280 type=str, dest='Global.extra_extension',
281 281 help="The dotted module name of an IPython extension to load.",
282 282 metavar='Global.extra_extension')
283 283 paa('-c',
284 284 type=str, dest='Global.code_to_run',
285 285 help="Execute the given command string.",
286 286 metavar='Global.code_to_run')
287 287 paa('-i',
288 288 action='store_true', dest='Global.force_interact',
289 289 help=
290 290 "If running code from the command line, become interactive afterwards.")
291 291
292 292 # Options to start with GUI control enabled from the beginning
293 293 paa('--gui',
294 294 type=str, dest='Global.gui',
295 295 help="Enable GUI event loop integration ('qt', 'wx', 'gtk').",
296 296 metavar='gui-mode')
297 297 paa('--pylab','-pylab',
298 298 type=str, dest='Global.pylab',
299 299 nargs='?', const='auto', metavar='gui-mode',
300 300 help="Pre-load matplotlib and numpy for interactive use. "+
301 301 "If no value is given, the gui backend is matplotlib's, else use "+
302 302 "one of: ['tk', 'qt', 'wx', 'gtk'].")
303 303
304 304 # Legacy GUI options. Leave them in for backwards compatibility, but the
305 305 # 'thread' names are really a misnomer now.
306 306 paa('--wthread', '-wthread',
307 307 action='store_true', dest='Global.wthread',
308 308 help=
309 309 """Enable wxPython event loop integration. (DEPRECATED, use --gui wx)""")
310 310 paa('--q4thread', '--qthread', '-q4thread', '-qthread',
311 311 action='store_true', dest='Global.q4thread',
312 312 help=
313 313 """Enable Qt4 event loop integration. Qt3 is no longer supported.
314 314 (DEPRECATED, use --gui qt)""")
315 315 paa('--gthread', '-gthread',
316 316 action='store_true', dest='Global.gthread',
317 317 help=
318 318 """Enable GTK event loop integration. (DEPRECATED, use --gui gtk)""")
319 319
320 320
321 321 #-----------------------------------------------------------------------------
322 322 # Crash handler for this application
323 323 #-----------------------------------------------------------------------------
324 324
325
326 325 _message_template = """\
327 326 Oops, $self.app_name crashed. We do our best to make it stable, but...
328 327
329 328 A crash report was automatically generated with the following information:
330 329 - A verbatim copy of the crash traceback.
331 330 - A copy of your input history during this session.
332 331 - Data on your current $self.app_name configuration.
333 332
334 333 It was left in the file named:
335 334 \t'$self.crash_report_fname'
336 335 If you can email this file to the developers, the information in it will help
337 336 them in understanding and correcting the problem.
338 337
339 338 You can mail it to: $self.contact_name at $self.contact_email
340 339 with the subject '$self.app_name Crash Report'.
341 340
342 341 If you want to do it now, the following command will work (under Unix):
343 342 mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname
344 343
345 344 To ensure accurate tracking of this issue, please file a report about it at:
346 345 $self.bug_tracker
347 346 """
348 347
349 348 class IPAppCrashHandler(CrashHandler):
350 349 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
351 350
352 351 message_template = _message_template
353 352
354 353 def __init__(self, app):
355 354 contact_name = release.authors['Fernando'][0]
356 355 contact_email = release.authors['Fernando'][1]
357 bug_tracker = 'https://bugs.launchpad.net/ipython/+filebug'
356 bug_tracker = 'http://github.com/ipython/ipython/issues'
358 357 super(IPAppCrashHandler,self).__init__(
359 358 app, contact_name, contact_email, bug_tracker
360 359 )
361 360
362 361 def make_report(self,traceback):
363 362 """Return a string containing a crash report."""
364 363
365 364 sec_sep = self.section_sep
366 365 # Start with parent report
367 366 report = [super(IPAppCrashHandler, self).make_report(traceback)]
368 367 # Add interactive-specific info we may have
369 368 rpt_add = report.append
370 369 try:
371 370 rpt_add(sec_sep+"History of session input:")
372 371 for line in self.app.shell.user_ns['_ih']:
373 372 rpt_add(line)
374 373 rpt_add('\n*** Last line of input (may not be in above history):\n')
375 374 rpt_add(self.app.shell._last_input_line+'\n')
376 375 except:
377 376 pass
378 377
379 378 return ''.join(report)
380 379
381 380
382 381 #-----------------------------------------------------------------------------
383 382 # Main classes and functions
384 383 #-----------------------------------------------------------------------------
385 384
386 385 class IPythonApp(Application):
387 386 name = u'ipython'
388 387 #: argparse formats better the 'usage' than the 'description' field
389 388 description = None
390 389 usage = usage.cl_usage
391 390 command_line_loader = IPAppConfigLoader
392 391 default_config_file_name = default_config_file_name
393 392 crash_handler_class = IPAppCrashHandler
394 393
395 394 def create_default_config(self):
396 395 super(IPythonApp, self).create_default_config()
397 396 # Eliminate multiple lookups
398 397 Global = self.default_config.Global
399 398
400 399 # Set all default values
401 400 Global.display_banner = True
402 401
403 402 # If the -c flag is given or a file is given to run at the cmd line
404 403 # like "ipython foo.py", normally we exit without starting the main
405 404 # loop. The force_interact config variable allows a user to override
406 405 # this and interact. It is also set by the -i cmd line flag, just
407 406 # like Python.
408 407 Global.force_interact = False
409 408
410 409 # By default always interact by starting the IPython mainloop.
411 410 Global.interact = True
412 411
413 412 # No GUI integration by default
414 413 Global.gui = False
415 414 # Pylab off by default
416 415 Global.pylab = False
417 416
418 417 # Deprecated versions of gui support that used threading, we support
419 418 # them just for bacwards compatibility as an alternate spelling for
420 419 # '--gui X'
421 420 Global.qthread = False
422 421 Global.q4thread = False
423 422 Global.wthread = False
424 423 Global.gthread = False
425 424
426 425 def load_file_config(self):
427 426 if hasattr(self.command_line_config.Global, 'quick'):
428 427 if self.command_line_config.Global.quick:
429 428 self.file_config = Config()
430 429 return
431 430 super(IPythonApp, self).load_file_config()
432 431
433 432 def post_load_file_config(self):
434 433 if hasattr(self.command_line_config.Global, 'extra_extension'):
435 434 if not hasattr(self.file_config.Global, 'extensions'):
436 435 self.file_config.Global.extensions = []
437 436 self.file_config.Global.extensions.append(
438 437 self.command_line_config.Global.extra_extension)
439 438 del self.command_line_config.Global.extra_extension
440 439
441 440 def pre_construct(self):
442 441 config = self.master_config
443 442
444 443 if hasattr(config.Global, 'classic'):
445 444 if config.Global.classic:
446 445 config.InteractiveShell.cache_size = 0
447 446 config.InteractiveShell.pprint = 0
448 447 config.InteractiveShell.prompt_in1 = '>>> '
449 448 config.InteractiveShell.prompt_in2 = '... '
450 449 config.InteractiveShell.prompt_out = ''
451 450 config.InteractiveShell.separate_in = \
452 451 config.InteractiveShell.separate_out = \
453 452 config.InteractiveShell.separate_out2 = ''
454 453 config.InteractiveShell.colors = 'NoColor'
455 454 config.InteractiveShell.xmode = 'Plain'
456 455
457 456 if hasattr(config.Global, 'nosep'):
458 457 if config.Global.nosep:
459 458 config.InteractiveShell.separate_in = \
460 459 config.InteractiveShell.separate_out = \
461 460 config.InteractiveShell.separate_out2 = ''
462 461
463 462 # if there is code of files to run from the cmd line, don't interact
464 463 # unless the -i flag (Global.force_interact) is true.
465 464 code_to_run = config.Global.get('code_to_run','')
466 465 file_to_run = False
467 466 if self.extra_args and self.extra_args[0]:
468 467 file_to_run = True
469 468 if file_to_run or code_to_run:
470 469 if not config.Global.force_interact:
471 470 config.Global.interact = False
472 471
473 472 def construct(self):
474 473 # I am a little hesitant to put these into InteractiveShell itself.
475 474 # But that might be the place for them
476 475 sys.path.insert(0, '')
477 476
478 477 # Create an InteractiveShell instance.
479 478 self.shell = TerminalInteractiveShell.instance(config=self.master_config)
480 479
481 480 def post_construct(self):
482 481 """Do actions after construct, but before starting the app."""
483 482 config = self.master_config
484 483
485 484 # shell.display_banner should always be False for the terminal
486 485 # based app, because we call shell.show_banner() by hand below
487 486 # so the banner shows *before* all extension loading stuff.
488 487 self.shell.display_banner = False
489 488 if config.Global.display_banner and \
490 489 config.Global.interact:
491 490 self.shell.show_banner()
492 491
493 492 # Make sure there is a space below the banner.
494 493 if self.log_level <= logging.INFO: print
495 494
496 495 # Now a variety of things that happen after the banner is printed.
497 496 self._enable_gui_pylab()
498 497 self._load_extensions()
499 498 self._run_exec_lines()
500 499 self._run_exec_files()
501 500 self._run_cmd_line_code()
502 501
503 502 def _enable_gui_pylab(self):
504 503 """Enable GUI event loop integration, taking pylab into account."""
505 504 Global = self.master_config.Global
506 505
507 506 # Select which gui to use
508 507 if Global.gui:
509 508 gui = Global.gui
510 509 # The following are deprecated, but there's likely to be a lot of use
511 510 # of this form out there, so we might as well support it for now. But
512 511 # the --gui option above takes precedence.
513 512 elif Global.wthread:
514 513 gui = inputhook.GUI_WX
515 514 elif Global.qthread:
516 515 gui = inputhook.GUI_QT
517 516 elif Global.gthread:
518 517 gui = inputhook.GUI_GTK
519 518 else:
520 519 gui = None
521 520
522 521 # Using --pylab will also require gui activation, though which toolkit
523 522 # to use may be chosen automatically based on mpl configuration.
524 523 if Global.pylab:
525 524 activate = self.shell.enable_pylab
526 525 if Global.pylab == 'auto':
527 526 gui = None
528 527 else:
529 528 gui = Global.pylab
530 529 else:
531 530 # Enable only GUI integration, no pylab
532 531 activate = inputhook.enable_gui
533 532
534 533 if gui or Global.pylab:
535 534 try:
536 535 self.log.info("Enabling GUI event loop integration, "
537 536 "toolkit=%s, pylab=%s" % (gui, Global.pylab) )
538 537 activate(gui)
539 538 except:
540 539 self.log.warn("Error in enabling GUI event loop integration:")
541 540 self.shell.showtraceback()
542 541
543 542 def _load_extensions(self):
544 543 """Load all IPython extensions in Global.extensions.
545 544
546 545 This uses the :meth:`ExtensionManager.load_extensions` to load all
547 546 the extensions listed in ``self.master_config.Global.extensions``.
548 547 """
549 548 try:
550 549 if hasattr(self.master_config.Global, 'extensions'):
551 550 self.log.debug("Loading IPython extensions...")
552 551 extensions = self.master_config.Global.extensions
553 552 for ext in extensions:
554 553 try:
555 554 self.log.info("Loading IPython extension: %s" % ext)
556 555 self.shell.extension_manager.load_extension(ext)
557 556 except:
558 557 self.log.warn("Error in loading extension: %s" % ext)
559 558 self.shell.showtraceback()
560 559 except:
561 560 self.log.warn("Unknown error in loading extensions:")
562 561 self.shell.showtraceback()
563 562
564 563 def _run_exec_lines(self):
565 564 """Run lines of code in Global.exec_lines in the user's namespace."""
566 565 try:
567 566 if hasattr(self.master_config.Global, 'exec_lines'):
568 567 self.log.debug("Running code from Global.exec_lines...")
569 568 exec_lines = self.master_config.Global.exec_lines
570 569 for line in exec_lines:
571 570 try:
572 571 self.log.info("Running code in user namespace: %s" %
573 572 line)
574 573 self.shell.run_cell(line)
575 574 except:
576 575 self.log.warn("Error in executing line in user "
577 576 "namespace: %s" % line)
578 577 self.shell.showtraceback()
579 578 except:
580 579 self.log.warn("Unknown error in handling Global.exec_lines:")
581 580 self.shell.showtraceback()
582 581
583 582 def _exec_file(self, fname):
584 583 full_filename = filefind(fname, [u'.', self.ipython_dir])
585 584 if os.path.isfile(full_filename):
586 585 if full_filename.endswith(u'.py'):
587 586 self.log.info("Running file in user namespace: %s" %
588 587 full_filename)
589 588 # Ensure that __file__ is always defined to match Python behavior
590 589 self.shell.user_ns['__file__'] = fname
591 590 try:
592 591 self.shell.safe_execfile(full_filename, self.shell.user_ns)
593 592 finally:
594 593 del self.shell.user_ns['__file__']
595 594 elif full_filename.endswith('.ipy'):
596 595 self.log.info("Running file in user namespace: %s" %
597 596 full_filename)
598 597 self.shell.safe_execfile_ipy(full_filename)
599 598 else:
600 599 self.log.warn("File does not have a .py or .ipy extension: <%s>"
601 600 % full_filename)
602 601 def _run_exec_files(self):
603 602 try:
604 603 if hasattr(self.master_config.Global, 'exec_files'):
605 604 self.log.debug("Running files in Global.exec_files...")
606 605 exec_files = self.master_config.Global.exec_files
607 606 for fname in exec_files:
608 607 self._exec_file(fname)
609 608 except:
610 609 self.log.warn("Unknown error in handling Global.exec_files:")
611 610 self.shell.showtraceback()
612 611
613 612 def _run_cmd_line_code(self):
614 613 if hasattr(self.master_config.Global, 'code_to_run'):
615 614 line = self.master_config.Global.code_to_run
616 615 try:
617 616 self.log.info("Running code given at command line (-c): %s" %
618 617 line)
619 618 self.shell.run_cell(line)
620 619 except:
621 620 self.log.warn("Error in executing line in user namespace: %s" %
622 621 line)
623 622 self.shell.showtraceback()
624 623 return
625 624 # Like Python itself, ignore the second if the first of these is present
626 625 try:
627 626 fname = self.extra_args[0]
628 627 except:
629 628 pass
630 629 else:
631 630 try:
632 631 self._exec_file(fname)
633 632 except:
634 633 self.log.warn("Error in executing file in user namespace: %s" %
635 634 fname)
636 635 self.shell.showtraceback()
637 636
638 637 def start_app(self):
639 638 if self.master_config.Global.interact:
640 639 self.log.debug("Starting IPython's mainloop...")
641 640 self.shell.mainloop()
642 641 else:
643 642 self.log.debug("IPython not interactive, start_app is no-op...")
644 643
645 644
646 645 def load_default_config(ipython_dir=None):
647 646 """Load the default config file from the default ipython_dir.
648 647
649 648 This is useful for embedded shells.
650 649 """
651 650 if ipython_dir is None:
652 651 ipython_dir = get_ipython_dir()
653 652 cl = PyFileConfigLoader(default_config_file_name, ipython_dir)
654 653 config = cl.load_config()
655 654 return config
656 655
657 656
658 657 def launch_new_instance():
659 658 """Create and run a full blown IPython instance"""
660 659 app = IPythonApp()
661 660 app.start()
662 661
663 662
664 663 if __name__ == '__main__':
665 664 launch_new_instance()
@@ -1,538 +1,538 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 The IPython cluster directory
5 5 """
6 6
7 7 #-----------------------------------------------------------------------------
8 8 # Copyright (C) 2008-2009 The IPython Development Team
9 9 #
10 10 # Distributed under the terms of the BSD License. The full license is in
11 11 # the file COPYING, distributed as part of this software.
12 12 #-----------------------------------------------------------------------------
13 13
14 14 #-----------------------------------------------------------------------------
15 15 # Imports
16 16 #-----------------------------------------------------------------------------
17 17
18 18 from __future__ import with_statement
19 19
20 20 import os
21 21 import shutil
22 22 import sys
23 23 import warnings
24 24
25 25 from twisted.python import log
26 26
27 27 from IPython.config.loader import PyFileConfigLoader
28 28 from IPython.core.application import Application, BaseAppConfigLoader
29 29 from IPython.config.configurable import Configurable
30 30 from IPython.core.crashhandler import CrashHandler
31 31 from IPython.core import release
32 32 from IPython.utils.path import (
33 33 get_ipython_package_dir,
34 34 expand_path
35 35 )
36 36 from IPython.utils.traitlets import Unicode
37 37
38 38 #-----------------------------------------------------------------------------
39 39 # Warnings control
40 40 #-----------------------------------------------------------------------------
41 41 # Twisted generates annoying warnings with Python 2.6, as will do other code
42 42 # that imports 'sets' as of today
43 43 warnings.filterwarnings('ignore', 'the sets module is deprecated',
44 44 DeprecationWarning )
45 45
46 46 # This one also comes from Twisted
47 47 warnings.filterwarnings('ignore', 'the sha module is deprecated',
48 48 DeprecationWarning)
49 49
50 50 #-----------------------------------------------------------------------------
51 51 # Module errors
52 52 #-----------------------------------------------------------------------------
53 53
54 54 class ClusterDirError(Exception):
55 55 pass
56 56
57 57
58 58 class PIDFileError(Exception):
59 59 pass
60 60
61 61
62 62 #-----------------------------------------------------------------------------
63 63 # Class for managing cluster directories
64 64 #-----------------------------------------------------------------------------
65 65
66 66 class ClusterDir(Configurable):
67 67 """An object to manage the cluster directory and its resources.
68 68
69 69 The cluster directory is used by :command:`ipcontroller`,
70 70 :command:`ipcontroller` and :command:`ipcontroller` to manage the
71 71 configuration, logging and security of these applications.
72 72
73 73 This object knows how to find, create and manage these directories. This
74 74 should be used by any code that want's to handle cluster directories.
75 75 """
76 76
77 77 security_dir_name = Unicode('security')
78 78 log_dir_name = Unicode('log')
79 79 pid_dir_name = Unicode('pid')
80 80 security_dir = Unicode(u'')
81 81 log_dir = Unicode(u'')
82 82 pid_dir = Unicode(u'')
83 83 location = Unicode(u'')
84 84
85 85 def __init__(self, location=u''):
86 86 super(ClusterDir, self).__init__(location=location)
87 87
88 88 def _location_changed(self, name, old, new):
89 89 if not os.path.isdir(new):
90 90 os.makedirs(new)
91 91 self.security_dir = os.path.join(new, self.security_dir_name)
92 92 self.log_dir = os.path.join(new, self.log_dir_name)
93 93 self.pid_dir = os.path.join(new, self.pid_dir_name)
94 94 self.check_dirs()
95 95
96 96 def _log_dir_changed(self, name, old, new):
97 97 self.check_log_dir()
98 98
99 99 def check_log_dir(self):
100 100 if not os.path.isdir(self.log_dir):
101 101 os.mkdir(self.log_dir)
102 102
103 103 def _security_dir_changed(self, name, old, new):
104 104 self.check_security_dir()
105 105
106 106 def check_security_dir(self):
107 107 if not os.path.isdir(self.security_dir):
108 108 os.mkdir(self.security_dir, 0700)
109 109 os.chmod(self.security_dir, 0700)
110 110
111 111 def _pid_dir_changed(self, name, old, new):
112 112 self.check_pid_dir()
113 113
114 114 def check_pid_dir(self):
115 115 if not os.path.isdir(self.pid_dir):
116 116 os.mkdir(self.pid_dir, 0700)
117 117 os.chmod(self.pid_dir, 0700)
118 118
119 119 def check_dirs(self):
120 120 self.check_security_dir()
121 121 self.check_log_dir()
122 122 self.check_pid_dir()
123 123
124 124 def load_config_file(self, filename):
125 125 """Load a config file from the top level of the cluster dir.
126 126
127 127 Parameters
128 128 ----------
129 129 filename : unicode or str
130 130 The filename only of the config file that must be located in
131 131 the top-level of the cluster directory.
132 132 """
133 133 loader = PyFileConfigLoader(filename, self.location)
134 134 return loader.load_config()
135 135
136 136 def copy_config_file(self, config_file, path=None, overwrite=False):
137 137 """Copy a default config file into the active cluster directory.
138 138
139 139 Default configuration files are kept in :mod:`IPython.config.default`.
140 140 This function moves these from that location to the working cluster
141 141 directory.
142 142 """
143 143 if path is None:
144 144 import IPython.config.default
145 145 path = IPython.config.default.__file__.split(os.path.sep)[:-1]
146 146 path = os.path.sep.join(path)
147 147 src = os.path.join(path, config_file)
148 148 dst = os.path.join(self.location, config_file)
149 149 if not os.path.isfile(dst) or overwrite:
150 150 shutil.copy(src, dst)
151 151
152 152 def copy_all_config_files(self, path=None, overwrite=False):
153 153 """Copy all config files into the active cluster directory."""
154 154 for f in [u'ipcontroller_config.py', u'ipengine_config.py',
155 155 u'ipcluster_config.py']:
156 156 self.copy_config_file(f, path=path, overwrite=overwrite)
157 157
158 158 @classmethod
159 159 def create_cluster_dir(csl, cluster_dir):
160 160 """Create a new cluster directory given a full path.
161 161
162 162 Parameters
163 163 ----------
164 164 cluster_dir : str
165 165 The full path to the cluster directory. If it does exist, it will
166 166 be used. If not, it will be created.
167 167 """
168 168 return ClusterDir(location=cluster_dir)
169 169
170 170 @classmethod
171 171 def create_cluster_dir_by_profile(cls, path, profile=u'default'):
172 172 """Create a cluster dir by profile name and path.
173 173
174 174 Parameters
175 175 ----------
176 176 path : str
177 177 The path (directory) to put the cluster directory in.
178 178 profile : str
179 179 The name of the profile. The name of the cluster directory will
180 180 be "cluster_<profile>".
181 181 """
182 182 if not os.path.isdir(path):
183 183 raise ClusterDirError('Directory not found: %s' % path)
184 184 cluster_dir = os.path.join(path, u'cluster_' + profile)
185 185 return ClusterDir(location=cluster_dir)
186 186
187 187 @classmethod
188 188 def find_cluster_dir_by_profile(cls, ipython_dir, profile=u'default'):
189 189 """Find an existing cluster dir by profile name, return its ClusterDir.
190 190
191 191 This searches through a sequence of paths for a cluster dir. If it
192 192 is not found, a :class:`ClusterDirError` exception will be raised.
193 193
194 194 The search path algorithm is:
195 195 1. ``os.getcwd()``
196 196 2. ``ipython_dir``
197 197 3. The directories found in the ":" separated
198 198 :env:`IPCLUSTER_DIR_PATH` environment variable.
199 199
200 200 Parameters
201 201 ----------
202 202 ipython_dir : unicode or str
203 203 The IPython directory to use.
204 204 profile : unicode or str
205 205 The name of the profile. The name of the cluster directory
206 206 will be "cluster_<profile>".
207 207 """
208 208 dirname = u'cluster_' + profile
209 209 cluster_dir_paths = os.environ.get('IPCLUSTER_DIR_PATH','')
210 210 if cluster_dir_paths:
211 211 cluster_dir_paths = cluster_dir_paths.split(':')
212 212 else:
213 213 cluster_dir_paths = []
214 214 paths = [os.getcwd(), ipython_dir] + cluster_dir_paths
215 215 for p in paths:
216 216 cluster_dir = os.path.join(p, dirname)
217 217 if os.path.isdir(cluster_dir):
218 218 return ClusterDir(location=cluster_dir)
219 219 else:
220 220 raise ClusterDirError('Cluster directory not found in paths: %s' % dirname)
221 221
222 222 @classmethod
223 223 def find_cluster_dir(cls, cluster_dir):
224 224 """Find/create a cluster dir and return its ClusterDir.
225 225
226 226 This will create the cluster directory if it doesn't exist.
227 227
228 228 Parameters
229 229 ----------
230 230 cluster_dir : unicode or str
231 231 The path of the cluster directory. This is expanded using
232 232 :func:`IPython.utils.genutils.expand_path`.
233 233 """
234 234 cluster_dir = expand_path(cluster_dir)
235 235 if not os.path.isdir(cluster_dir):
236 236 raise ClusterDirError('Cluster directory not found: %s' % cluster_dir)
237 237 return ClusterDir(location=cluster_dir)
238 238
239 239
240 240 #-----------------------------------------------------------------------------
241 241 # Command line options
242 242 #-----------------------------------------------------------------------------
243 243
244 244 class ClusterDirConfigLoader(BaseAppConfigLoader):
245 245
246 246 def _add_cluster_profile(self, parser):
247 247 paa = parser.add_argument
248 248 paa('-p', '--profile',
249 249 dest='Global.profile',type=unicode,
250 250 help=
251 251 """The string name of the profile to be used. This determines the name
252 252 of the cluster dir as: cluster_<profile>. The default profile is named
253 253 'default'. The cluster directory is resolve this way if the
254 254 --cluster-dir option is not used.""",
255 255 metavar='Global.profile')
256 256
257 257 def _add_cluster_dir(self, parser):
258 258 paa = parser.add_argument
259 259 paa('--cluster-dir',
260 260 dest='Global.cluster_dir',type=unicode,
261 261 help="""Set the cluster dir. This overrides the logic used by the
262 262 --profile option.""",
263 263 metavar='Global.cluster_dir')
264 264
265 265 def _add_work_dir(self, parser):
266 266 paa = parser.add_argument
267 267 paa('--work-dir',
268 268 dest='Global.work_dir',type=unicode,
269 269 help='Set the working dir for the process.',
270 270 metavar='Global.work_dir')
271 271
272 272 def _add_clean_logs(self, parser):
273 273 paa = parser.add_argument
274 274 paa('--clean-logs',
275 275 dest='Global.clean_logs', action='store_true',
276 276 help='Delete old log flies before starting.')
277 277
278 278 def _add_no_clean_logs(self, parser):
279 279 paa = parser.add_argument
280 280 paa('--no-clean-logs',
281 281 dest='Global.clean_logs', action='store_false',
282 282 help="Don't Delete old log flies before starting.")
283 283
284 284 def _add_arguments(self):
285 285 super(ClusterDirConfigLoader, self)._add_arguments()
286 286 self._add_cluster_profile(self.parser)
287 287 self._add_cluster_dir(self.parser)
288 288 self._add_work_dir(self.parser)
289 289 self._add_clean_logs(self.parser)
290 290 self._add_no_clean_logs(self.parser)
291 291
292 292
293 293 #-----------------------------------------------------------------------------
294 294 # Crash handler for this application
295 295 #-----------------------------------------------------------------------------
296 296
297 297
298 298 _message_template = """\
299 299 Oops, $self.app_name crashed. We do our best to make it stable, but...
300 300
301 301 A crash report was automatically generated with the following information:
302 302 - A verbatim copy of the crash traceback.
303 303 - Data on your current $self.app_name configuration.
304 304
305 305 It was left in the file named:
306 306 \t'$self.crash_report_fname'
307 307 If you can email this file to the developers, the information in it will help
308 308 them in understanding and correcting the problem.
309 309
310 310 You can mail it to: $self.contact_name at $self.contact_email
311 311 with the subject '$self.app_name Crash Report'.
312 312
313 313 If you want to do it now, the following command will work (under Unix):
314 314 mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname
315 315
316 316 To ensure accurate tracking of this issue, please file a report about it at:
317 317 $self.bug_tracker
318 318 """
319 319
320 320 class ClusterDirCrashHandler(CrashHandler):
321 321 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
322 322
323 323 message_template = _message_template
324 324
325 325 def __init__(self, app):
326 326 contact_name = release.authors['Brian'][0]
327 327 contact_email = release.authors['Brian'][1]
328 bug_tracker = 'https://bugs.launchpad.net/ipython/+filebug'
328 bug_tracker = 'http://github.com/ipython/ipython/issues'
329 329 super(ClusterDirCrashHandler,self).__init__(
330 330 app, contact_name, contact_email, bug_tracker
331 331 )
332 332
333 333
334 334 #-----------------------------------------------------------------------------
335 335 # Main application
336 336 #-----------------------------------------------------------------------------
337 337
338 338 class ApplicationWithClusterDir(Application):
339 339 """An application that puts everything into a cluster directory.
340 340
341 341 Instead of looking for things in the ipython_dir, this type of application
342 342 will use its own private directory called the "cluster directory"
343 343 for things like config files, log files, etc.
344 344
345 345 The cluster directory is resolved as follows:
346 346
347 347 * If the ``--cluster-dir`` option is given, it is used.
348 348 * If ``--cluster-dir`` is not given, the application directory is
349 349 resolve using the profile name as ``cluster_<profile>``. The search
350 350 path for this directory is then i) cwd if it is found there
351 351 and ii) in ipython_dir otherwise.
352 352
353 353 The config file for the application is to be put in the cluster
354 354 dir and named the value of the ``config_file_name`` class attribute.
355 355 """
356 356
357 357 command_line_loader = ClusterDirConfigLoader
358 358 crash_handler_class = ClusterDirCrashHandler
359 359 auto_create_cluster_dir = True
360 360
361 361 def create_default_config(self):
362 362 super(ApplicationWithClusterDir, self).create_default_config()
363 363 self.default_config.Global.profile = u'default'
364 364 self.default_config.Global.cluster_dir = u''
365 365 self.default_config.Global.work_dir = os.getcwd()
366 366 self.default_config.Global.log_to_file = False
367 367 self.default_config.Global.clean_logs = False
368 368
369 369 def find_resources(self):
370 370 """This resolves the cluster directory.
371 371
372 372 This tries to find the cluster directory and if successful, it will
373 373 have done:
374 374 * Sets ``self.cluster_dir_obj`` to the :class:`ClusterDir` object for
375 375 the application.
376 376 * Sets ``self.cluster_dir`` attribute of the application and config
377 377 objects.
378 378
379 379 The algorithm used for this is as follows:
380 380 1. Try ``Global.cluster_dir``.
381 381 2. Try using ``Global.profile``.
382 382 3. If both of these fail and ``self.auto_create_cluster_dir`` is
383 383 ``True``, then create the new cluster dir in the IPython directory.
384 384 4. If all fails, then raise :class:`ClusterDirError`.
385 385 """
386 386
387 387 try:
388 388 cluster_dir = self.command_line_config.Global.cluster_dir
389 389 except AttributeError:
390 390 cluster_dir = self.default_config.Global.cluster_dir
391 391 cluster_dir = expand_path(cluster_dir)
392 392 try:
393 393 self.cluster_dir_obj = ClusterDir.find_cluster_dir(cluster_dir)
394 394 except ClusterDirError:
395 395 pass
396 396 else:
397 397 self.log.info('Using existing cluster dir: %s' % \
398 398 self.cluster_dir_obj.location
399 399 )
400 400 self.finish_cluster_dir()
401 401 return
402 402
403 403 try:
404 404 self.profile = self.command_line_config.Global.profile
405 405 except AttributeError:
406 406 self.profile = self.default_config.Global.profile
407 407 try:
408 408 self.cluster_dir_obj = ClusterDir.find_cluster_dir_by_profile(
409 409 self.ipython_dir, self.profile)
410 410 except ClusterDirError:
411 411 pass
412 412 else:
413 413 self.log.info('Using existing cluster dir: %s' % \
414 414 self.cluster_dir_obj.location
415 415 )
416 416 self.finish_cluster_dir()
417 417 return
418 418
419 419 if self.auto_create_cluster_dir:
420 420 self.cluster_dir_obj = ClusterDir.create_cluster_dir_by_profile(
421 421 self.ipython_dir, self.profile
422 422 )
423 423 self.log.info('Creating new cluster dir: %s' % \
424 424 self.cluster_dir_obj.location
425 425 )
426 426 self.finish_cluster_dir()
427 427 else:
428 428 raise ClusterDirError('Could not find a valid cluster directory.')
429 429
430 430 def finish_cluster_dir(self):
431 431 # Set the cluster directory
432 432 self.cluster_dir = self.cluster_dir_obj.location
433 433
434 434 # These have to be set because they could be different from the one
435 435 # that we just computed. Because command line has the highest
436 436 # priority, this will always end up in the master_config.
437 437 self.default_config.Global.cluster_dir = self.cluster_dir
438 438 self.command_line_config.Global.cluster_dir = self.cluster_dir
439 439
440 440 def find_config_file_name(self):
441 441 """Find the config file name for this application."""
442 442 # For this type of Application it should be set as a class attribute.
443 443 if not hasattr(self, 'default_config_file_name'):
444 444 self.log.critical("No config filename found")
445 445 else:
446 446 self.config_file_name = self.default_config_file_name
447 447
448 448 def find_config_file_paths(self):
449 449 # Set the search path to to the cluster directory. We should NOT
450 450 # include IPython.config.default here as the default config files
451 451 # are ALWAYS automatically moved to the cluster directory.
452 452 conf_dir = os.path.join(get_ipython_package_dir(), 'config', 'default')
453 453 self.config_file_paths = (self.cluster_dir,)
454 454
455 455 def pre_construct(self):
456 456 # The log and security dirs were set earlier, but here we put them
457 457 # into the config and log them.
458 458 config = self.master_config
459 459 sdir = self.cluster_dir_obj.security_dir
460 460 self.security_dir = config.Global.security_dir = sdir
461 461 ldir = self.cluster_dir_obj.log_dir
462 462 self.log_dir = config.Global.log_dir = ldir
463 463 pdir = self.cluster_dir_obj.pid_dir
464 464 self.pid_dir = config.Global.pid_dir = pdir
465 465 self.log.info("Cluster directory set to: %s" % self.cluster_dir)
466 466 config.Global.work_dir = unicode(expand_path(config.Global.work_dir))
467 467 # Change to the working directory. We do this just before construct
468 468 # is called so all the components there have the right working dir.
469 469 self.to_work_dir()
470 470
471 471 def to_work_dir(self):
472 472 wd = self.master_config.Global.work_dir
473 473 if unicode(wd) != unicode(os.getcwd()):
474 474 os.chdir(wd)
475 475 self.log.info("Changing to working dir: %s" % wd)
476 476
477 477 def start_logging(self):
478 478 # Remove old log files
479 479 if self.master_config.Global.clean_logs:
480 480 log_dir = self.master_config.Global.log_dir
481 481 for f in os.listdir(log_dir):
482 482 if f.startswith(self.name + u'-') and f.endswith('.log'):
483 483 os.remove(os.path.join(log_dir, f))
484 484 # Start logging to the new log file
485 485 if self.master_config.Global.log_to_file:
486 486 log_filename = self.name + u'-' + str(os.getpid()) + u'.log'
487 487 logfile = os.path.join(self.log_dir, log_filename)
488 488 open_log_file = open(logfile, 'w')
489 489 else:
490 490 open_log_file = sys.stdout
491 491 log.startLogging(open_log_file)
492 492
493 493 def write_pid_file(self, overwrite=False):
494 494 """Create a .pid file in the pid_dir with my pid.
495 495
496 496 This must be called after pre_construct, which sets `self.pid_dir`.
497 497 This raises :exc:`PIDFileError` if the pid file exists already.
498 498 """
499 499 pid_file = os.path.join(self.pid_dir, self.name + u'.pid')
500 500 if os.path.isfile(pid_file):
501 501 pid = self.get_pid_from_file()
502 502 if not overwrite:
503 503 raise PIDFileError(
504 504 'The pid file [%s] already exists. \nThis could mean that this '
505 505 'server is already running with [pid=%s].' % (pid_file, pid)
506 506 )
507 507 with open(pid_file, 'w') as f:
508 508 self.log.info("Creating pid file: %s" % pid_file)
509 509 f.write(repr(os.getpid())+'\n')
510 510
511 511 def remove_pid_file(self):
512 512 """Remove the pid file.
513 513
514 514 This should be called at shutdown by registering a callback with
515 515 :func:`reactor.addSystemEventTrigger`. This needs to return
516 516 ``None``.
517 517 """
518 518 pid_file = os.path.join(self.pid_dir, self.name + u'.pid')
519 519 if os.path.isfile(pid_file):
520 520 try:
521 521 self.log.info("Removing pid file: %s" % pid_file)
522 522 os.remove(pid_file)
523 523 except:
524 524 self.log.warn("Error removing the pid file: %s" % pid_file)
525 525
526 526 def get_pid_from_file(self):
527 527 """Get the pid from the pid file.
528 528
529 529 If the pid file doesn't exist a :exc:`PIDFileError` is raised.
530 530 """
531 531 pid_file = os.path.join(self.pid_dir, self.name + u'.pid')
532 532 if os.path.isfile(pid_file):
533 533 with open(pid_file, 'r') as f:
534 534 pid = int(f.read().strip())
535 535 return pid
536 536 else:
537 537 raise PIDFileError('pid file not found: %s' % pid_file)
538 538
General Comments 0
You need to be logged in to leave comments. Login now