PageRenderTime 28ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 0ms

/celery/bin/base.py

https://github.com/offbyone/celery
Python | 324 lines | 298 code | 5 blank | 21 comment | 13 complexity | b71c427ef10aae9d90b542eca576ec5a MD5 | raw file
  1. # -*- coding: utf-8 -*-
  2. """
  3. .. _preload-options:
  4. Preload Options
  5. ---------------
  6. .. cmdoption:: --app
  7. Fully qualified name of the app instance to use.
  8. .. cmdoption:: -b, --broker
  9. Broker URL. Default is 'amqp://guest:guest@localhost:5672//'
  10. .. cmdoption:: --loader
  11. Name of the loader class to use.
  12. Taken from the environment variable :envvar:`CELERY_LOADER`
  13. or 'default' if that is not set.
  14. .. cmdoption:: --config
  15. Name of the module to read configuration from,
  16. default is 'celeryconfig'.
  17. .. _daemon-options:
  18. Daemon Options
  19. --------------
  20. .. cmdoption:: -f, --logfile
  21. Path to log file. If no logfile is specified, `stderr` is used.
  22. .. cmdoption:: --pidfile
  23. Optional file used to store the process pid.
  24. The program will not start if this file already exists
  25. and the pid is still alive.
  26. .. cmdoption:: --uid
  27. User id, or user name of the user to run as after detaching.
  28. .. cmdoption:: --gid
  29. Group id, or group name of the main group to change to after
  30. detaching.
  31. .. cmdoption:: --umask
  32. Effective umask of the process after detaching. Default is 0.
  33. .. cmdoption:: --workdir
  34. Optional directory to change to after detaching.
  35. """
  36. from __future__ import absolute_import
  37. import os
  38. import re
  39. import sys
  40. import warnings
  41. from collections import defaultdict
  42. from optparse import OptionParser, make_option as Option
  43. from types import ModuleType
  44. import celery
  45. from celery.exceptions import CDeprecationWarning, CPendingDeprecationWarning
  46. from celery.platforms import EX_FAILURE, EX_USAGE
  47. from celery.utils.imports import symbol_by_name, import_from_cwd
  48. # always enable DeprecationWarnings, so our users can see them.
  49. for warning in (CDeprecationWarning, CPendingDeprecationWarning):
  50. warnings.simplefilter("once", warning, 0)
  51. ARGV_DISABLED = """
  52. Unrecognized command line arguments: %s
  53. Try --help?
  54. """
  55. find_long_opt = re.compile(r'.+?(--.+?)(?:\s|,|$)')
  56. find_rst_ref = re.compile(r':\w+:`(.+?)`')
  57. class Command(object):
  58. """Base class for command line applications.
  59. :keyword app: The current app.
  60. :keyword get_app: Callable returning the current app if no app provided.
  61. """
  62. #: Arg list used in help.
  63. args = ''
  64. #: Application version.
  65. version = celery.__version__
  66. #: If false the parser will raise an exception if positional
  67. #: args are provided.
  68. supports_args = True
  69. #: List of options (without preload options).
  70. option_list = ()
  71. # module Rst documentation to parse help from (if any)
  72. doc = None
  73. #: List of options to parse before parsing other options.
  74. preload_options = (
  75. Option("--app", default=None),
  76. Option("-b", "--broker", default=None),
  77. Option("--loader", default=None),
  78. Option("--config", default="celeryconfig", dest="config_module"),
  79. )
  80. #: Enable if the application should support config from the cmdline.
  81. enable_config_from_cmdline = False
  82. #: Default configuration namespace.
  83. namespace = "celery"
  84. Parser = OptionParser
  85. def __init__(self, app=None, get_app=None):
  86. self.app = app
  87. self.get_app = get_app or self._get_default_app
  88. def run(self, *args, **options):
  89. """This is the body of the command called by :meth:`handle_argv`."""
  90. raise NotImplementedError("subclass responsibility")
  91. def execute_from_commandline(self, argv=None):
  92. """Execute application from command line.
  93. :keyword argv: The list of command line arguments.
  94. Defaults to ``sys.argv``.
  95. """
  96. if argv is None:
  97. argv = list(sys.argv)
  98. argv = self.setup_app_from_commandline(argv)
  99. prog_name = os.path.basename(argv[0])
  100. return self.handle_argv(prog_name, argv[1:])
  101. def usage(self, command):
  102. """Returns the command-line usage string for this app."""
  103. return "%%prog [options] %s" % (self.args, )
  104. def get_options(self):
  105. """Get supported command line options."""
  106. return self.option_list
  107. def expanduser(self, value):
  108. if isinstance(value, basestring):
  109. return os.path.expanduser(value)
  110. return value
  111. def handle_argv(self, prog_name, argv):
  112. """Parses command line arguments from ``argv`` and dispatches
  113. to :meth:`run`.
  114. :param prog_name: The program name (``argv[0]``).
  115. :param argv: Command arguments.
  116. Exits with an error message if :attr:`supports_args` is disabled
  117. and ``argv`` contains positional arguments.
  118. """
  119. options, args = self.prepare_args(*self.parse_options(prog_name, argv))
  120. return self.run(*args, **options)
  121. def prepare_args(self, options, args):
  122. if options:
  123. options = dict((k, self.expanduser(v))
  124. for k, v in vars(options).iteritems()
  125. if not k.startswith('_'))
  126. args = map(self.expanduser, args)
  127. self.check_args(args)
  128. return options, args
  129. def check_args(self, args):
  130. if not self.supports_args and args:
  131. self.die(ARGV_DISABLED % (', '.join(args, )), EX_USAGE)
  132. def die(self, msg, status=EX_FAILURE):
  133. sys.stderr.write(msg + "\n")
  134. sys.exit(status)
  135. def parse_options(self, prog_name, arguments):
  136. """Parse the available options."""
  137. # Don't want to load configuration to just print the version,
  138. # so we handle --version manually here.
  139. if "--version" in arguments:
  140. sys.stdout.write("%s\n" % self.version)
  141. sys.exit(0)
  142. parser = self.create_parser(prog_name)
  143. return parser.parse_args(arguments)
  144. def create_parser(self, prog_name, command=None):
  145. return self.prepare_parser(self.Parser(prog=prog_name,
  146. usage=self.usage(command),
  147. version=self.version,
  148. option_list=(self.preload_options +
  149. self.get_options())))
  150. def prepare_parser(self, parser):
  151. docs = [self.parse_doc(doc) for doc in (self.doc, __doc__) if doc]
  152. for doc in docs:
  153. for long_opt, help in doc.iteritems():
  154. option = parser.get_option(long_opt)
  155. if option is not None:
  156. option.help = ' '.join(help) % {"default": option.default}
  157. return parser
  158. def prepare_preload_options(self, options):
  159. """Optional handler to do additional processing of preload options.
  160. Configuration must not have been initialized
  161. until after this is called.
  162. """
  163. pass
  164. def setup_app_from_commandline(self, argv):
  165. preload_options = self.parse_preload_options(argv)
  166. self.prepare_preload_options(preload_options)
  167. app = (preload_options.get("app") or
  168. os.environ.get("CELERY_APP") or
  169. self.app)
  170. loader = (preload_options.get("loader") or
  171. os.environ.get("CELERY_LOADER") or
  172. "default")
  173. broker = preload_options.get("broker", None)
  174. if broker:
  175. os.environ["CELERY_BROKER_URL"] = broker
  176. config_module = preload_options.get("config_module")
  177. if config_module:
  178. os.environ["CELERY_CONFIG_MODULE"] = config_module
  179. if app:
  180. self.app = self.find_app(app)
  181. else:
  182. self.app = self.get_app(loader=loader)
  183. if self.enable_config_from_cmdline:
  184. argv = self.process_cmdline_config(argv)
  185. return argv
  186. def find_app(self, app):
  187. sym = self.symbol_by_name(app)
  188. if isinstance(sym, ModuleType):
  189. if getattr(sym, "__path__", None):
  190. return self.find_app("%s.celery:" % (app.replace(":", ""), ))
  191. return sym.celery
  192. return sym
  193. def symbol_by_name(self, name):
  194. return symbol_by_name(name, imp=import_from_cwd)
  195. get_cls_by_name = symbol_by_name # XXX compat
  196. def process_cmdline_config(self, argv):
  197. try:
  198. cargs_start = argv.index('--')
  199. except ValueError:
  200. return argv
  201. argv, cargs = argv[:cargs_start], argv[cargs_start + 1:]
  202. self.app.config_from_cmdline(cargs, namespace=self.namespace)
  203. return argv
  204. def parse_preload_options(self, args):
  205. acc = {}
  206. opts = {}
  207. for opt in self.preload_options:
  208. for t in (opt._long_opts, opt._short_opts):
  209. opts.update(dict(zip(t, [opt.dest] * len(t))))
  210. index = 0
  211. length = len(args)
  212. while index < length:
  213. arg = args[index]
  214. if arg.startswith('--') and '=' in arg:
  215. key, value = arg.split('=', 1)
  216. dest = opts.get(key)
  217. if dest:
  218. acc[dest] = value
  219. elif arg.startswith('-'):
  220. dest = opts.get(arg)
  221. if dest:
  222. acc[dest] = args[index + 1]
  223. index += 1
  224. index += 1
  225. return acc
  226. def parse_doc(self, doc):
  227. options, in_option = defaultdict(list), None
  228. for line in doc.splitlines():
  229. if line.startswith(".. cmdoption::"):
  230. m = find_long_opt.match(line)
  231. if m:
  232. in_option = m.groups()[0].strip()
  233. assert in_option, "missing long opt"
  234. elif in_option and line.startswith(' ' * 4):
  235. options[in_option].append(find_rst_ref.sub(r'\1',
  236. line.strip()).replace('`', ''))
  237. return options
  238. def _get_default_app(self, *args, **kwargs):
  239. from celery.app import default_app
  240. return default_app._get_current_object() # omit proxy
  241. def daemon_options(default_pidfile=None, default_logfile=None):
  242. return (
  243. Option("-f", "--logfile", default=default_logfile),
  244. Option("--pidfile", default=default_pidfile),
  245. Option("--uid", default=None),
  246. Option("--gid", default=None),
  247. Option("--umask", default=0, type="int"),
  248. Option("--workdir", default=None, dest="working_directory"),
  249. )