123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- # -*- test-case-name: twisted.application.twist.test.test_options -*-
- # Copyright (c) Twisted Matrix Laboratories.
- # See LICENSE for details.
- """
- Command line options for C{twist}.
- """
- from sys import stdout, stderr
- from textwrap import dedent
- from twisted.copyright import version
- from twisted.python.usage import Options, UsageError
- from twisted.logger import (
- LogLevel, InvalidLogLevelError,
- textFileLogObserver, jsonFileLogObserver,
- )
- from twisted.plugin import getPlugins
- from ..reactors import installReactor, NoSuchReactor, getReactorTypes
- from ..runner._exit import exit, ExitStatus
- from ..service import IServiceMaker
- openFile = open
- class TwistOptions(Options):
- """
- Command line options for C{twist}.
- """
- defaultReactorName = "default"
- defaultLogLevel = LogLevel.info
- def __init__(self):
- Options.__init__(self)
- self["reactorName"] = self.defaultReactorName
- self["logLevel"] = self.defaultLogLevel
- self["logFile"] = stdout
- def getSynopsis(self):
- return "{} plugin [plugin_options]".format(
- Options.getSynopsis(self)
- )
- def opt_version(self):
- """
- Print version and exit.
- """
- exit(ExitStatus.EX_OK, "{}".format(version))
- def opt_reactor(self, name):
- """
- The name of the reactor to use.
- (options: {options})
- """
- # Actually actually actually install the reactor right at this very
- # moment, before any other code (for example, a sub-command plugin)
- # runs and accidentally imports and installs the default reactor.
- try:
- self["reactor"] = self.installReactor(name)
- except NoSuchReactor:
- raise UsageError("Unknown reactor: {}".format(name))
- else:
- self["reactorName"] = name
- opt_reactor.__doc__ = dedent(opt_reactor.__doc__).format(
- options=", ".join(
- '"{}"'.format(rt.shortName) for rt in getReactorTypes()
- ),
- )
- def installReactor(self, name):
- """
- Install the reactor.
- """
- if name == self.defaultReactorName:
- from twisted.internet import reactor
- return reactor
- else:
- return installReactor(name)
- def opt_log_level(self, levelName):
- """
- Set default log level.
- (options: {options}; default: "{default}")
- """
- try:
- self["logLevel"] = LogLevel.levelWithName(levelName)
- except InvalidLogLevelError:
- raise UsageError("Invalid log level: {}".format(levelName))
- opt_log_level.__doc__ = dedent(opt_log_level.__doc__).format(
- options=", ".join(
- '"{}"'.format(l.name) for l in LogLevel.iterconstants()
- ),
- default=defaultLogLevel.name,
- )
- def opt_log_file(self, fileName):
- """
- Log to file. ("-" for stdout, "+" for stderr; default: "-")
- """
- if fileName == "-":
- self["logFile"] = stdout
- return
- if fileName == "+":
- self["logFile"] = stderr
- return
- try:
- self["logFile"] = openFile(fileName, "a")
- except EnvironmentError as e:
- exit(
- ExitStatus.EX_IOERR,
- "Unable to open log file {!r}: {}".format(fileName, e)
- )
- def opt_log_format(self, format):
- """
- Log file format.
- (options: "text", "json"; default: "text" if the log file is a tty,
- otherwise "json")
- """
- format = format.lower()
- if format == "text":
- self["fileLogObserverFactory"] = textFileLogObserver
- elif format == "json":
- self["fileLogObserverFactory"] = jsonFileLogObserver
- else:
- raise UsageError("Invalid log format: {}".format(format))
- self["logFormat"] = format
- opt_log_format.__doc__ = dedent(opt_log_format.__doc__)
- def selectDefaultLogObserver(self):
- """
- Set C{fileLogObserverFactory} to the default appropriate for the
- chosen C{logFile}.
- """
- if "fileLogObserverFactory" not in self:
- logFile = self["logFile"]
- if hasattr(logFile, "isatty") and logFile.isatty():
- self["fileLogObserverFactory"] = textFileLogObserver
- self["logFormat"] = "text"
- else:
- self["fileLogObserverFactory"] = jsonFileLogObserver
- self["logFormat"] = "json"
- def parseOptions(self, options=None):
- self.selectDefaultLogObserver()
- Options.parseOptions(self, options=options)
- if "reactor" not in self:
- self["reactor"] = self.installReactor(self["reactorName"])
- @property
- def plugins(self):
- if "plugins" not in self:
- plugins = {}
- for plugin in getPlugins(IServiceMaker):
- plugins[plugin.tapname] = plugin
- self["plugins"] = plugins
- return self["plugins"]
- @property
- def subCommands(self):
- plugins = self.plugins
- for name in sorted(plugins):
- plugin = plugins[name]
- yield (
- plugin.tapname,
- None,
- # Avoid resolving the options attribute right away, in case
- # it's a property with a non-trivial getter (eg, one which
- # imports modules).
- lambda plugin=plugin: plugin.options(),
- plugin.description,
- )
- def postOptions(self):
- Options.postOptions(self)
- if self.subCommand is None:
- raise UsageError("No plugin specified.")
|