Browse Source

Simplifying the tutorail even more

Ask Solem 13 years ago
parent
commit
faf517576c

+ 6 - 0
celery/app/__init__.py

@@ -110,6 +110,12 @@ class App(base.BaseApp):
         return instantiate("celery.task.sets:TaskSet",
         return instantiate("celery.task.sets:TaskSet",
                            app=self, *args, **kwargs)
                            app=self, *args, **kwargs)
 
 
+    def celery_main(self, argv=None):
+        """Run :program:`celery` using `argv`.  Uses :data:`sys.argv`
+        if `argv` is not specified."""
+        return instantiate("celery.bin.celery:CeleryCommand", app=self) \
+                    .execute_from_commandline(argv)
+
     def worker_main(self, argv=None):
     def worker_main(self, argv=None):
         """Run :program:`celeryd` using `argv`.  Uses :data:`sys.argv`
         """Run :program:`celeryd` using `argv`.  Uses :data:`sys.argv`
         if `argv` is not specified."""
         if `argv` is not specified."""

+ 566 - 0
celery/bin/celery.py

@@ -0,0 +1,566 @@
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import
+from __future__ import with_statement
+
+if __name__ == "__main__" and __package__ is None:
+    __package__ = "celery.bin.celery"
+
+import sys
+
+from importlib import import_module
+from optparse import OptionParser, make_option as Option
+from pprint import pformat
+from textwrap import wrap
+
+from anyjson import deserialize
+
+from .. import __version__
+from ..app import app_or_default, current_app
+from ..platforms import EX_OK, EX_FAILURE, EX_UNAVAILABLE, EX_USAGE
+from ..utils import term
+from ..utils.imports import symbol_by_name
+from ..utils.text import pluralize
+from ..utils.timeutils import maybe_iso8601
+
+from ..bin.base import Command as BaseCommand
+
+HELP = """
+Type '%(prog_name)s <command> --help' for help using
+a specific command.
+
+Available commands:
+%(commands)s
+"""
+
+commands = {}
+
+
+class Error(Exception):
+
+    def __init__(self, reason, status=EX_FAILURE):
+        self.reason = reason
+        self.status = status
+        super(Error, self).__init__(reason, status)
+
+    def __str__(self):
+        return self.reason
+
+
+def command(fun, name=None):
+    commands[name or fun.__name__] = fun
+    return fun
+
+
+class Command(object):
+    help = ""
+    args = ""
+    version = __version__
+
+    option_list = BaseCommand.preload_options + (
+        Option("--quiet", "-q", action="store_true", dest="quiet",
+                default=False),
+        Option("--no-color", "-C", dest="no_color", action="store_true",
+            help="Don't colorize output."),
+    )
+
+    def __init__(self, app=None, no_color=False):
+        self.app = app_or_default(app)
+        self.colored = term.colored(enabled=not no_color)
+
+    def __call__(self, *args, **kwargs):
+        try:
+            ret = self.run(*args, **kwargs)
+        except Error, exc:
+            self.error(self.colored.red("Error: %s" % exc))
+            return exc.status
+
+        return ret if ret is not None else EX_OK
+
+    def show_help(self, command):
+        self.run_from_argv(self.prog_name, [command, "--help"])
+        return EX_USAGE
+
+    def error(self, s):
+        self.out(s, fh=sys.stderr)
+
+    def out(self, s, fh=sys.stdout):
+        s = str(s)
+        if not s.endswith("\n"):
+            s += "\n"
+        fh.write(s)
+
+    def create_parser(self, prog_name, command):
+        return OptionParser(prog=prog_name,
+                            usage=self.usage(command),
+                            version=self.version,
+                            option_list=self.get_options())
+
+    def get_options(self):
+        return self.option_list
+
+    def run_from_argv(self, prog_name, argv):
+        self.prog_name = prog_name
+        self.command = argv[0]
+        self.arglist = argv[1:]
+        self.parser = self.create_parser(self.prog_name, self.command)
+        options, args = self.parser.parse_args(self.arglist)
+        self.colored = term.colored(enabled=not options.no_color)
+        return self(*args, **options.__dict__)
+
+    def run(self, *args, **kwargs):
+        raise NotImplementedError()
+
+    def usage(self, command):
+        return "%%prog %s [options] %s" % (command, self.args)
+
+    def prettify_list(self, n):
+        c = self.colored
+        if not n:
+            return "- empty -"
+        return "\n".join(str(c.reset(c.white("*"), " %s" % (item, )))
+                            for item in n)
+
+    def prettify_dict_ok_error(self, n):
+        c = self.colored
+        if "ok" in n:
+            return (c.green("OK"),
+                    indent(self.prettify(n["ok"])[1]))
+        elif "error" in n:
+            return (c.red("ERROR"),
+                    indent(self.prettify(n["error"])[1]))
+
+    def prettify(self, n):
+        OK = str(self.colored.green("OK"))
+        if isinstance(n, list):
+            return OK, self.prettify_list(n)
+        if isinstance(n, dict):
+            if "ok" in n or "error" in n:
+                return self.prettify_dict_ok_error(n)
+        if isinstance(n, basestring):
+            return OK, unicode(n)
+        return OK, pformat(n)
+
+
+class Delegate(Command):
+
+    def __init__(self, *args, **kwargs):
+        super(Delegate, self).__init__(*args, **kwargs)
+
+        self.target = symbol_by_name(self.Command)(app=self.app)
+        self.args = self.target.args
+
+    def get_options(self):
+        return self.option_list + self.target.get_options()
+
+    def run(self, *args, **kwargs):
+        self.target.check_args(args)
+        return self.target.run(*args, **kwargs)
+
+
+def create_delegate(name, Command):
+    return command(type(name, (Delegate, ), {"Command": Command,
+                                             "__module__": __name__}))
+
+
+worker = create_delegate("worker", "celery.bin.celeryd:WorkerCommand")
+events = create_delegate("events", "celery.bin.celeryev:EvCommand")
+beat = create_delegate("beat", "celery.bin.celerybeat:BeatCommand")
+amqp = create_delegate("amqp", "celery.bin.camqadm:AMQPAdminCommand")
+
+
+class list_(Command):
+    args = "[bindings]"
+
+    def list_bindings(self, channel):
+        try:
+            bindings = channel.list_bindings()
+        except NotImplementedError:
+            raise Error("Your transport cannot list bindings.")
+
+        fmt = lambda q, e, r: self.out("%s %s %s" % (q.ljust(28),
+                                                     e.ljust(28), r))
+        fmt("Queue", "Exchange", "Routing Key")
+        fmt("-" * 16, "-" * 16, "-" * 16)
+        for binding in bindings:
+            fmt(*binding)
+
+    def run(self, what=None, *_, **kw):
+        topics = {"bindings": self.list_bindings}
+        available = ', '.join(topics.keys())
+        if not what:
+            raise Error("You must specify what to list (%s)" % available)
+        if what not in topics:
+            raise Error("unknown topic %r (choose one of: %s)" % (
+                            what, available))
+        with self.app.broker_connection() as conn:
+            self.app.amqp.get_task_consumer(conn).declare()
+            with conn.channel() as channel:
+                return topics[what](channel)
+list_ = command(list_, "list")
+
+
+class apply(Command):
+    args = "<task_name>"
+    option_list = Command.option_list + (
+            Option("--args", "-a", dest="args"),
+            Option("--kwargs", "-k", dest="kwargs"),
+            Option("--eta", dest="eta"),
+            Option("--countdown", dest="countdown", type="int"),
+            Option("--expires", dest="expires"),
+            Option("--serializer", dest="serializer", default="json"),
+            Option("--queue", dest="queue"),
+            Option("--exchange", dest="exchange"),
+            Option("--routing-key", dest="routing_key"),
+    )
+
+    def run(self, name, *_, **kw):
+        # Positional args.
+        args = kw.get("args") or ()
+        if isinstance(args, basestring):
+            args = deserialize(args)
+
+        # Keyword args.
+        kwargs = kw.get("kwargs") or {}
+        if isinstance(kwargs, basestring):
+            kwargs = deserialize(kwargs)
+
+        # Expires can be int/float.
+        expires = kw.get("expires") or None
+        try:
+            expires = float(expires)
+        except (TypeError, ValueError):
+            # or a string describing an ISO 8601 datetime.
+            try:
+                expires = maybe_iso8601(expires)
+            except (TypeError, ValueError):
+                pass
+
+        res = self.app.send_task(name, args=args, kwargs=kwargs,
+                                 countdown=kw.get("countdown"),
+                                 serializer=kw.get("serializer"),
+                                 queue=kw.get("queue"),
+                                 exchange=kw.get("exchange"),
+                                 routing_key=kw.get("routing_key"),
+                                 eta=maybe_iso8601(kw.get("eta")),
+                                 expires=expires)
+        self.out(res.id)
+apply = command(apply)
+
+
+class purge(Command):
+
+    def run(self, *args, **kwargs):
+        app = current_app()
+        queues = len(app.amqp.queues.keys())
+        messages_removed = app.control.discard_all()
+        if messages_removed:
+            self.out("Purged %s %s from %s known task %s." % (
+                messages_removed, pluralize(messages_removed, "message"),
+                queues, pluralize(queues, "queue")))
+        else:
+            self.out("No messages purged from %s known %s" % (
+                queues, pluralize(queues, "queue")))
+purge = command(purge)
+
+
+class result(Command):
+    args = "<task_id>"
+    option_list = Command.option_list + (
+            Option("--task", "-t", dest="task"),
+    )
+
+    def run(self, task_id, *args, **kwargs):
+        result_cls = self.app.AsyncResult
+        task = kwargs.get("task")
+
+        if task:
+            result_cls = self.app.tasks[task].AsyncResult
+        result = result_cls(task_id)
+        self.out(self.prettify(result.get())[1])
+result = command(result)
+
+
+class inspect(Command):
+    choices = {"active": 1.0,
+               "active_queues": 1.0,
+               "scheduled": 1.0,
+               "reserved": 1.0,
+               "stats": 1.0,
+               "revoked": 1.0,
+               "registered_tasks": 1.0,  # alias to registered
+               "registered": 1.0,
+               "enable_events": 1.0,
+               "disable_events": 1.0,
+               "ping": 0.2,
+               "add_consumer": 1.0,
+               "cancel_consumer": 1.0,
+               "report": 1.0}
+    option_list = Command.option_list + (
+                Option("--timeout", "-t", type="float", dest="timeout",
+                    default=None,
+                    help="Timeout in seconds (float) waiting for reply"),
+                Option("--destination", "-d", dest="destination",
+                    help="Comma separated list of destination node names."))
+    show_body = True
+
+    def usage(self, command):
+        return "%%prog %s [options] %s [%s]" % (
+                command, self.args, "|".join(self.choices.keys()))
+
+    def run(self, *args, **kwargs):
+        self.quiet = kwargs.get("quiet", False)
+        self.show_body = kwargs.get("show_body", True)
+        if not args:
+            raise Error("Missing inspect command. See --help")
+        command = args[0]
+        if command == "help":
+            raise Error("Did you mean 'inspect --help'?")
+        if command not in self.choices:
+            raise Error("Unknown inspect command: %s" % command)
+
+        destination = kwargs.get("destination")
+        timeout = kwargs.get("timeout") or self.choices[command]
+        if destination and isinstance(destination, basestring):
+            destination = map(str.strip, destination.split(","))
+
+        def on_reply(body):
+            c = self.colored
+            node = body.keys()[0]
+            reply = body[node]
+            status, preply = self.prettify(reply)
+            self.say("->", c.cyan(node, ": ") + status, indent(preply))
+
+        self.say("<-", command)
+        i = self.app.control.inspect(destination=destination,
+                                     timeout=timeout,
+                                     callback=on_reply)
+        replies = getattr(i, command)(*args[1:])
+        if not replies:
+            raise Error("No nodes replied within time constraint.",
+                        status=EX_UNAVAILABLE)
+        return replies
+
+    def say(self, direction, title, body=""):
+        c = self.colored
+        if direction == "<-" and self.quiet:
+            return
+        dirstr = not self.quiet and c.bold(c.white(direction), " ") or ""
+        self.out(c.reset(dirstr, title))
+        if body and self.show_body:
+            self.out(body)
+inspect = command(inspect)
+
+
+def indent(s, n=4):
+    i = [" " * n + l for l in s.split("\n")]
+    return "\n".join("\n".join(wrap(j)) for j in i)
+
+
+class status(Command):
+    option_list = inspect.option_list
+
+    def run(self, *args, **kwargs):
+        replies = inspect(app=self.app,
+                          no_color=kwargs.get("no_color", False)) \
+                    .run("ping", **dict(kwargs, quiet=True, show_body=False))
+        if not replies:
+            raise Error("No nodes replied within time constraint",
+                        status=EX_UNAVAILABLE)
+        nodecount = len(replies)
+        if not kwargs.get("quiet", False):
+            self.out("\n%s %s online." % (nodecount,
+                                          pluralize(nodecount, "node")))
+status = command(status)
+
+
+class migrate(Command):
+
+    def usage(self, command):
+        return "%%prog %s <source_url> <dest_url>" % (command, )
+
+    def on_migrate_task(self, state, body, message):
+        self.out("Migrating task %s/%s: %s[%s]" % (
+            state.count, state.strtotal, body["task"], body["id"]))
+
+    def run(self, *args, **kwargs):
+        if len(args) != 2:
+            return self.show_help("migrate")
+        from kombu import BrokerConnection
+        from ..contrib.migrate import migrate_tasks
+
+        migrate_tasks(BrokerConnection(args[0]),
+                      BrokerConnection(args[1]),
+                      callback=self.on_migrate_task)
+migrate = command(migrate)
+
+
+class shell(Command):
+    option_list = Command.option_list + (
+                Option("--ipython", "-I", action="store_true",
+                    dest="force_ipython", default=False,
+                    help="Force IPython."),
+                Option("--bpython", "-B", action="store_true",
+                    dest="force_bpython", default=False,
+                    help="Force bpython."),
+                Option("--python", "-P", action="store_true",
+                    dest="force_python", default=False,
+                    help="Force default Python shell."),
+                Option("--without-tasks", "-T", action="store_true",
+                    dest="without_tasks", default=False,
+                    help="Don't add tasks to locals."),
+                Option("--eventlet", action="store_true",
+                    dest="eventlet", default=False,
+                    help="Use eventlet."),
+                Option("--gevent", action="store_true",
+                    dest="gevent", default=False,
+                    help="Use gevent."),
+    )
+
+    def run(self, force_ipython=False, force_bpython=False,
+            force_python=False, without_tasks=False, eventlet=False,
+            gevent=False, **kwargs):
+        if eventlet:
+            import_module("celery.concurrency.eventlet")
+        if gevent:
+            import_module("celery.concurrency.gevent")
+        from .. import task
+        self.app.loader.import_default_modules()
+        self.locals = {"celery": self.app,
+                       "BaseTask": task.BaseTask,
+                       "TaskSet": task.TaskSet,
+                       "chord": task.chord,
+                       "group": task.group,
+                       "chain": task.chain}
+
+        if not without_tasks:
+            self.locals.update(dict((task.__name__, task)
+                                for task in self.app.tasks.itervalues()))
+
+        if force_python:
+            return self.invoke_fallback_shell()
+        elif force_bpython:
+            return self.invoke_bpython_shell()
+        elif force_ipython:
+            return self.invoke_ipython_shell()
+        return self.invoke_default_shell()
+
+    def invoke_default_shell(self):
+        try:
+            import IPython  # noqa
+        except ImportError:
+            try:
+                import bpython  # noqa
+            except ImportError:
+                return self.invoke_fallback_shell()
+            else:
+                return self.invoke_bpython_shell()
+        else:
+            return self.invoke_ipython_shell()
+
+    def invoke_fallback_shell(self):
+        import code
+        try:
+            import readline
+        except ImportError:
+            pass
+        else:
+            import rlcompleter
+            readline.set_completer(
+                    rlcompleter.Completer(self.locals).complete)
+            readline.parse_and_bind("tab:complete")
+        code.interact(local=self.locals)
+
+    def invoke_ipython_shell(self):
+        try:
+            from IPython.frontend.terminal import embed
+            embed.TerminalInteractiveShell(user_ns=self.locals).mainloop()
+        except ImportError:  # ipython < 0.11
+            from IPython.Shell import IPShell
+            IPShell(argv=[], user_ns=self.locals).mainloop()
+
+    def invoke_bpython_shell(self):
+        import bpython
+        bpython.embed(self.locals)
+
+shell = command(shell)
+
+
+class help(Command):
+
+    def usage(self, command):
+        return "%%prog <command> [options] %s" % (self.args, )
+
+    def run(self, *args, **kwargs):
+        self.parser.print_help()
+        self.out(HELP % {"prog_name": self.prog_name,
+                         "commands": "\n".join(indent(command)
+                                             for command in sorted(commands))})
+
+        return EX_USAGE
+help = command(help)
+
+
+class report(Command):
+
+    def run(self, *args, **kwargs):
+        print(self.app.bugreport())
+        return EX_OK
+report = command(report)
+
+
+class CeleryCommand(BaseCommand):
+    commands = commands
+    enable_config_from_cmdline = True
+
+    def execute(self, command, argv=None):
+        try:
+            cls = self.commands[command]
+        except KeyError:
+            cls, argv = self.commands["help"], ["help"]
+        cls = self.commands.get(command) or self.commands["help"]
+        try:
+            return cls(app=self.app).run_from_argv(self.prog_name, argv)
+        except Error:
+            return self.execute("help", argv)
+
+    def remove_options_at_beginning(self, argv, index=0):
+        if argv:
+            while index < len(argv):
+                value = argv[index]
+                if value.startswith("--"):
+                    pass
+                elif value.startswith("-"):
+                    index += 1
+                else:
+                    return argv[index:]
+                index += 1
+        return []
+
+    def handle_argv(self, prog_name, argv):
+        self.prog_name = prog_name
+        argv = self.remove_options_at_beginning(argv)
+        try:
+            command = argv[0]
+        except IndexError:
+            command, argv = "help", ["help"]
+        return self.execute(command, argv)
+
+    def execute_from_commandline(self, argv=None):
+        try:
+            sys.exit(determine_exit_status(
+                super(CeleryCommand, self).execute_from_commandline(argv)))
+        except KeyboardInterrupt:
+            sys.exit(EX_FAILURE)
+
+
+def determine_exit_status(ret):
+    if isinstance(ret, int):
+        return ret
+    return EX_OK if ret else EX_FAILURE
+
+
+def main():
+    CeleryCommand().execute_from_commandline()
+
+if __name__ == "__main__":          # pragma: no cover
+    main()

+ 2 - 550
celery/bin/celeryctl.py

@@ -7,557 +7,9 @@ if __name__ == "__main__" and __package__ is None:
 
 
 import sys
 import sys
 
 
-from importlib import import_module
-from optparse import OptionParser, make_option as Option
-from pprint import pformat
-from textwrap import wrap
+from ..platforms import EX_FAILURE
 
 
-from anyjson import deserialize
-
-from .. import __version__
-from ..app import app_or_default, current_app
-from ..platforms import EX_OK, EX_FAILURE, EX_UNAVAILABLE, EX_USAGE
-from ..utils import term
-from ..utils.imports import symbol_by_name
-from ..utils.text import pluralize
-from ..utils.timeutils import maybe_iso8601
-
-from ..bin.base import Command as CeleryCommand
-
-HELP = """
-Type '%(prog_name)s <command> --help' for help using
-a specific command.
-
-Available commands:
-%(commands)s
-"""
-
-commands = {}
-
-
-class Error(Exception):
-
-    def __init__(self, reason, status=EX_FAILURE):
-        self.reason = reason
-        self.status = status
-        super(Error, self).__init__(reason, status)
-
-    def __str__(self):
-        return self.reason
-
-
-def command(fun, name=None):
-    commands[name or fun.__name__] = fun
-    return fun
-
-
-class Command(object):
-    help = ""
-    args = ""
-    version = __version__
-
-    option_list = CeleryCommand.preload_options + (
-        Option("--quiet", "-q", action="store_true", dest="quiet",
-                default=False),
-        Option("--no-color", "-C", dest="no_color", action="store_true",
-            help="Don't colorize output."),
-    )
-
-    def __init__(self, app=None, no_color=False):
-        self.app = app_or_default(app)
-        self.colored = term.colored(enabled=not no_color)
-
-    def __call__(self, *args, **kwargs):
-        try:
-            ret = self.run(*args, **kwargs)
-        except Error, exc:
-            self.error(self.colored.red("Error: %s" % exc))
-            return exc.status
-
-        return ret if ret is not None else EX_OK
-
-    def show_help(self, command):
-        self.run_from_argv(self.prog_name, [command, "--help"])
-        return EX_USAGE
-
-    def error(self, s):
-        self.out(s, fh=sys.stderr)
-
-    def out(self, s, fh=sys.stdout):
-        s = str(s)
-        if not s.endswith("\n"):
-            s += "\n"
-        fh.write(s)
-
-    def create_parser(self, prog_name, command):
-        return OptionParser(prog=prog_name,
-                            usage=self.usage(command),
-                            version=self.version,
-                            option_list=self.get_options())
-
-    def get_options(self):
-        return self.option_list
-
-    def run_from_argv(self, prog_name, argv):
-        self.prog_name = prog_name
-        self.command = argv[0]
-        self.arglist = argv[1:]
-        self.parser = self.create_parser(self.prog_name, self.command)
-        options, args = self.parser.parse_args(self.arglist)
-        self.colored = term.colored(enabled=not options.no_color)
-        return self(*args, **options.__dict__)
-
-    def run(self, *args, **kwargs):
-        raise NotImplementedError()
-
-    def usage(self, command):
-        return "%%prog %s [options] %s" % (command, self.args)
-
-    def prettify_list(self, n):
-        c = self.colored
-        if not n:
-            return "- empty -"
-        return "\n".join(str(c.reset(c.white("*"), " %s" % (item, )))
-                            for item in n)
-
-    def prettify_dict_ok_error(self, n):
-        c = self.colored
-        if "ok" in n:
-            return (c.green("OK"),
-                    indent(self.prettify(n["ok"])[1]))
-        elif "error" in n:
-            return (c.red("ERROR"),
-                    indent(self.prettify(n["error"])[1]))
-
-    def prettify(self, n):
-        OK = str(self.colored.green("OK"))
-        if isinstance(n, list):
-            return OK, self.prettify_list(n)
-        if isinstance(n, dict):
-            if "ok" in n or "error" in n:
-                return self.prettify_dict_ok_error(n)
-        if isinstance(n, basestring):
-            return OK, unicode(n)
-        return OK, pformat(n)
-
-
-class Delegate(Command):
-
-    def __init__(self, *args, **kwargs):
-        super(Delegate, self).__init__(*args, **kwargs)
-
-        self.target = symbol_by_name(self.Command)(app=self.app)
-        self.args = self.target.args
-
-    def get_options(self):
-        return self.option_list + self.target.get_options()
-
-    def run(self, *args, **kwargs):
-        self.target.check_args(args)
-        return self.target.run(*args, **kwargs)
-
-
-def create_delegate(name, Command):
-    return command(type(name, (Delegate, ), {"Command": Command,
-                                             "__module__": __name__}))
-
-
-worker = create_delegate("worker", "celery.bin.celeryd:WorkerCommand")
-events = create_delegate("events", "celery.bin.celeryev:EvCommand")
-beat = create_delegate("beat", "celery.bin.celerybeat:BeatCommand")
-amqp = create_delegate("amqp", "celery.bin.camqadm:AMQPAdminCommand")
-
-
-class list_(Command):
-    args = "[bindings]"
-
-    def list_bindings(self, channel):
-        try:
-            bindings = channel.list_bindings()
-        except NotImplementedError:
-            raise Error("Your transport cannot list bindings.")
-
-        fmt = lambda q, e, r: self.out("%s %s %s" % (q.ljust(28),
-                                                     e.ljust(28), r))
-        fmt("Queue", "Exchange", "Routing Key")
-        fmt("-" * 16, "-" * 16, "-" * 16)
-        for binding in bindings:
-            fmt(*binding)
-
-    def run(self, what=None, *_, **kw):
-        topics = {"bindings": self.list_bindings}
-        available = ', '.join(topics.keys())
-        if not what:
-            raise Error("You must specify what to list (%s)" % available)
-        if what not in topics:
-            raise Error("unknown topic %r (choose one of: %s)" % (
-                            what, available))
-        with self.app.broker_connection() as conn:
-            self.app.amqp.get_task_consumer(conn).declare()
-            with conn.channel() as channel:
-                return topics[what](channel)
-list_ = command(list_, "list")
-
-
-class apply(Command):
-    args = "<task_name>"
-    option_list = Command.option_list + (
-            Option("--args", "-a", dest="args"),
-            Option("--kwargs", "-k", dest="kwargs"),
-            Option("--eta", dest="eta"),
-            Option("--countdown", dest="countdown", type="int"),
-            Option("--expires", dest="expires"),
-            Option("--serializer", dest="serializer", default="json"),
-            Option("--queue", dest="queue"),
-            Option("--exchange", dest="exchange"),
-            Option("--routing-key", dest="routing_key"),
-    )
-
-    def run(self, name, *_, **kw):
-        # Positional args.
-        args = kw.get("args") or ()
-        if isinstance(args, basestring):
-            args = deserialize(args)
-
-        # Keyword args.
-        kwargs = kw.get("kwargs") or {}
-        if isinstance(kwargs, basestring):
-            kwargs = deserialize(kwargs)
-
-        # Expires can be int/float.
-        expires = kw.get("expires") or None
-        try:
-            expires = float(expires)
-        except (TypeError, ValueError):
-            # or a string describing an ISO 8601 datetime.
-            try:
-                expires = maybe_iso8601(expires)
-            except (TypeError, ValueError):
-                pass
-
-        res = self.app.send_task(name, args=args, kwargs=kwargs,
-                                 countdown=kw.get("countdown"),
-                                 serializer=kw.get("serializer"),
-                                 queue=kw.get("queue"),
-                                 exchange=kw.get("exchange"),
-                                 routing_key=kw.get("routing_key"),
-                                 eta=maybe_iso8601(kw.get("eta")),
-                                 expires=expires)
-        self.out(res.id)
-apply = command(apply)
-
-
-class purge(Command):
-
-    def run(self, *args, **kwargs):
-        app = current_app()
-        queues = len(app.amqp.queues.keys())
-        messages_removed = app.control.discard_all()
-        if messages_removed:
-            self.out("Purged %s %s from %s known task %s." % (
-                messages_removed, pluralize(messages_removed, "message"),
-                queues, pluralize(queues, "queue")))
-        else:
-            self.out("No messages purged from %s known %s" % (
-                queues, pluralize(queues, "queue")))
-purge = command(purge)
-
-
-class result(Command):
-    args = "<task_id>"
-    option_list = Command.option_list + (
-            Option("--task", "-t", dest="task"),
-    )
-
-    def run(self, task_id, *args, **kwargs):
-        result_cls = self.app.AsyncResult
-        task = kwargs.get("task")
-
-        if task:
-            result_cls = self.app.tasks[task].AsyncResult
-        result = result_cls(task_id)
-        self.out(self.prettify(result.get())[1])
-result = command(result)
-
-
-class inspect(Command):
-    choices = {"active": 1.0,
-               "active_queues": 1.0,
-               "scheduled": 1.0,
-               "reserved": 1.0,
-               "stats": 1.0,
-               "revoked": 1.0,
-               "registered_tasks": 1.0,  # alias to registered
-               "registered": 1.0,
-               "enable_events": 1.0,
-               "disable_events": 1.0,
-               "ping": 0.2,
-               "add_consumer": 1.0,
-               "cancel_consumer": 1.0,
-               "report": 1.0}
-    option_list = Command.option_list + (
-                Option("--timeout", "-t", type="float", dest="timeout",
-                    default=None,
-                    help="Timeout in seconds (float) waiting for reply"),
-                Option("--destination", "-d", dest="destination",
-                    help="Comma separated list of destination node names."))
-    show_body = True
-
-    def usage(self, command):
-        return "%%prog %s [options] %s [%s]" % (
-                command, self.args, "|".join(self.choices.keys()))
-
-    def run(self, *args, **kwargs):
-        self.quiet = kwargs.get("quiet", False)
-        self.show_body = kwargs.get("show_body", True)
-        if not args:
-            raise Error("Missing inspect command. See --help")
-        command = args[0]
-        if command == "help":
-            raise Error("Did you mean 'inspect --help'?")
-        if command not in self.choices:
-            raise Error("Unknown inspect command: %s" % command)
-
-        destination = kwargs.get("destination")
-        timeout = kwargs.get("timeout") or self.choices[command]
-        if destination and isinstance(destination, basestring):
-            destination = map(str.strip, destination.split(","))
-
-        def on_reply(body):
-            c = self.colored
-            node = body.keys()[0]
-            reply = body[node]
-            status, preply = self.prettify(reply)
-            self.say("->", c.cyan(node, ": ") + status, indent(preply))
-
-        self.say("<-", command)
-        i = self.app.control.inspect(destination=destination,
-                                     timeout=timeout,
-                                     callback=on_reply)
-        replies = getattr(i, command)(*args[1:])
-        if not replies:
-            raise Error("No nodes replied within time constraint.",
-                        status=EX_UNAVAILABLE)
-        return replies
-
-    def say(self, direction, title, body=""):
-        c = self.colored
-        if direction == "<-" and self.quiet:
-            return
-        dirstr = not self.quiet and c.bold(c.white(direction), " ") or ""
-        self.out(c.reset(dirstr, title))
-        if body and self.show_body:
-            self.out(body)
-inspect = command(inspect)
-
-
-def indent(s, n=4):
-    i = [" " * n + l for l in s.split("\n")]
-    return "\n".join("\n".join(wrap(j)) for j in i)
-
-
-class status(Command):
-    option_list = inspect.option_list
-
-    def run(self, *args, **kwargs):
-        replies = inspect(app=self.app,
-                          no_color=kwargs.get("no_color", False)) \
-                    .run("ping", **dict(kwargs, quiet=True, show_body=False))
-        if not replies:
-            raise Error("No nodes replied within time constraint",
-                        status=EX_UNAVAILABLE)
-        nodecount = len(replies)
-        if not kwargs.get("quiet", False):
-            self.out("\n%s %s online." % (nodecount,
-                                          pluralize(nodecount, "node")))
-status = command(status)
-
-
-class migrate(Command):
-
-    def usage(self, command):
-        return "%%prog %s <source_url> <dest_url>" % (command, )
-
-    def on_migrate_task(self, state, body, message):
-        self.out("Migrating task %s/%s: %s[%s]" % (
-            state.count, state.strtotal, body["task"], body["id"]))
-
-    def run(self, *args, **kwargs):
-        if len(args) != 2:
-            return self.show_help("migrate")
-        from kombu import BrokerConnection
-        from ..contrib.migrate import migrate_tasks
-
-        migrate_tasks(BrokerConnection(args[0]),
-                      BrokerConnection(args[1]),
-                      callback=self.on_migrate_task)
-migrate = command(migrate)
-
-
-class shell(Command):
-    option_list = Command.option_list + (
-                Option("--ipython", "-I", action="store_true",
-                    dest="force_ipython", default=False,
-                    help="Force IPython."),
-                Option("--bpython", "-B", action="store_true",
-                    dest="force_bpython", default=False,
-                    help="Force bpython."),
-                Option("--python", "-P", action="store_true",
-                    dest="force_python", default=False,
-                    help="Force default Python shell."),
-                Option("--without-tasks", "-T", action="store_true",
-                    dest="without_tasks", default=False,
-                    help="Don't add tasks to locals."),
-                Option("--eventlet", action="store_true",
-                    dest="eventlet", default=False,
-                    help="Use eventlet."),
-                Option("--gevent", action="store_true",
-                    dest="gevent", default=False,
-                    help="Use gevent."),
-    )
-
-    def run(self, force_ipython=False, force_bpython=False,
-            force_python=False, without_tasks=False, eventlet=False,
-            gevent=False, **kwargs):
-        if eventlet:
-            import_module("celery.concurrency.eventlet")
-        if gevent:
-            import_module("celery.concurrency.gevent")
-        from .. import task
-        self.app.loader.import_default_modules()
-        self.locals = {"celery": self.app,
-                       "BaseTask": task.BaseTask,
-                       "TaskSet": task.TaskSet,
-                       "chord": task.chord,
-                       "group": task.group,
-                       "chain": task.chain}
-
-        if not without_tasks:
-            self.locals.update(dict((task.__name__, task)
-                                for task in self.app.tasks.itervalues()))
-
-        if force_python:
-            return self.invoke_fallback_shell()
-        elif force_bpython:
-            return self.invoke_bpython_shell()
-        elif force_ipython:
-            return self.invoke_ipython_shell()
-        return self.invoke_default_shell()
-
-    def invoke_default_shell(self):
-        try:
-            import IPython  # noqa
-        except ImportError:
-            try:
-                import bpython  # noqa
-            except ImportError:
-                return self.invoke_fallback_shell()
-            else:
-                return self.invoke_bpython_shell()
-        else:
-            return self.invoke_ipython_shell()
-
-    def invoke_fallback_shell(self):
-        import code
-        try:
-            import readline
-        except ImportError:
-            pass
-        else:
-            import rlcompleter
-            readline.set_completer(
-                    rlcompleter.Completer(self.locals).complete)
-            readline.parse_and_bind("tab:complete")
-        code.interact(local=self.locals)
-
-    def invoke_ipython_shell(self):
-        try:
-            from IPython.frontend.terminal import embed
-            embed.TerminalInteractiveShell(user_ns=self.locals).mainloop()
-        except ImportError:  # ipython < 0.11
-            from IPython.Shell import IPShell
-            IPShell(argv=[], user_ns=self.locals).mainloop()
-
-    def invoke_bpython_shell(self):
-        import bpython
-        bpython.embed(self.locals)
-
-shell = command(shell)
-
-
-class help(Command):
-
-    def usage(self, command):
-        return "%%prog <command> [options] %s" % (self.args, )
-
-    def run(self, *args, **kwargs):
-        self.parser.print_help()
-        self.out(HELP % {"prog_name": self.prog_name,
-                         "commands": "\n".join(indent(command)
-                                             for command in sorted(commands))})
-
-        return EX_USAGE
-help = command(help)
-
-
-class report(Command):
-
-    def run(self, *args, **kwargs):
-        print(self.app.bugreport())
-        return EX_OK
-report = command(report)
-
-
-class celeryctl(CeleryCommand):
-    commands = commands
-    enable_config_from_cmdline = True
-
-    def execute(self, command, argv=None):
-        try:
-            cls = self.commands[command]
-        except KeyError:
-            cls, argv = self.commands["help"], ["help"]
-        cls = self.commands.get(command) or self.commands["help"]
-        try:
-            return cls(app=self.app).run_from_argv(self.prog_name, argv)
-        except Error:
-            return self.execute("help", argv)
-
-    def remove_options_at_beginning(self, argv, index=0):
-        if argv:
-            while index < len(argv):
-                value = argv[index]
-                if value.startswith("--"):
-                    pass
-                elif value.startswith("-"):
-                    index += 1
-                else:
-                    return argv[index:]
-                index += 1
-        return []
-
-    def handle_argv(self, prog_name, argv):
-        self.prog_name = prog_name
-        argv = self.remove_options_at_beginning(argv)
-        try:
-            command = argv[0]
-        except IndexError:
-            command, argv = "help", ["help"]
-        return self.execute(command, argv)
-
-
-def determine_exit_status(ret):
-    if isinstance(ret, int):
-        return ret
-    return EX_OK if ret else EX_FAILURE
-
-
-def main():
-    try:
-        sys.exit(determine_exit_status(
-            celeryctl().execute_from_commandline()))
-    except KeyboardInterrupt:
-        sys.exit(EX_FAILURE)
+from .celery import CeleryCommand as celeryctl, main
 
 
 if __name__ == "__main__":          # pragma: no cover
 if __name__ == "__main__":          # pragma: no cover
     main()
     main()

+ 59 - 73
docs/getting-started/first-steps-with-celery.rst

@@ -12,9 +12,8 @@
 Choosing a Broker
 Choosing a Broker
 =================
 =================
 
 
-Celery requires a solution to send and receive messages, this is called
-the *message transport*.  Usually this comes in the form of a separate
-service called a *message broker*.
+Celery requires a solution to send and receive messages, usually this
+comes in the form of a separate service called a *message broker*.
 
 
 There are several choices available, including:
 There are several choices available, including:
 
 
@@ -49,88 +48,45 @@ in the Kombu documentation.
 Application
 Application
 ===========
 ===========
 
 
-The first thing you need is a Celery instance.  Since the instance is used as
-the entry-point for everything you want to do in Celery, like creating task and
+The first thing you need is a Celery instance, this is called the celery
+application or just app.  Since this instance is used as
+the entry-point for everything you want to do in Celery, like creating tasks and
 managing workers, it must be possible for other modules to import it.
 managing workers, it must be possible for other modules to import it.
 
 
 Some people create a dedicated module for it, but in this tutorial we will
 Some people create a dedicated module for it, but in this tutorial we will
-keep it in the same module used to start our worker.
+keep everything in the same module.
 
 
-Let's create the file :file:`worker.py`:
+Let's create the file :file:`tasks.py`:
 
 
 .. code-block:: python
 .. code-block:: python
 
 
     from celery import Celery
     from celery import Celery
 
 
-    celery = Celery(broker="amqp://guest:guest@localhost:5672")
-
-    if __name__ == "__main__":
-        celery.worker_main()
-
-The broker argument specifies the message broker we want to use, what
-we are using in this example is the default, but we keep it there for
-reference so you can see what the URLs look like.
-
-By default the state and return value (results) of the tasks are ignored,
-if you want to enable this please see :ref:`celerytut-keeping-results`.
-
-That's all you need to get started!
-
-If you want to dig deeper there are lots of configuration possibilities that
-can be applied.  For example you can set the default value for the workers
-`--concurrency`` argument, which is used to decide the number of pool worker
-processes, the name for this setting is :setting:`CELERYD_CONCURRENCY`:
-
-.. code-block:: python
-
-    celery.conf.CELERY_CONCURRENCY = 10
-
-If you are configuring many settings then one practice is to have a separate module
-containing the configuration.  You can tell your Celery instance to use
-this module, historically called ``celeryconfig.py``, with the
-:meth:`config_from_obj` method:
-
-.. code-block:: python
-
-    celery.config_from_object("celeryconfig")
-
-For a complete reference of configuration options, see :ref:`configuration`.
-
-.. _celerytut-simple-tasks:
-
-Creating a simple task
-======================
-
-In this tutorial we are creating a simple task that adds two
-numbers.  Tasks are defined in normal Python modules.
-
-By convention we will call our module :file:`tasks.py`, and it looks
-like this:
-
-:file: `tasks.py`
-
-.. code-block:: python
-
-    from worker import celery
+    celery = Celery("tasks", broker="amqp://guest:guest@localhost:5672")
 
 
     @celery.task
     @celery.task
     def add(x, y):
     def add(x, y):
         return x + y
         return x + y
 
 
-.. seealso::
+    if __name__ == "__main__":
+        celery.celery_main()
 
 
-    The full documentation on how to create tasks and task classes is in the
-    :doc:`../userguide/tasks` part of the user guide.
+The first argument to :class:`Celery` is the name of the current module,
+this is needed to that names can be automatically generated, the second
+argument is the broker keyword argument which specifies the URL of the
+message broker we want to use.
 
 
+We defined a single task, called ``add``, which returns the sum of two numbers.
 
 
 .. _celerytut-running-celeryd:
 .. _celerytut-running-celeryd:
 
 
 Running the celery worker server
 Running the celery worker server
 ================================
 ================================
 
 
-We can now run our ``worker.py`` program::
+We can now run the worker by executing our program with the ``worker``
+argument::
 
 
-    $ python worker.py --loglevel=INFO
+    $ python tasks.py worker --loglevel=INFO
 
 
 In production you will probably want to run the worker in the
 In production you will probably want to run the worker in the
 background as a daemon.  To do this you need to use the tools provided
 background as a daemon.  To do this you need to use the tools provided
@@ -139,7 +95,12 @@ for more information).
 
 
 For a complete listing of the command line options available, do::
 For a complete listing of the command line options available, do::
 
 
-    $  python worker.py --help
+    $  python tasks.py worker --help
+
+There also several other commands available, and similarly you can get a list
+of these::
+
+    $ python tasks.py --help
 
 
 .. _`supervisord`: http://supervisord.org
 .. _`supervisord`: http://supervisord.org
 
 
@@ -157,18 +118,16 @@ method which gives greater control of the task execution (see
 
 
     >>> from tasks import add
     >>> from tasks import add
     >>> add.delay(4, 4)
     >>> add.delay(4, 4)
-    <AsyncResult: 889143a6-39a2-4e52-837b-d80d33efb22d>
 
 
-At this point, the task has been sent to the message broker. The message
-broker will hold on to the task until a worker server has consumed and
-executed it.
+The task should now be executed by the worker you started earlier,
+and you can verify that by looking at the workers console output.
+
+Applying a task returns an :class:`~celery.result.AsyncResult` instance,
+which can be bused to check the state of the task, wait for the task to finish
+or get its return value (or if the task failed, the exception and traceback).
 
 
-Right now we have to check the worker log files to know what happened
-with the task.  Applying a task returns an
-:class:`~celery.result.AsyncResult`, if you have configured a result store
-the :class:`~celery.result.AsyncResult` enables you to check the state of
-the task, wait for the task to finish, get its return value
-or exception/traceback if the task failed, and more.
+But results aren't enabled by default, to enable it you have configure
+Celery to use a result backend, which is detailed in the next section.
 
 
 .. _celerytut-keeping-results:
 .. _celerytut-keeping-results:
 
 
@@ -218,6 +177,33 @@ If the task raises an exception, the return value of `result.successful()`
 will be :const:`False`, and `result.result` will contain the exception instance
 will be :const:`False`, and `result.result` will contain the exception instance
 raised by the task.
 raised by the task.
 
 
+.. _celerytut-configuration:
+
+Configuration
+-------------
+
+Celery is very flexible and comes with many configuration options that
+can be set on your app directly, or by using dedicated configuration files.
+
+For example you can set the default value for the workers
+`--concurrency`` argument, which is used to decide the number of pool worker
+processes, the name for this setting is :setting:`CELERYD_CONCURRENCY`:
+
+.. code-block:: python
+
+    celery.conf.CELERY_CONCURRENCY = 10
+
+If you are configuring many settings then one practice is to have a separate module
+containing the configuration.  You can tell your Celery instance to use
+this module, historically called ``celeryconfig.py``, with the
+:meth:`config_from_obj` method:
+
+.. code-block:: python
+
+    celery.config_from_object("celeryconfig")
+
+For a complete reference of configuration options, see :ref:`configuration`.
+
 Where to go from here
 Where to go from here
 =====================
 =====================
 
 

+ 12 - 0
examples/tutorial/tasks.py

@@ -0,0 +1,12 @@
+from __future__ import absolute_import
+
+from celery import Celery
+
+celery = Celery("tasks", broker="amqp://")
+
+@celery.task
+def add(x, y):
+    return x + y
+
+if __name__ == "__main__":
+    celery.celery_main()

+ 1 - 1
setup.py

@@ -152,7 +152,7 @@ else:
 # -*- Entry Points -*- #
 # -*- Entry Points -*- #
 
 
 console_scripts = entrypoints["console_scripts"] = [
 console_scripts = entrypoints["console_scripts"] = [
-        'celery = celery.bin.celeryctl:main',
+        'celery = celery.bin.celery:main',
         'celeryd = celery.bin.celeryd:main',
         'celeryd = celery.bin.celeryd:main',
         'celerybeat = celery.bin.celerybeat:main',
         'celerybeat = celery.bin.celerybeat:main',
         'camqadm = celery.bin.camqadm:main',
         'camqadm = celery.bin.camqadm:main',