Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(172)

Side by Side Diff: third_party/twisted_8_1/twisted/application/app.py

Issue 12261012: Remove third_party/twisted_8_1 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 # -*- test-case-name: twisted.test.test_application,twisted.test.test_twistd -*-
2 # Copyright (c) 2001-2008 Twisted Matrix Laboratories.
3 # See LICENSE for details.
4
5 import sys, os, pdb, getpass, traceback, signal, warnings
6
7 from twisted.python import runtime, log, usage, failure, util, logfile
8 from twisted.persisted import sob
9 from twisted.application import service, reactors
10 from twisted.internet import defer
11 from twisted import copyright
12
13 # Expose the new implementation of installReactor at the old location.
14 from twisted.application.reactors import installReactor
15 from twisted.application.reactors import NoSuchReactor
16
17
18
19 class _BasicProfiler(object):
20 """
21 @ivar saveStats: if C{True}, save the stats information instead of the
22 human readable format
23 @type saveStats: C{bool}
24
25 @ivar profileOutput: the name of the file use to print profile data.
26 @type profileOutput: C{str}
27 """
28
29 def __init__(self, profileOutput, saveStats):
30 self.profileOutput = profileOutput
31 self.saveStats = saveStats
32
33
34 def _reportImportError(self, module, e):
35 """
36 Helper method to report an import error with a profile module. This
37 has to be explicit because some of these modules are removed by
38 distributions due to them being non-free.
39 """
40 s = "Failed to import module %s: %s" % (module, e)
41 s += """
42 This is most likely caused by your operating system not including
43 the module due to it being non-free. Either do not use the option
44 --profile, or install the module; your operating system vendor
45 may provide it in a separate package.
46 """
47 raise SystemExit(s)
48
49
50
51 class ProfileRunner(_BasicProfiler):
52 """
53 Runner for the standard profile module.
54 """
55
56 def run(self, reactor):
57 """
58 Run reactor under the standard profiler.
59 """
60 try:
61 import profile
62 except ImportError, e:
63 self._reportImportError("profile", e)
64
65 p = profile.Profile()
66 p.runcall(reactor.run)
67 if self.saveStats:
68 p.dump_stats(self.profileOutput)
69 else:
70 tmp, sys.stdout = sys.stdout, open(self.profileOutput, 'a')
71 try:
72 p.print_stats()
73 finally:
74 sys.stdout, tmp = tmp, sys.stdout
75 tmp.close()
76
77
78
79 class HotshotRunner(_BasicProfiler):
80 """
81 Runner for the hotshot profile module.
82 """
83
84 def run(self, reactor):
85 """
86 Run reactor under the hotshot profiler.
87 """
88 try:
89 import hotshot.stats
90 except (ImportError, SystemExit), e:
91 # Certain versions of Debian (and Debian derivatives) raise
92 # SystemExit when importing hotshot if the "non-free" profiler
93 # module is not installed. Someone eventually recognized this
94 # as a bug and changed the Debian packaged Python to raise
95 # ImportError instead. Handle both exception types here in
96 # order to support the versions of Debian which have this
97 # behavior. The bug report which prompted the introduction of
98 # this highly undesirable behavior should be available online at
99 # <http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=334067>.
100 # There seems to be no corresponding bug report which resulted
101 # in the behavior being removed. -exarkun
102 self._reportImportError("hotshot", e)
103
104 # this writes stats straight out
105 p = hotshot.Profile(self.profileOutput)
106 p.runcall(reactor.run)
107 if self.saveStats:
108 # stats are automatically written to file, nothing to do
109 return
110 else:
111 s = hotshot.stats.load(self.profileOutput)
112 s.strip_dirs()
113 s.sort_stats(-1)
114 if getattr(s, 'stream', None) is not None:
115 # Python 2.5 and above supports a stream attribute
116 s.stream = open(self.profileOutput, 'w')
117 s.print_stats()
118 s.stream.close()
119 else:
120 # But we have to use a trick for Python < 2.5
121 tmp, sys.stdout = sys.stdout, open(self.profileOutput, 'w')
122 try:
123 s.print_stats()
124 finally:
125 sys.stdout, tmp = tmp, sys.stdout
126 tmp.close()
127
128
129
130 class CProfileRunner(_BasicProfiler):
131 """
132 Runner for the cProfile module.
133 """
134
135 def run(self, reactor):
136 """
137 Run reactor under the cProfile profiler.
138 """
139 try:
140 import cProfile, pstats
141 except ImportError, e:
142 self._reportImportError("cProfile", e)
143
144 p = cProfile.Profile()
145 p.runcall(reactor.run)
146 if self.saveStats:
147 p.dump_stats(self.profileOutput)
148 else:
149 stream = open(self.profileOutput, 'w')
150 s = pstats.Stats(p, stream=stream)
151 s.strip_dirs()
152 s.sort_stats(-1)
153 s.print_stats()
154 stream.close()
155
156
157
158 class AppProfiler(object):
159 """
160 Class which selects a specific profile runner based on configuration
161 options.
162
163 @ivar profiler: the name of the selected profiler.
164 @type profiler: C{str}
165 """
166 profilers = {"profile": ProfileRunner, "hotshot": HotshotRunner,
167 "cProfile": CProfileRunner}
168
169 def __init__(self, options):
170 saveStats = options.get("savestats", False)
171 profileOutput = options.get("profile", None)
172 self.profiler = options.get("profiler", None)
173 if options.get("nothotshot", False):
174 warnings.warn("The --nothotshot option is deprecated. Please "
175 "specify the profiler name using the --profiler "
176 "option", category=DeprecationWarning)
177 self.profiler = "profile"
178 if self.profiler in self.profilers:
179 profiler = self.profilers[self.profiler](profileOutput, saveStats)
180 self.run = profiler.run
181 else:
182 raise SystemExit("Unsupported profiler name: %s" % (self.profiler,))
183
184
185
186 def runWithProfiler(reactor, config):
187 """
188 DEPRECATED in Twisted 8.0.
189
190 Run reactor under standard profiler.
191 """
192 warnings.warn("runWithProfiler is deprecated since Twisted 8.0. "
193 "Use ProfileRunner instead.", DeprecationWarning, 2)
194 item = AppProfiler(config)
195 return item.run(reactor)
196
197
198
199 def runWithHotshot(reactor, config):
200 """
201 DEPRECATED in Twisted 8.0.
202
203 Run reactor under hotshot profiler.
204 """
205 warnings.warn("runWithHotshot is deprecated since Twisted 8.0. "
206 "Use HotshotRunner instead.", DeprecationWarning, 2)
207 item = AppProfiler(config)
208 return item.run(reactor)
209
210
211
212 def fixPdb():
213 def do_stop(self, arg):
214 self.clear_all_breaks()
215 self.set_continue()
216 from twisted.internet import reactor
217 reactor.callLater(0, reactor.stop)
218 return 1
219
220 def help_stop(self):
221 print """stop - Continue execution, then cleanly shutdown the twisted re actor."""
222
223 def set_quit(self):
224 os._exit(0)
225
226 pdb.Pdb.set_quit = set_quit
227 pdb.Pdb.do_stop = do_stop
228 pdb.Pdb.help_stop = help_stop
229
230
231
232 def runReactorWithLogging(config, oldstdout, oldstderr, profiler=None, reactor=N one):
233 """
234 Start the reactor, using profiling if specified by the configuration, and
235 log any error happening in the process.
236
237 @param config: configuration of the twistd application.
238 @type config: L{ServerOptions}
239
240 @param oldstdout: initial value of C{sys.stdout}.
241 @type oldstdout: C{file}
242
243 @param oldstderr: initial value of C{sys.stderr}.
244 @type oldstderr: C{file}
245
246 @param profiler: object used to run the reactor with profiling.
247 @type profiler: L{AppProfiler}
248
249 @param reactor: The reactor to use. If C{None}, the global reactor will
250 be used.
251 """
252 if reactor is None:
253 from twisted.internet import reactor
254 try:
255 if config['profile']:
256 if profiler is not None:
257 profiler.run(reactor)
258 else:
259 # Backward compatible code
260 if not config['nothotshot']:
261 runWithHotshot(reactor, config)
262 else:
263 runWithProfiler(reactor, config)
264 elif config['debug']:
265 sys.stdout = oldstdout
266 sys.stderr = oldstderr
267 if runtime.platformType == 'posix':
268 signal.signal(signal.SIGUSR2, lambda *args: pdb.set_trace())
269 signal.signal(signal.SIGINT, lambda *args: pdb.set_trace())
270 fixPdb()
271 pdb.runcall(reactor.run)
272 else:
273 reactor.run()
274 except:
275 if config['nodaemon']:
276 file = oldstdout
277 else:
278 file = open("TWISTD-CRASH.log",'a')
279 traceback.print_exc(file=file)
280 file.flush()
281
282
283
284 def getPassphrase(needed):
285 if needed:
286 return getpass.getpass('Passphrase: ')
287 else:
288 return None
289
290
291
292 def getSavePassphrase(needed):
293 if needed:
294 passphrase = util.getPassword("Encryption passphrase: ")
295 else:
296 return None
297
298
299
300 class ApplicationRunner(object):
301 """
302 An object which helps running an application based on a config object.
303
304 Subclass me and implement preApplication and postApplication
305 methods. postApplication generally will want to run the reactor
306 after starting the application.
307
308 @ivar config: The config object, which provides a dict-like interface.
309
310 @ivar application: Available in postApplication, but not
311 preApplication. This is the application object.
312
313 @ivar profilerFactory: Factory for creating a profiler object, able to
314 profile the application if options are set accordingly.
315
316 @ivar profiler: Instance provided by C{profilerFactory}.
317 """
318 profilerFactory = AppProfiler
319
320 def __init__(self, config):
321 self.config = config
322 self.profiler = self.profilerFactory(config)
323
324
325 def run(self):
326 """
327 Run the application.
328 """
329 self.preApplication()
330 self.application = self.createOrGetApplication()
331
332 # Later, try adapting self.application to ILogObserverFactory or
333 # whatever and getting an observer from it, instead. Fall back to
334 # self.getLogObserver if the adaption fails.
335 self.startLogging(self.getLogObserver())
336
337 self.postApplication()
338
339
340 def startReactor(self, reactor, oldstdout, oldstderr):
341 """
342 Run the reactor with the given configuration. Subclasses should
343 probably call this from C{postApplication}.
344
345 @see: L{runReactorWithLogging}
346 """
347 runReactorWithLogging(
348 self.config, oldstdout, oldstderr, self.profiler, reactor)
349
350
351 def preApplication(self):
352 """
353 Override in subclass.
354
355 This should set up any state necessary before loading and
356 running the Application.
357 """
358 raise NotImplementedError()
359
360
361 def startLogging(self, observer):
362 """
363 Initialize the logging system.
364
365 @param observer: The observer to add to the logging system.
366 """
367 log.startLoggingWithObserver(observer)
368 sys.stdout.flush()
369 initialLog()
370
371
372 def getLogObserver(self):
373 """
374 Create a log observer to be added to the logging system before running
375 this application.
376 """
377 raise NotImplementedError()
378
379
380 def postApplication(self):
381 """
382 Override in subclass.
383
384 This will be called after the application has been loaded (so
385 the C{application} attribute will be set). Generally this
386 should start the application and run the reactor.
387 """
388 raise NotImplementedError
389
390
391 def createOrGetApplication(self):
392 """
393 Create or load an Application based on the parameters found in the
394 given L{ServerOptions} instance.
395
396 If a subcommand was used, the L{service.IServiceMaker} that it
397 represents will be used to construct a service to be added to
398 a newly-created Application.
399
400 Otherwise, an application will be loaded based on parameters in
401 the config.
402 """
403 if self.config.subCommand:
404 # If a subcommand was given, it's our responsibility to create
405 # the application, instead of load it from a file.
406
407 # loadedPlugins is set up by the ServerOptions.subCommands
408 # property, which is iterated somewhere in the bowels of
409 # usage.Options.
410 plg = self.config.loadedPlugins[self.config.subCommand]
411 ser = plg.makeService(self.config.subOptions)
412 application = service.Application(plg.tapname)
413 ser.setServiceParent(application)
414 else:
415 passphrase = getPassphrase(self.config['encrypted'])
416 application = getApplication(self.config, passphrase)
417 return application
418
419
420
421 def getApplication(config, passphrase):
422 s = [(config[t], t)
423 for t in ['python', 'xml', 'source', 'file'] if config[t]][0]
424 filename, style = s[0], {'file':'pickle'}.get(s[1],s[1])
425 try:
426 log.msg("Loading %s..." % filename)
427 application = service.loadApplication(filename, style, passphrase)
428 log.msg("Loaded.")
429 except Exception, e:
430 s = "Failed to load application: %s" % e
431 if isinstance(e, KeyError) and e.args[0] == "application":
432 s += """
433 Could not find 'application' in the file. To use 'twistd -y', your .tac
434 file must create a suitable object (e.g., by calling service.Application())
435 and store it in a variable named 'application'. twistd loads your .tac file
436 and scans the global variables for one of this name.
437
438 Please read the 'Using Application' HOWTO for details.
439 """
440 traceback.print_exc(file=log.logfile)
441 log.msg(s)
442 log.deferr()
443 sys.exit('\n' + s + '\n')
444 return application
445
446
447
448 def reportProfile(report_profile, name):
449 """
450 DEPRECATED since Twisted 8.0. This does nothing.
451 """
452 warnings.warn("reportProfile is deprecated and a no-op since Twisted 8.0.",
453 category=DeprecationWarning)
454
455
456
457 def _reactorZshAction():
458 return "(%s)" % " ".join([r.shortName for r in reactors.getReactorTypes()])
459
460 class ReactorSelectionMixin:
461 """
462 Provides options for selecting a reactor to install.
463 """
464 zsh_actions = {"reactor" : _reactorZshAction}
465 messageOutput = sys.stdout
466
467
468 def opt_help_reactors(self):
469 """
470 Display a list of possibly available reactor names.
471 """
472 for r in reactors.getReactorTypes():
473 self.messageOutput.write(' %-4s\t%s\n' %
474 (r.shortName, r.description))
475 raise SystemExit(0)
476
477
478 def opt_reactor(self, shortName):
479 """
480 Which reactor to use (see --help-reactors for a list of possibilities)
481 """
482 # Actually actually actually install the reactor right at this very
483 # moment, before any other code (for example, a sub-command plugin)
484 # runs and accidentally imports and installs the default reactor.
485 #
486 # This could probably be improved somehow.
487 try:
488 installReactor(shortName)
489 except NoSuchReactor:
490 msg = ("The specified reactor does not exist: '%s'.\n"
491 "See the list of available reactors with "
492 "--help-reactors" % (shortName,))
493 raise usage.UsageError(msg)
494 except Exception, e:
495 msg = ("The specified reactor cannot be used, failed with error: "
496 "%s.\nSee the list of available reactors with "
497 "--help-reactors" % (e,))
498 raise usage.UsageError(msg)
499 opt_r = opt_reactor
500
501
502
503
504 class ServerOptions(usage.Options, ReactorSelectionMixin):
505
506 optFlags = [['savestats', None,
507 "save the Stats object rather than the text output of "
508 "the profiler."],
509 ['no_save','o', "do not save state on shutdown"],
510 ['encrypted', 'e',
511 "The specified tap/aos/xml file is encrypted."],
512 ['nothotshot', None,
513 "DEPRECATED. Don't use the 'hotshot' profiler even if "
514 "it's available."]]
515
516 optParameters = [['logfile','l', None,
517 "log to a specified file, - for stdout"],
518 ['profile', 'p', None,
519 "Run in profile mode, dumping results to specified file"],
520 ['profiler', None, "hotshot",
521 "Name of the profiler to use, 'hotshot' or 'profile'."],
522 ['file','f','twistd.tap',
523 "read the given .tap file"],
524 ['python','y', None,
525 "read an application from within a Python file (implies -o )"],
526 ['xml', 'x', None,
527 "Read an application from a .tax file "
528 "(Marmalade format)."],
529 ['source', 's', None,
530 "Read an application from a .tas file (AOT format)."],
531 ['rundir','d','.',
532 'Change to a supplied directory before running'],
533 ['report-profile', None, None,
534 'E-mail address to use when reporting dynamic execution '
535 'profiler stats. This should not be combined with '
536 'other profiling options. This will only take effect '
537 'if the application to be run has an application '
538 'name.']]
539
540 #zsh_altArgDescr = {"foo":"use this description for foo instead"}
541 #zsh_multiUse = ["foo", "bar"]
542 zsh_mutuallyExclusive = [("file", "python", "xml", "source")]
543 zsh_actions = {"file":'_files -g "*.tap"',
544 "python":'_files -g "*.(tac|py)"',
545 "xml":'_files -g "*.tax"',
546 "source":'_files -g "*.tas"',
547 "rundir":"_dirs"}
548 #zsh_actionDescr = {"logfile":"log file name", "random":"random seed"}
549
550 def __init__(self, *a, **kw):
551 self['debug'] = False
552 usage.Options.__init__(self, *a, **kw)
553
554 def opt_debug(self):
555 """
556 run the application in the Python Debugger (implies nodaemon),
557 sending SIGUSR2 will drop into debugger
558 """
559 defer.setDebugging(True)
560 failure.startDebugMode()
561 self['debug'] = True
562 opt_b = opt_debug
563
564
565 def opt_spew(self):
566 """Print an insanely verbose log of everything that happens.
567 Useful when debugging freezes or locks in complex code."""
568 sys.settrace(util.spewer)
569 try:
570 import threading
571 except ImportError:
572 return
573 threading.settrace(util.spewer)
574
575
576 def opt_report_profile(self, value):
577 """
578 DEPRECATED.
579
580 Manage --report-profile option, which does nothing currently.
581 """
582 warnings.warn("--report-profile option is deprecated and a no-op "
583 "since Twisted 8.0.", category=DeprecationWarning)
584
585
586 def parseOptions(self, options=None):
587 if options is None:
588 options = sys.argv[1:] or ["--help"]
589 usage.Options.parseOptions(self, options)
590
591 def postOptions(self):
592 if self.subCommand or self['python']:
593 self['no_save'] = True
594
595 def subCommands(self):
596 from twisted import plugin
597 plugins = plugin.getPlugins(service.IServiceMaker)
598 self.loadedPlugins = {}
599 for plug in plugins:
600 self.loadedPlugins[plug.tapname] = plug
601 yield (plug.tapname, None, lambda: plug.options(), plug.description)
602 subCommands = property(subCommands)
603
604
605
606 def run(runApp, ServerOptions):
607 config = ServerOptions()
608 try:
609 config.parseOptions()
610 except usage.error, ue:
611 print config
612 print "%s: %s" % (sys.argv[0], ue)
613 else:
614 runApp(config)
615
616
617 def initialLog():
618 from twisted.internet import reactor
619 log.msg("twistd %s (%s %s) starting up" % (copyright.version,
620 sys.executable,
621 runtime.shortPythonVersion()))
622 log.msg('reactor class: %s' % reactor.__class__)
623
624
625 def convertStyle(filein, typein, passphrase, fileout, typeout, encrypt):
626 application = service.loadApplication(filein, typein, passphrase)
627 sob.IPersistable(application).setStyle(typeout)
628 passphrase = getSavePassphrase(encrypt)
629 if passphrase:
630 fileout = None
631 sob.IPersistable(application).save(filename=fileout, passphrase=passphrase)
632
633 def startApplication(application, save):
634 from twisted.internet import reactor
635 service.IService(application).startService()
636 if save:
637 p = sob.IPersistable(application)
638 reactor.addSystemEventTrigger('after', 'shutdown', p.save, 'shutdown')
639 reactor.addSystemEventTrigger('before', 'shutdown',
640 service.IService(application).stopService)
641
642 def getLogFile(logfilename):
643 """
644 Build a log file from the full path.
645 """
646 import warnings
647 warnings.warn(
648 "app.getLogFile is deprecated. Use "
649 "twisted.python.logfile.LogFile.fromFullPath instead",
650 DeprecationWarning, stacklevel=2)
651
652 return logfile.LogFile.fromFullPath(logfilename)
653
OLDNEW
« no previous file with comments | « third_party/twisted_8_1/twisted/application/__init__.py ('k') | third_party/twisted_8_1/twisted/application/internet.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698