_twist.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. # -*- test-case-name: twisted.application.twist.test.test_twist -*-
  2. # Copyright (c) Twisted Matrix Laboratories.
  3. # See LICENSE for details.
  4. """
  5. Run a Twisted application.
  6. """
  7. import sys
  8. from typing import Sequence
  9. from twisted.application.app import _exitWithSignal
  10. from twisted.internet.interfaces import IReactorCore, _ISupportsExitSignalCapturing
  11. from twisted.python.usage import Options, UsageError
  12. from ..runner._exit import ExitStatus, exit
  13. from ..runner._runner import Runner
  14. from ..service import Application, IService, IServiceMaker
  15. from ._options import TwistOptions
  16. class Twist:
  17. """
  18. Run a Twisted application.
  19. """
  20. @staticmethod
  21. def options(argv: Sequence[str]) -> TwistOptions:
  22. """
  23. Parse command line options.
  24. @param argv: Command line arguments.
  25. @return: The parsed options.
  26. """
  27. options = TwistOptions()
  28. try:
  29. options.parseOptions(argv[1:])
  30. except UsageError as e:
  31. exit(ExitStatus.EX_USAGE, f"Error: {e}\n\n{options}")
  32. return options
  33. @staticmethod
  34. def service(plugin: IServiceMaker, options: Options) -> IService:
  35. """
  36. Create the application service.
  37. @param plugin: The name of the plugin that implements the service
  38. application to run.
  39. @param options: Options to pass to the application.
  40. @return: The created application service.
  41. """
  42. service = plugin.makeService(options)
  43. application = Application(plugin.tapname)
  44. service.setServiceParent(application)
  45. return IService(application)
  46. @staticmethod
  47. def startService(reactor: IReactorCore, service: IService) -> None:
  48. """
  49. Start the application service.
  50. @param reactor: The reactor to run the service with.
  51. @param service: The application service to run.
  52. """
  53. service.startService()
  54. # Ask the reactor to stop the service before shutting down
  55. reactor.addSystemEventTrigger("before", "shutdown", service.stopService)
  56. @staticmethod
  57. def run(twistOptions: TwistOptions) -> None:
  58. """
  59. Run the application service.
  60. @param twistOptions: Command line options to convert to runner
  61. arguments.
  62. """
  63. runner = Runner(
  64. reactor=twistOptions["reactor"],
  65. defaultLogLevel=twistOptions["logLevel"],
  66. logFile=twistOptions["logFile"],
  67. fileLogObserverFactory=twistOptions["fileLogObserverFactory"],
  68. )
  69. runner.run()
  70. reactor = twistOptions["reactor"]
  71. if _ISupportsExitSignalCapturing.providedBy(reactor):
  72. if reactor._exitSignal is not None:
  73. _exitWithSignal(reactor._exitSignal)
  74. @classmethod
  75. def main(cls, argv: Sequence[str] = sys.argv) -> None:
  76. """
  77. Executable entry point for L{Twist}.
  78. Processes options and run a twisted reactor with a service.
  79. @param argv: Command line arguments.
  80. @type argv: L{list}
  81. """
  82. options = cls.options(argv)
  83. reactor = options["reactor"]
  84. # If subCommand is None, TwistOptions.parseOptions() raises UsageError
  85. # and Twist.options() will exit the runner, so we'll never get here.
  86. subCommand = options.subCommand
  87. assert subCommand is not None
  88. service = cls.service(
  89. plugin=options.plugins[subCommand],
  90. options=options.subOptions,
  91. )
  92. cls.startService(reactor, service)
  93. cls.run(options)