base.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. import os
  2. import sys
  3. from optparse import OptionParser, make_option as Option
  4. import celery
  5. class Command(object):
  6. """Base class for command line applications.
  7. :keyword app: The current app.
  8. :keyword get_app: Callable returning the current app if no app provided.
  9. """
  10. #: Arg list used in help.
  11. args = ''
  12. #: Application version.
  13. version = celery.__version__
  14. #: If false the parser will raise an exception if positional
  15. #: args are provided.
  16. supports_args = True
  17. #: List of options (without preload options).
  18. option_list = ()
  19. #: List of options to parse before parsing other options.
  20. preload_options = (
  21. Option("--app",
  22. default=None, action="store", dest="app",
  23. help="Name of the app instance to use. "),
  24. Option("--loader",
  25. default=None, action="store", dest="loader",
  26. help="Name of the loader class to use. "
  27. "Taken from the environment variable CELERY_LOADER, "
  28. "or 'default' if that is not set."),
  29. Option("--config",
  30. default="celeryconfig", action="store",
  31. dest="config_module",
  32. help="Name of the module to read configuration from.")
  33. )
  34. #: Enable if the application should support config from the cmdline.
  35. enable_config_from_cmdline = False
  36. #: Default configuration namespace.
  37. namespace = "celery"
  38. Parser = OptionParser
  39. def __init__(self, app=None, get_app=None):
  40. self.app = app
  41. self.get_app = get_app or self._get_default_app
  42. def run(self, *args, **options):
  43. """This is the body of the command called by :meth:`handle_argv`."""
  44. raise NotImplementedError("subclass responsibility")
  45. def execute_from_commandline(self, argv=None):
  46. """Execute application from command line.
  47. :keyword argv: The list of command line arguments.
  48. Defaults to ``sys.argv``.
  49. """
  50. if argv is None:
  51. argv = list(sys.argv)
  52. argv = self.setup_app_from_commandline(argv)
  53. prog_name = os.path.basename(argv[0])
  54. return self.handle_argv(prog_name, argv[1:])
  55. def usage(self):
  56. """Returns the command-line usage string for this app."""
  57. return "%%prog [options] %s" % (self.args, )
  58. def get_options(self):
  59. """Get supported command line options."""
  60. return self.option_list
  61. def handle_argv(self, prog_name, argv):
  62. """Parses command line arguments from ``argv`` and dispatches
  63. to :meth:`run`.
  64. :param prog_name: The program name (``argv[0]``).
  65. :param argv: Command arguments.
  66. Exits with an error message if :attr:`supports_args` is disabled
  67. and ``argv`` contains positional arguments.
  68. """
  69. options, args = self.parse_options(prog_name, argv)
  70. if not self.supports_args and args:
  71. sys.stderr.write(
  72. "\nUnrecognized command line arguments: %r\n" % (
  73. ", ".join(args), ))
  74. sys.stderr.write("\nTry --help?\n")
  75. sys.exit(1)
  76. return self.run(*args, **vars(options))
  77. def parse_options(self, prog_name, arguments):
  78. """Parse the available options."""
  79. # Don't want to load configuration to just print the version,
  80. # so we handle --version manually here.
  81. if "--version" in arguments:
  82. print(self.version)
  83. sys.exit(0)
  84. parser = self.create_parser(prog_name)
  85. options, args = parser.parse_args(arguments)
  86. return options, args
  87. def create_parser(self, prog_name):
  88. return self.Parser(prog=prog_name,
  89. usage=self.usage(),
  90. version=self.version,
  91. option_list=(self.preload_options +
  92. self.get_options()))
  93. def setup_app_from_commandline(self, argv):
  94. preload_options = self.parse_preload_options(argv)
  95. app = (preload_options.pop("app", None) or
  96. os.environ.get("CELERY_APP") or
  97. self.app)
  98. loader = (preload_options.pop("loader", None) or
  99. os.environ.get("CELERY_LOADER") or
  100. "default")
  101. config_module = preload_options.pop("config_module", None)
  102. if config_module:
  103. os.environ["CELERY_CONFIG_MODULE"] = config_module
  104. if app:
  105. self.app = self.get_cls_by_name(app)
  106. else:
  107. self.app = self.get_app(loader=loader)
  108. if self.enable_config_from_cmdline:
  109. argv = self.process_cmdline_config(argv)
  110. return argv
  111. def get_cls_by_name(self, name):
  112. from celery.utils import get_cls_by_name, import_from_cwd
  113. return get_cls_by_name(name, imp=import_from_cwd)
  114. def process_cmdline_config(self, argv):
  115. try:
  116. cargs_start = argv.index('--')
  117. except ValueError:
  118. return argv
  119. argv, cargs = argv[:cargs_start], argv[cargs_start + 1:]
  120. self.app.config_from_cmdline(cargs, namespace=self.namespace)
  121. return argv
  122. def parse_preload_options(self, args):
  123. acc = {}
  124. preload_options = dict((opt._long_opts[0], opt.dest)
  125. for opt in self.preload_options)
  126. for arg in args:
  127. if arg.startswith('--') and '=' in arg:
  128. key, value = arg.split('=', 1)
  129. dest = preload_options.get(key)
  130. if dest:
  131. acc[dest] = value
  132. return acc
  133. def _get_default_app(self, *args, **kwargs):
  134. return celery.Celery(*args, **kwargs)
  135. def daemon_options(default_pidfile, default_logfile=None):
  136. return (
  137. Option('-f', '--logfile', default=default_logfile,
  138. action="store", dest="logfile",
  139. help="Path to the logfile"),
  140. Option('--pidfile', default=default_pidfile,
  141. action="store", dest="pidfile",
  142. help="Path to the pidfile."),
  143. Option('--uid', default=None,
  144. action="store", dest="uid",
  145. help="Effective user id to run as when detached."),
  146. Option('--gid', default=None,
  147. action="store", dest="gid",
  148. help="Effective group id to run as when detached."),
  149. Option('--umask', default=0,
  150. action="store", type="int", dest="umask",
  151. help="Umask of the process when detached."),
  152. Option('--workdir', default=None,
  153. action="store", dest="working_directory",
  154. help="Directory to change to when detached."),
  155. )