Browse Source

Merge branch 'djangofree'

Ask Solem 15 years ago
parent
commit
045f7da2ba
100 changed files with 1057 additions and 2514 deletions
  1. 1 0
      AUTHORS
  2. 121 0
      Changelog
  3. 4 190
      FAQ
  4. 14 10
      README.rst
  5. 0 8
      bin/celeryinit
  6. 1 1
      celery/__init__.py
  7. 6 6
      celery/backends/__init__.py
  8. 0 62
      celery/backends/cache.py
  9. 74 13
      celery/backends/database.py
  10. 1 1
      celery/backends/pyredis.py
  11. 0 14
      celery/bin/celeryinit.py
  12. 11 56
      celery/conf.py
  13. 0 19
      celery/contrib/test_runner.py
  14. 0 0
      celery/db/__init__.py
  15. 70 0
      celery/db/models.py
  16. 36 0
      celery/db/session.py
  17. 7 81
      celery/loaders/__init__.py
  18. 1 0
      celery/loaders/base.py
  19. 19 10
      celery/loaders/default.py
  20. 0 100
      celery/loaders/djangoapp.py
  21. 0 0
      celery/management/commands/__init__.py
  22. 0 18
      celery/management/commands/camqadm.py
  23. 0 18
      celery/management/commands/celerybeat.py
  24. 0 18
      celery/management/commands/celeryd.py
  25. 0 37
      celery/management/commands/celerymon.py
  26. 0 149
      celery/managers.py
  27. 3 1
      celery/messaging.py
  28. 27 51
      celery/models.py
  29. 2 2
      celery/result.py
  30. 1 1
      celery/signals.py
  31. 1 8
      celery/task/base.py
  32. 0 19
      celery/task/rest.py
  33. 0 19
      celery/tests/runners.py
  34. 0 3
      celery/tests/test_backends/__init__.py
  35. 0 127
      celery/tests/test_backends/test_cache.py
  36. 0 68
      celery/tests/test_backends/test_database.py
  37. 0 36
      celery/tests/test_conf.py
  38. 0 28
      celery/tests/test_discovery.py
  39. 8 59
      celery/tests/test_loaders.py
  40. 0 74
      celery/tests/test_models.py
  41. 0 127
      celery/tests/test_views.py
  42. 16 83
      celery/tests/test_worker_job.py
  43. 0 16
      celery/urls.py
  44. 1 0
      celery/utils/dispatch/__init__.py
  45. 36 0
      celery/utils/dispatch/license.txt
  46. 275 0
      celery/utils/dispatch/saferef.py
  47. 209 0
      celery/utils/dispatch/signal.py
  48. 24 0
      celery/utils/mail.py
  49. 0 106
      celery/views.py
  50. 1 1
      celery/worker/job.py
  51. 5 27
      contrib/debian/init.d/celeryd
  52. 1 1
      contrib/release/doc4allmods
  53. 2 1
      contrib/requirements/default.txt
  54. 0 1
      contrib/requirements/test.txt
  55. 0 18
      contrib/supervisord/django/celerybeat.conf
  56. 0 22
      contrib/supervisord/django/celeryd.conf
  57. 0 112
      docs/_ext/djangodocs.py
  58. 2 9
      docs/conf.py
  59. 0 1
      docs/cookbook/index.rst
  60. 0 56
      docs/cookbook/unit-testing.rst
  61. 0 119
      docs/getting-started/first-steps-with-django.rst
  62. 0 1
      docs/getting-started/index.rst
  63. 12 27
      docs/getting-started/periodic-tasks.rst
  64. 14 10
      docs/includes/introduction.txt
  65. 0 26
      docs/internals/moduleindex.rst
  66. 0 8
      docs/internals/reference/celery.backends.cache.rst
  67. 0 8
      docs/internals/reference/celery.backends.database.rst
  68. 0 8
      docs/internals/reference/celery.managers.rst
  69. 0 76
      docs/internals/reference/celery.models.rst
  70. 0 4
      docs/internals/reference/index.rst
  71. 0 8
      docs/reference/celery.bin.celeryinit.rst
  72. 2 3
      docs/reference/celery.conf.rst
  73. 0 8
      docs/reference/celery.contrib.test_runner.rst
  74. 0 8
      docs/reference/celery.loaders.djangoapp.rst
  75. 0 8
      docs/reference/celery.views.rst
  76. 0 4
      docs/reference/index.rst
  77. 10 12
      docs/userguide/tasks.rst
  78. 0 4
      examples/README.rst
  79. 1 1
      examples/celery_http_gateway/settings.py
  80. 1 1
      examples/celery_http_gateway/urls.py
  81. 0 0
      examples/django/demoproject/__init__.py
  82. 0 0
      examples/django/demoproject/demoapp/__init__.py
  83. 0 3
      examples/django/demoproject/demoapp/models.py
  84. 0 6
      examples/django/demoproject/demoapp/tasks.py
  85. 0 1
      examples/django/demoproject/demoapp/views.py
  86. 0 15
      examples/django/demoproject/manage.py
  87. 0 80
      examples/django/demoproject/settings.py
  88. 0 0
      examples/django/demoproject/twitterfollow/__init__.py
  89. 0 20
      examples/django/demoproject/twitterfollow/models.py
  90. 0 0
      examples/django/demoproject/twitterfollow/tasks.py
  91. 0 1
      examples/django/demoproject/twitterfollow/views.py
  92. 0 17
      examples/django/demoproject/urls.py
  93. 7 2
      setup.cfg
  94. 13 32
      setup.py
  95. 17 0
      tests/celeryconfig.py
  96. 0 16
      tests/manage.py
  97. 0 78
      tests/settings.py
  98. 0 0
      tests/someapp/__init__.py
  99. 0 3
      tests/someapp/models.py
  100. 0 8
      tests/someapp/tasks.py

+ 1 - 0
AUTHORS

@@ -28,3 +28,4 @@ Ordered by date of first contribution:
   Jeff Balogh <me@jeffbalogh.org>
   Patrick Altman <paltman@gmail.com>
   Vincent Driessen <vincent@datafox.nl>
+  Hari <haridara@gmail.com>

+ 121 - 0
Changelog

@@ -2,6 +2,127 @@
  Change history
 ================
 
+1.2.0 [xxxx-xx-xx xx:xx x.x xxxx]
+=================================
+
+Upgrading for Django-users
+--------------------------
+
+Django integration has been moved to a separate package: `django-celery`_.
+
+* To upgrade you need to install the `django-celery`_ module and change::
+
+    INSTALLED_APPS = "celery"
+
+  to::
+
+    INSTALLED_APPS = "djcelery"
+
+
+* The following modules has been moved to `django-celery`_:
+
+    =====================================  =====================================
+    **Module name**                        **Replace with**
+    =====================================  =====================================
+    ``celery.models``                      ``djcelery.models``
+    ``celery.managers``                    ``djcelery.managers``
+    ``celery.views``                       ``djcelery.views``
+    ``celery.urls``                        ``djcelery.url``
+    ``celery.management``                  ``djcelery.management``
+    ``celery.loaders.djangoapp``           ``djcelery.loaders``
+    ``celery.backends.database``           ``djcelery.backends.database``
+    ``celery.backends.cache``              ``djcelery.backends.cache``
+    =====================================  =====================================
+
+Importing ``djcelery`` will automatically setup celery to use the Django
+loader by setting the :envvar:`CELERY_LOADER`` environment variable (it won't
+change it if it's already defined).
+
+When the Django loader is used, the "database" and "cache" backend aliases
+will point to the ``djcelery`` backends instead of the built-in backends.
+
+.. _`django-celery`: http://pypi.python.org/pypi/django-celery
+
+
+Upgrading for others
+--------------------
+
+The database backend is now using `SQLAlchemy`_ instead of the Django ORM,
+see `Supported Databases`_ for a table of supported databases.
+
+
+The ``DATABASE_*`` settings has been replaced by a single setting:
+``CELERY_RESULT_DBURI``. The value here should be an `SQLAlchemy Connection
+String`_, some examples include:
+
+.. code-block:: python
+
+    # sqlite (filename)
+    CELERY_RESULT_DBURI = "sqlite:///celerydb.sqlite"
+
+    # mysql
+    CELERY_RESULT_DBURI = "mysql://scott:tiger@localhost/foo"
+
+    # postgresql
+    CELERY_RESULT_DBURI = "postgresql://scott:tiger@localhost/mydatabase"
+
+    # oracle
+    CELERY_RESULT_DBURI = "oracle://scott:tiger@127.0.0.1:1521/sidname"
+
+See `SQLAlchemy Connection Strings`_ for more information about connection
+strings.
+
+To specify additional SQLAlchemy database engine options you can use
+the ``CELERY_RESULT_ENGINE_OPTIONS`` setting::
+
+    # echo enables verbose logging from SQLAlchemy.
+    CELERY_RESULT_ENGINE_OPTIONS = {"echo": True}
+
+.. _`SQLAlchemy`:
+    http://www.sqlalchemy.org
+.. _`Supported Databases`:
+    http://www.sqlalchemy.org/docs/dbengine.html#supported-databases
+.. _`SQLAlchemy Connection String`:
+    http://www.sqlalchemy.org/docs/dbengine.html#create-engine-url-arguments
+.. _`SQLAlchemy Connection Strings`:
+    http://www.sqlalchemy.org/docs/dbengine.html#create-engine-url-arguments
+
+Backward incompatible changes
+-----------------------------
+
+* The following deprecated settings has been removed (as scheduled by
+  the `deprecation timeline`_):
+
+    =====================================  =====================================
+    **Setting name**                       **Replace with**
+    =====================================  =====================================
+    ``CELERY_AMQP_CONSUMER_QUEUES``        ``CELERY_QUEUES``
+    ``CELERY_AMQP_CONSUMER_QUEUES``        ``CELERY_QUEUES``
+    ``CELERY_AMQP_EXCHANGE``               ``CELERY_DEFAULT_EXCHANGE``
+    ``CELERY_AMQP_EXCHANGE_TYPE``          ``CELERY_DEFAULT_AMQP_EXCHANGE_TYPE``
+    ``CELERY_AMQP_CONSUMER_ROUTING_KEY``   ``CELERY_QUEUES``
+    ``CELERY_AMQP_PUBLISHER_ROUTING_KEY``  ``CELERY_DEFAULT_ROUTING_KEY``
+    =====================================  =====================================
+
+.. _`deprecation timeline`:
+    http://ask.github.com/celery/internals/deprecation.html
+
+* The ``celery.task.rest`` module has been removed, use ``celery.task.http``
+  instead (as scheduled by the `deprecation timeline`_).
+
+* It's no longer allowed to skip the class name in loader names.
+  (as scheduled by the `deprecation timeline`_):
+
+    Assuming the implicit ``Loader`` class name is no longer supported,
+    if you use e.g.::
+
+        CELERY_LOADER = "myapp.loaders"
+
+    You need to include the loader class name, like this::
+
+        CELERY_LOADER = "myapp.loaders.Loader"
+
+
 1.0.3 [2010-05-15 03:00 P.M CEST]
 =================================
 

+ 4 - 190
FAQ

@@ -58,41 +58,11 @@ Is celery for Django only?
 
 **Answer:** No.
 
-You can use all of the features without using Django.
+Celery does not depend on Django anymore. To use Celery with Django you have
+to use the `django-celery`_ package:
 
 
-Why is Django a dependency?
----------------------------
-
-Celery uses the Django ORM for database access when using the database result
-backend, the Django cache framework when using the cache result backend, and the Django signal
-dispatch mechanisms for signaling.
-
-This doesn't mean you need to have a Django project to use celery, it
-just means that sometimes we use internal Django components.
-
-The long term plan is to replace these with other solutions, (e.g. `SQLAlchemy`_ as the ORM,
-and `louie`_, for signaling). The celery distribution will be split into two:
-
-    * celery
-
-        The core. Using SQLAlchemy for the database backend.
-
-    * django-celery
-
-        Celery integration for Django, using the Django ORM for the database
-        backend.
-
-We're currently seeking people with `SQLAlchemy`_ experience, so please
-contact the project if you want this done sooner.
-
-The reason for the split is for purity only. It shouldn't affect you much as a
-user, so please don't worry about the Django dependency, just have a good time
-using celery.
-
-.. _`SQLAlchemy`: http://www.sqlalchemy.org/
-.. _`louie`: http://pypi.python.org/pypi/Louie/
-
+.. _`django-celery`: http://pypi.python.org/pypi/django-celery
 
 Do I have to use AMQP/RabbitMQ?
 -------------------------------
@@ -222,9 +192,7 @@ with::
 Why won't my Task run?
 ----------------------
 
-**Answer:** Did you register the task in the applications ``tasks.py`` module?
-(or in some other module Django loads by default, like ``models.py``?).
-Also there might be syntax errors preventing the tasks module being imported.
+**Answer:** There might be syntax errors preventing the tasks module being imported.
 
 You can find out if celery is able to run the task by executing the
 task manually:
@@ -612,121 +580,6 @@ could also be useful as a source of information.
 .. _`Standard Exchange Types`: http://bit.ly/EEWca
 .. _`RabbitMQ FAQ`: http://www.rabbitmq.com/faq.html
 
-Can I use celery without Django?
---------------------------------
-
-**Answer:** Yes.
-
-Celery uses something called loaders to read/setup configuration, import
-modules that register tasks and to decide what happens when a task is
-executed. Currently there are two loaders, the default loader and the Django
-loader. If you want to use celery without a Django project, you either have to
-use the default loader, or write a loader of your own.
-
-The rest of this answer describes how to use the default loader.
-
-While it is possible to use Celery from outside of Django, we still need
-Django itself to run, this is to use the ORM and cache-framework.
-Duplicating these features would be time consuming and mostly pointless, so
-while me might rewrite these in the future, this is a good solution in the
-mean time.
-Install Django using your favorite install tool, ``easy_install``, ``pip``, or
-whatever::
-
-    # easy_install django # as root
-
-You need a configuration file named ``celeryconfig.py``, either in the
-directory you run ``celeryd`` in, or in a Python library path where it is
-able to find it. The configuration file can contain any of the settings
-described in :mod:`celery.conf`. In addition; if you're using the
-database backend you have to configure the database. Here is an example
-configuration using the database backend with MySQL:
-
-.. code-block:: python
-
-    # Broker configuration
-    BROKER_HOST = "localhost"
-    BROKER_PORT = "5672"
-    BROKER_VHOST = "celery"
-    BROKER_USER = "celery"
-    BROKER_PASSWORD = "celerysecret"
-    CARROT_BACKEND="amqp"
-
-    # Using the database backend.
-    CELERY_RESULT_BACKEND = "database"
-    DATABASE_ENGINE = "mysql" # see Django docs for a description of these.
-    DATABASE_NAME = "mydb"
-    DATABASE_HOST = "mydb.example.org"
-    DATABASE_USER = "myuser"
-    DATABASE_PASSWORD = "mysecret"
-
-    # Number of processes that processes tasks simultaneously.
-    CELERYD_CONCURRENCY = 8
-
-    # Modules to import when celeryd starts.
-    # This must import every module where you register tasks so celeryd
-    # is able to find and run them.
-    CELERY_IMPORTS = ("mytaskmodule1", "mytaskmodule2")
-    
-With this configuration file in the current directory you have to
-run ``celeryinit`` to create the database tables::
-
-    $ celeryinit
-
-At this point you should be able to successfully run ``celeryd``::
-
-    $ celeryd --loglevel=INFO
-
-and send a task from a python shell (note that it must be able to import
-``celeryconfig.py``):
-
-    >>> from celery.task.builtins import PingTask
-    >>> result = PingTask.apply_async()
-    >>> result.get()
-    'pong'
-
-The celery test-suite is failing
---------------------------------
-
-**Answer**: If you're running tests from your Django project, and the celery
-test suite is failing in that context, then follow the steps below. If the
-celery tests are failing in another context, please report an issue to our
-issue tracker at GitHub:
-
-    http://github.com/ask/celery/issues/
-
-That Django is running tests for all applications in ``INSTALLED_APPS``
-by default is a pet peeve for many. You should use a test runner that either
-
-    1) Explicitly lists the apps you want to run tests for, or
-
-    2) Make a test runner that skips tests for apps you don't want to run.
-
-For example the test runner that celery is using:
-
-    http://bit.ly/NVKep
-
-To use this test runner, add the following to your ``settings.py``:
-
-.. code-block:: python
-
-    TEST_RUNNER = "celery.tests.runners.run_tests"
-    TEST_APPS = (
-        "app1",
-        "app2",
-        "app3",
-        "app4",
-    )
-
-Or, if you just want to skip the celery tests:
-
-.. code-block:: python
-
-    INSTALLED_APPS = (.....)
-    TEST_RUNNER = "celery.tests.runners.run_tests"
-    TEST_APPS = filter(lambda k: k != "celery", INSTALLED_APPS)
-
-
 Can I change the interval of a periodic task at runtime?
 --------------------------------------------------------
 
@@ -835,42 +688,3 @@ and they will not be re-run unless you have the ``acks_late`` option set.
 How do I run celeryd in the background on [platform]?
 -----------------------------------------------------
 **Answer**: Please see :doc:`cookbook/daemonizing`.
-
-Django
-======
-
-Generating a template in a task doesn't seem to respect my i18n settings?
--------------------------------------------------------------------------
-
-**Answer**: To enable the Django translation machinery you need to activate
-it with a language. **Note**: Be sure to reset to the previous language when
-done.
-
-    >>> from django.utils import translation
-
-    >>> prev_language = translation.get_language()
-    >>> translation.activate(language)
-    >>> try:
-    ...     render_template()
-    ... finally:
-            translation.activate(prev_language)
-
-The common pattern here would be for the task to take a ``language``
-argument:
-
-.. code-block:: python
-
-    from celery.decorators import task
-
-    from django.utils import translation
-    from django.template.loader import render_to_string
-
-    @task()
-    def generate_report(template="report.html", language=None):
-        prev_language = translation.get_language()
-        language and translation.activate(language)
-        try:
-            report = render_to_string(template)
-        finally:
-            translation.activate(prev_language)
-        save_report_somewhere(report)

+ 14 - 10
README.rst

@@ -4,12 +4,12 @@
 
 .. image:: http://cloud.github.com/downloads/ask/celery/celery_favicon_128.png
 
-:Version: 1.0.3
+:Version: 1.1.0
 :Web: http://celeryproject.org/
 :Download: http://pypi.python.org/pypi/celery/
 :Source: http://github.com/ask/celery/
 :Keywords: task queue, job queue, asynchronous, rabbitmq, amqp, redis,
-  django, python, webhooks, queue, distributed
+  python, webhooks, queue, distributed
 
 --
 
@@ -22,14 +22,20 @@ more worker servers. Tasks can execute asynchronously (in the background) or syn
 
 Celery is already used in production to process millions of tasks a day.
 
-Celery was originally created for use with Django, but is now usable
-from any Python project. It can
-also `operate with other languages via webhooks`_.
+Celery is written in Python, but the protocol can be implemented in any
+language. It can also `operate with other languages using webhooks`_.
 
-The recommended message broker is `RabbitMQ`_, but support for Redis and
-databases is also available.
+The recommended message broker is `RabbitMQ`_, but support for `Redis`_ and
+databases (`SQLAlchemy`_) is also available.
 
-.. _`operate with other languages via webhooks`:
+You may also be pleased to know that full Django integration exists
+via the `django-celery`_ package.
+
+.. _`RabbitMQ`: http://www.rabbitmq.com/
+.. _`Redis`: http://code.google.com/p/redis/
+.. _`SQLAlchemy`: http://www.sqlalchemy.org/
+.. _`django-celery`: http://pypi.python.org/pypi/django-celery
+.. _`operate with other languages using webhooks`:
     http://ask.github.com/celery/userguide/remote-tasks.html
 
 Overview
@@ -150,12 +156,10 @@ Features
     +-----------------+----------------------------------------------------+
 
 
-.. _`RabbitMQ`: http://www.rabbitmq.com/
 .. _`clustering`: http://www.rabbitmq.com/clustering.html
 .. _`AMQP`: http://www.amqp.org/
 .. _`Stomp`: http://stomp.codehaus.org/
 .. _`MongoDB`: http://www.mongodb.org/
-.. _`Redis`: http://code.google.com/p/redis/
 .. _`Tokyo Tyrant`: http://tokyocabinet.sourceforge.net/
 
 Documentation

+ 0 - 8
bin/celeryinit

@@ -1,8 +0,0 @@
-#!/usr/bin/env python
-import sys
-if not '' in sys.path:
-    sys.path.insert(0, '')
-from celery.bin import celeryinit
-
-if __name__ == "__main__":
-    celeryinit.main()

+ 1 - 1
celery/__init__.py

@@ -1,6 +1,6 @@
 """Distributed Task Queue"""
 
-VERSION = (1, 0, 3)
+VERSION = (1, 1, 0)
 
 __version__ = ".".join(map(str, VERSION[0:3])) + "".join(VERSION[3:])
 __author__ = "Ask Solem"

+ 6 - 6
celery/backends/__init__.py

@@ -2,15 +2,14 @@ from billiard.utils.functional import curry
 
 from celery import conf
 from celery.utils import get_cls_by_name
+from celery.loaders import current_loader
 
 BACKEND_ALIASES = {
     "amqp": "celery.backends.amqp.AMQPBackend",
-    "database": "celery.backends.database.DatabaseBackend",
-    "db": "celery.backends.database.DatabaseBackend",
     "redis": "celery.backends.pyredis.RedisBackend",
-    "cache": "celery.backends.cache.CacheBackend",
     "mongodb": "celery.backends.mongodb.MongoBackend",
     "tyrant": "celery.backends.tyrant.TyrantBackend",
+    "database": "celery.backends.database.DatabaseBackend",
 }
 
 _backend_cache = {}
@@ -19,14 +18,15 @@ _backend_cache = {}
 def get_backend_cls(backend):
     """Get backend class by name/alias"""
     if backend not in _backend_cache:
-        _backend_cache[backend] = get_cls_by_name(backend, BACKEND_ALIASES)
+        aliases = dict(BACKEND_ALIASES, **current_loader().override_backends)
+        _backend_cache[backend] = get_cls_by_name(backend, aliases)
     return _backend_cache[backend]
 
 
 """
 .. function:: get_default_backend_cls()
 
-    Get the backend class specified in :setting:`CELERY_RESULT_BACKEND`.
+    Get the backend class specified in the ``CELERY_RESULT_BACKEND`` setting.
 
 """
 get_default_backend_cls = curry(get_backend_cls, conf.RESULT_BACKEND)
@@ -36,7 +36,7 @@ get_default_backend_cls = curry(get_backend_cls, conf.RESULT_BACKEND)
 .. class:: DefaultBackend
 
     The default backend class used for storing task results and status,
-    specified in :setting:`CELERY_RESULT_BACKEND`.
+    specified in the ``CELERY_RESULT_BACKEND`` setting.
 
 """
 DefaultBackend = get_default_backend_cls()

+ 0 - 62
celery/backends/cache.py

@@ -1,62 +0,0 @@
-"""celery.backends.cache"""
-from datetime import timedelta
-
-from django.utils.encoding import smart_str
-from django.core.cache import cache, get_cache
-from django.core.cache.backends.base import InvalidCacheBackendError
-
-from celery import conf
-from celery.utils.timeutils import timedelta_seconds
-from celery.backends.base import KeyValueStoreBackend
-
-# CELERY_CACHE_BACKEND overrides the django-global(tm) backend settings.
-if conf.CELERY_CACHE_BACKEND:
-    cache = get_cache(conf.CELERY_CACHE_BACKEND)
-
-
-class DjangoMemcacheWrapper(object):
-    """Wrapper class to django's memcache backend class, that overrides the
-    :meth:`get` method in order to remove the forcing of unicode strings
-    since it may cause binary or pickled data to break."""
-
-    def __init__(self, cache):
-        self.cache = cache
-
-    def get(self, key, default=None):
-        val = self.cache._cache.get(smart_str(key))
-        if val is None:
-            return default
-        else:
-            return val
-
-    def set(self, key, value, timeout=0):
-        self.cache.set(key, value, timeout)
-
-# Check if django is using memcache as the cache backend. If so, wrap the
-# cache object in a DjangoMemcacheWrapper that fixes a bug with retrieving
-# pickled data
-from django.core.cache.backends.base import InvalidCacheBackendError
-try:
-    from django.core.cache.backends.memcached import CacheClass
-except InvalidCacheBackendError:
-    pass
-else:
-    if isinstance(cache, CacheClass):
-        cache = DjangoMemcacheWrapper(cache)
-
-
-class CacheBackend(KeyValueStoreBackend):
-    """Backend using the Django cache framework to store task metadata."""
-
-    def __init__(self, *args, **kwargs):
-        super(CacheBackend, self).__init__(self, *args, **kwargs)
-        expires = conf.TASK_RESULT_EXPIRES
-        if isinstance(expires, timedelta):
-            expires = timedelta_seconds(conf.TASK_RESULT_EXPIRES)
-        self.expires = expires
-
-    def get(self, key):
-        return cache.get(key)
-
-    def set(self, key, value):
-        cache.set(key, value, self.expires)

+ 74 - 13
celery/backends/database.py

@@ -1,34 +1,95 @@
-from celery.models import TaskMeta, TaskSetMeta
+import urllib
+from datetime import datetime
+
+
+from celery import conf
+from celery.db.models import Task, TaskSet
+from celery.db.session import ResultSession
 from celery.backends.base import BaseDictBackend
 
 
 class DatabaseBackend(BaseDictBackend):
-    """The database backends. Using Django models to store task metadata."""
+    """The database result backend."""
+
+    def __init__(self, dburi=conf.RESULT_DBURI,
+            engine_options=None, **kwargs):
+        self.dburi = dburi
+        self.engine_options = dict(engine_options or {},
+                                   **conf.RESULT_ENGINE_OPTIONS or {})
+        super(DatabaseBackend, self).__init__(**kwargs)
+
+    def ResultSession(self):
+        return ResultSession(dburi=self.dburi, **self.engine_options)
 
     def _store_result(self, task_id, result, status, traceback=None):
         """Store return value and status of an executed task."""
-        TaskMeta.objects.store_result(task_id, result, status,
-                                      traceback=traceback)
+        session = self.ResultSession()
+        try:
+            tasks = session.query(Task).filter(Task.task_id == task_id).all()
+            if not tasks:
+                task = Task(task_id)
+                session.add(task)
+                session.flush()
+            else:
+                task = tasks[0]
+            task.result = result
+            task.status = status
+            task.traceback = traceback
+            session.commit()
+        finally:
+            session.close()
         return result
 
     def _save_taskset(self, taskset_id, result):
         """Store the result of an executed taskset."""
-        TaskSetMeta.objects.store_result(taskset_id, result)
+        taskset = TaskSet(taskset_id, result)
+        session = self.ResultSession()
+        try:
+            session.add(taskset)
+            session.flush()
+            session.commit()
+        finally:
+            session.close()
         return result
 
     def _get_task_meta_for(self, task_id):
         """Get task metadata for a task by id."""
-        meta = TaskMeta.objects.get_task(task_id)
-        if meta:
-            return meta.to_dict()
+        session = self.ResultSession()
+        try:
+            task = None
+            for task in session.query(Task).filter(Task.task_id == task_id):
+                break
+            if not task:
+                task = Task(task_id)
+                session.add(task)
+                session.flush()
+                session.commit()
+            if task:
+                return task.to_dict()
+        finally:
+            session.close()
 
     def _restore_taskset(self, taskset_id):
         """Get taskset metadata for a taskset by id."""
-        meta = TaskSetMeta.objects.restore_taskset(taskset_id)
-        if meta:
-            return meta.to_dict()
+        session = self.ResultSession()
+        try:
+            qs = session.query(TaskSet)
+            for taskset in qs.filter(TaskSet.task_id == task_id):
+                return taskset.to_dict()
+        finally:
+            session.close()
 
     def cleanup(self):
         """Delete expired metadata."""
-        TaskMeta.objects.delete_expired()
-        TaskSetMeta.objects.delete_expired()
+        expires = conf.TASK_RESULT_EXPIRES
+        session = self.ResultSession()
+        try:
+            for task in session.query(Task).filter(
+                    Task.date_done < (datetime.now() - expires)):
+                session.delete(task)
+            for taskset in session.query(TaskSet).filter(
+                    TaskSet.date_done < (datetime.now() - expires)):
+                session.delete(taskset)
+            session.commit()
+        finally:
+            session.close()

+ 1 - 1
celery/backends/pyredis.py

@@ -25,7 +25,7 @@ class RedisBackend(KeyValueStoreBackend):
         The port to the Redis server.
 
         Raises :class:`celery.exceptions.ImproperlyConfigured` if
-        :setting:`REDIS_HOST` or :setting:`REDIS_PORT` is not set.
+        the ``REDIS_HOST`` or ``REDIS_PORT`` settings is not set.
 
     """
     redis_host = "localhost"

+ 0 - 14
celery/bin/celeryinit.py

@@ -1,14 +0,0 @@
-import sys
-
-
-def main():
-    from celery.loaders.default import Loader
-    loader = Loader()
-    conf = loader.read_configuration()
-    from django.core.management import call_command, setup_environ
-    sys.stderr.write("Creating database tables...\n")
-    setup_environ(conf)
-    call_command("syncdb")
-
-if __name__ == "__main__":
-    main()

+ 11 - 56
celery/conf.py

@@ -72,7 +72,7 @@ _DEFAULTS = {
 
 
 _DEPRECATION_FMT = """
-%s is deprecated in favor of %s and is scheduled for removal in celery v1.2.
+%s is deprecated in favor of %s and is scheduled for removal in celery v1.4.
 """.strip()
 
 def _get(name, default=None, compat=None):
@@ -104,6 +104,11 @@ ACKS_LATE = _get("CELERY_ACKS_LATE")
 if isinstance(TASK_RESULT_EXPIRES, int):
     TASK_RESULT_EXPIRES = timedelta(seconds=TASK_RESULT_EXPIRES)
 
+# <--- SQLAlchemy                                  <-   --   --- - ----- -- #
+RESULT_DBURI = _get("CELERY_RESULT_DBURI")
+RESULT_ENGINE_OPTIONS = _get("CELERY_RESULT_ENGINE_OPTIONS")
+
+
 # <--- Client                                      <-   --   --- - ----- -- #
 
 MAX_CACHED_RESULTS = _get("CELERY_MAX_CACHED_RESULTS")
@@ -122,7 +127,7 @@ CELERYD_LOG_FORMAT = _get("CELERYD_LOG_FORMAT",
 CELERYD_TASK_LOG_FORMAT = _get("CELERYD_TASK_LOG_FORMAT")
 CELERYD_LOG_FILE = _get("CELERYD_LOG_FILE")
 CELERYD_LOG_LEVEL = _get("CELERYD_LOG_LEVEL",
-                        compat=["CELERYD_DAEMON_LOG_LEVEL"])
+                            compat=["CELERYD_DAEMON_LOG_LEVEL"])
 CELERYD_LOG_LEVEL = LOG_LEVELS[CELERYD_LOG_LEVEL.upper()]
 CELERYD_CONCURRENCY = _get("CELERYD_CONCURRENCY")
 CELERYD_PREFETCH_MULTIPLIER = _get("CELERYD_PREFETCH_MULTIPLIER")
@@ -133,65 +138,15 @@ CELERYD_MEDIATOR = _get("CELERYD_MEDIATOR")
 CELERYD_ETA_SCHEDULER = _get("CELERYD_ETA_SCHEDULER")
 
 # <--- Message routing                             <-   --   --- - ----- -- #
-QUEUES = _get("CELERY_QUEUES")
 DEFAULT_QUEUE = _get("CELERY_DEFAULT_QUEUE")
 DEFAULT_ROUTING_KEY = _get("CELERY_DEFAULT_ROUTING_KEY")
 DEFAULT_EXCHANGE = _get("CELERY_DEFAULT_EXCHANGE")
 DEFAULT_EXCHANGE_TYPE = _get("CELERY_DEFAULT_EXCHANGE_TYPE")
 DEFAULT_DELIVERY_MODE = _get("CELERY_DEFAULT_DELIVERY_MODE")
-
-_DEPRECATIONS = {"CELERY_AMQP_CONSUMER_QUEUES": "CELERY_QUEUES",
-                 "CELERY_AMQP_CONSUMER_QUEUE": "CELERY_QUEUES",
-                 "CELERY_AMQP_EXCHANGE": "CELERY_DEFAULT_EXCHANGE",
-                 "CELERY_AMQP_EXCHANGE_TYPE": "CELERY_DEFAULT_EXCHANGE_TYPE",
-                 "CELERY_AMQP_CONSUMER_ROUTING_KEY": "CELERY_QUEUES",
-                 "CELERY_AMQP_PUBLISHER_ROUTING_KEY":
-                 "CELERY_DEFAULT_ROUTING_KEY"}
-
-
-_DEPRECATED_QUEUE_SETTING_FMT = """
-%s is deprecated in favor of %s and scheduled for removal in celery v1.0.
-Please visit http://bit.ly/5DsSuX for more information.
-
-We're sorry for the inconvenience.
-""".strip()
-
-
-def _find_deprecated_queue_settings():
-    global DEFAULT_QUEUE, DEFAULT_ROUTING_KEY
-    global DEFAULT_EXCHANGE, DEFAULT_EXCHANGE_TYPE
-    binding_key = None
-
-    multi = _get("CELERY_AMQP_CONSUMER_QUEUES")
-    if multi:
-        return multi
-
-    single = _get("CELERY_AMQP_CONSUMER_QUEUE")
-    if single:
-        DEFAULT_QUEUE = single
-        DEFAULT_EXCHANGE = _get("CELERY_AMQP_EXCHANGE", DEFAULT_EXCHANGE)
-        DEFAULT_EXCHANGE_TYPE = _get("CELERY_AMQP_EXCHANGE_TYPE",
-                                     DEFAULT_EXCHANGE_TYPE)
-        binding_key = _get("CELERY_AMQP_CONSUMER_ROUTING_KEY",
-                            DEFAULT_ROUTING_KEY)
-        DEFAULT_ROUTING_KEY = _get("CELERY_AMQP_PUBLISHER_ROUTING_KEY",
-                                   DEFAULT_ROUTING_KEY)
-    binding_key = binding_key or DEFAULT_ROUTING_KEY
-    return {DEFAULT_QUEUE: {"exchange": DEFAULT_EXCHANGE,
-                            "exchange_type": DEFAULT_EXCHANGE_TYPE,
-                            "binding_key": binding_key}}
-
-
-def _warn_if_deprecated_queue_settings():
-    for setting, new_setting in _DEPRECATIONS.items():
-        if _get(setting):
-            warnings.warn(DeprecationWarning(_DEPRECATED_QUEUE_SETTING_FMT % (
-                setting, _DEPRECATIONS[setting])))
-            break
-
-_warn_if_deprecated_queue_settings()
-if not QUEUES:
-    QUEUES = _find_deprecated_queue_settings()
+QUEUES = _get("CELERY_QUEUES") or {DEFAULT_QUEUE: {
+                                       "exchange": DEFAULT_EXCHANGE,
+                                       "exchange_type": DEFAULT_EXCHANGE_TYPE,
+                                       "binding_key": DEFAULT_ROUTING_KEY}}
 
 # :--- Broadcast queue settings                     <-   --   --- - ----- -- #
 

+ 0 - 19
celery/contrib/test_runner.py

@@ -1,19 +0,0 @@
-from django.conf import settings
-from django.test.simple import run_tests as run_tests_orig
-
-USAGE = """\
-Custom test runner to allow testing of celery delayed tasks.
-"""
-
-def run_tests(test_labels, *args, **kwargs):
-    """Django test runner allowing testing of celery delayed tasks.
-
-    All tasks are run locally, not in a worker.
-
-    To use this runner set ``settings.TEST_RUNNER``::
-
-        TEST_RUNNER = "celery.contrib.test_runner.run_tests"
-
-    """
-    settings.CELERY_ALWAYS_EAGER = True
-    return run_tests_orig(test_labels, *args, **kwargs)

+ 0 - 0
celery/management/__init__.py → celery/db/__init__.py


+ 70 - 0
celery/db/models.py

@@ -0,0 +1,70 @@
+from datetime import datetime
+
+from sqlalchemy import Column, Sequence, ForeignKey
+from sqlalchemy import Integer, String, Text, DateTime, PickleType
+from sqlalchemy.orm import relation
+
+from celery import states
+from celery.db.session import ResultModelBase
+
+
+
+class Task(ResultModelBase):
+    """Task result/status."""
+    __tablename__ = "celery_taskmeta"
+    __table_args__ = {"sqlite_autoincrement": True}
+
+    id = Column("id", Integer, Sequence("task_id_sequence"), primary_key=True,
+            autoincrement=True)
+    task_id = Column("task_id", String(255))
+    status = Column("status", String(50), default=states.PENDING)
+    result = Column("result", PickleType, nullable=True)
+    date_done = Column("date_done", DateTime, default=datetime.now,
+                       onupdate=datetime.now, nullable=True)
+    traceback = Column("traceback", Text, nullable=True)
+
+    def __init__(self, task_id):
+        self.task_id = task_id
+
+    def __str__(self):
+        return "<Task(%s, %s, %s, %s)>" % (self.task_id,
+                                           self.result,
+                                           self.status,
+                                           self.traceback)
+
+    def to_dict(self):
+        return {"task_id": self.task_id,
+                "status": self.status,
+                "result": self.result,
+                "date_done": self.date_done,
+                "traceback": self.traceback}
+
+    def __unicode__(self):
+        return u"<Task: %s successful: %s>" % (self.task_id, self.status)
+
+
+class TaskSet(ResultModelBase):
+    """TaskSet result"""
+    __tablename__ = "celery_tasksetmeta"
+    __table_args__ = {"sqlite_autoincrement": True}
+
+    id = Column("id", Integer, Sequence("taskset_id_sequence"),
+                autoincrement=True, primary_key=True)
+    taskset_id = Column("taskset_id", String(255))
+    result = Column("result", PickleType, nullable=True)
+    date_done = Column("date_done", DateTime, default=datetime.now,
+                       nullable=True)
+
+    def __init__(self, task_id):
+        self.task_id = task_id
+
+    def __str__(self):
+        return "<TaskSet(%s, %s)>" % (self.task_id, self.result)
+
+    def to_dict(self):
+        return {"taskset_id": self.taskset_id,
+                "result": self.result,
+                "date_done": self.date_done}
+
+    def __unicode__(self):
+        return u"<TaskSet: %s>" % (self.taskset_id)

+ 36 - 0
celery/db/session.py

@@ -0,0 +1,36 @@
+import os
+
+from sqlalchemy import create_engine
+from sqlalchemy.orm import sessionmaker
+from sqlalchemy.ext.declarative import declarative_base
+
+from celery import conf
+
+ResultModelBase = declarative_base()
+
+_SETUP = {"results": False}
+_ENGINES = {}
+
+
+def get_engine(dburi, **kwargs):
+    if dburi not in _ENGINES:
+        _ENGINES[dburi] = create_engine(dburi, **kwargs)
+    return _ENGINES[dburi]
+
+
+def create_session(dburi, **kwargs):
+    engine = get_engine(dburi, **kwargs)
+    return engine, sessionmaker(bind=engine)
+
+
+def setup_results(engine):
+    if not _SETUP["results"]:
+        ResultModelBase.metadata.create_all(engine)
+        _SETUP["results"] = True
+
+
+def ResultSession(dburi=conf.RESULT_DBURI, **kwargs):
+    engine, session = create_session(dburi, **kwargs)
+    if os.environ.get("CELERYINIT"):
+        setup_results(engine)
+    return session()

+ 7 - 81
celery/loaders/__init__.py

@@ -1,101 +1,27 @@
 import os
-import warnings
-import importlib
 
-from carrot.utils import rpartition
+from celery.utils import get_cls_by_name
 
-from celery.utils import get_full_cls_name, first
-from celery.loaders.default import Loader as DefaultLoader
-from celery.loaders.djangoapp import Loader as DjangoLoader
-
-_DEFAULT_LOADER_CLASS_NAME = "Loader"
-LOADER_ALIASES = {"django": "celery.loaders.djangoapp.Loader",
-                  "default": "celery.loaders.default.Loader"}
-_loader_cache = {}
+LOADER_ALIASES = {"default": "celery.loaders.default.Loader",
+                  "django": "djcelery.loaders.DjangoLoader"}
 _loader = None
 _settings = None
 
 
-def resolve_loader(loader):
-    loader = LOADER_ALIASES.get(loader, loader)
-    loader_module_name, _, loader_cls_name = rpartition(loader, ".")
-
-    # For backward compatibility, try to detect a valid loader name by
-    # ensuring the first letter in the name is uppercase.
-    # e.g. both module.Foo and module.__Foo is valid, but not module.foo.
-    if not first(str.isalpha, loader_cls_name).isupper():
-        warnings.warn(DeprecationWarning(
-            "CELERY_LOADER now needs loader class name, e.g. %s.%s" % (
-                loader, _DEFAULT_LOADER_CLASS_NAME)))
-        return loader, _DEFAULT_LOADER_CLASS_NAME
-    return loader_module_name, loader_cls_name
-
-
-def _get_loader_cls(loader):
-    loader_module_name, loader_cls_name = resolve_loader(loader)
-    loader_module = importlib.import_module(loader_module_name)
-    return getattr(loader_module, loader_cls_name)
-
-
 def get_loader_cls(loader):
     """Get loader class by name/alias"""
-    if loader not in _loader_cache:
-        _loader_cache[loader] = _get_loader_cls(loader)
-    return _loader_cache[loader]
-
-
-def detect_loader():
-    loader = os.environ.get("CELERY_LOADER")
-    if loader:
-        return get_loader_cls(loader)
-
-    loader = _detect_loader()
-    os.environ["CELERY_LOADER"] = get_full_cls_name(loader)
-
-    return loader
-
+    return get_cls_by_name(loader, LOADER_ALIASES)
 
-def _detect_loader(): # pragma: no cover
-    from django.conf import settings
-    if settings.configured:
-        return DjangoLoader
-    try:
-        # A settings module may be defined, but Django didn't attempt to
-        # load it yet. As an alternative to calling the private _setup(),
-        # we could also check whether DJANGO_SETTINGS_MODULE is set.
-        settings._setup()
-    except ImportError:
-        if not callable(getattr(os, "fork", None)):
-            # Platform doesn't support fork()
-            # XXX On systems without fork, multiprocessing seems to be
-            # launching the processes in some other way which does
-            # not copy the memory of the parent process. This means
-            # any configured env might be lost. This is a hack to make
-            # it work on Windows.
-            # A better way might be to use os.environ to set the currently
-            # used configuration method so to propogate it to the "child"
-            # processes. But this has to be experimented with.
-            # [asksol/heyman]
-            from django.core.management import setup_environ
-            try:
-                settings_mod = os.environ.get("DJANGO_SETTINGS_MODULE",
-                                                "settings")
-                project_settings = __import__(settings_mod, {}, {}, [''])
-                setup_environ(project_settings)
-                return DjangoLoader
-            except ImportError:
-                pass
-    else:
-        return DjangoLoader
 
-    return DefaultLoader
+def setup_loader():
+    return get_loader_cls(os.environ.setdefault("CELERY_LOADER", "default"))()
 
 
 def current_loader():
     """Detect and return the current loader."""
     global _loader
     if _loader is None:
-        _loader = detect_loader()()
+        _loader = setup_loader()
     return _loader
 
 

+ 1 - 0
celery/loaders/base.py

@@ -19,6 +19,7 @@ class BaseLoader(object):
     """
     _conf_cache = None
     worker_initialized = False
+    override_backends = {}
 
     def on_task_init(self, task_id, task):
         """This method is called before a task is executed."""

+ 19 - 10
celery/loaders/default.py

@@ -1,4 +1,5 @@
 import os
+from importlib import import_module
 
 from celery.loaders.base import BaseLoader
 
@@ -6,9 +7,11 @@ DEFAULT_CONFIG_MODULE = "celeryconfig"
 
 DEFAULT_SETTINGS = {
     "DEBUG": False,
+    "ADMINS": (),
     "DATABASE_ENGINE": "sqlite3",
     "DATABASE_NAME": "celery.sqlite",
     "INSTALLED_APPS": ("celery", ),
+    "CELERY_IMPORTS": (),
 }
 
 
@@ -16,6 +19,18 @@ def wanted_module_item(item):
     return not item.startswith("_")
 
 
+class Settings(dict):
+
+    def __getattr__(self, key):
+        try:
+            return self[key]
+        except KeyError:
+            raise AttributeError(key)
+
+    def __setattr_(self, key, value):
+        self[key] = value
+
+
 class Loader(BaseLoader):
     """The default loader.
 
@@ -23,14 +38,8 @@ class Loader(BaseLoader):
 
     """
 
-    def setup_django_env(self, settingsdict):
-        config = dict(DEFAULT_SETTINGS, **settingsdict)
-
-        from django.conf import settings
-        if not settings.configured:
-            settings.configure()
-        for config_key, config_value in config.items():
-            setattr(settings, config_key, config_value)
+    def setup_settings(self, settingsdict):
+        settings = Settings(DEFAULT_SETTINGS, **settingsdict)
         installed_apps = set(list(DEFAULT_SETTINGS["INSTALLED_APPS"]) + \
                              list(settings.INSTALLED_APPS))
         settings.INSTALLED_APPS = tuple(installed_apps)
@@ -42,11 +51,11 @@ class Loader(BaseLoader):
         celery and Django so it can be used by regular Python."""
         configname = os.environ.get("CELERY_CONFIG_MODULE",
                                     DEFAULT_CONFIG_MODULE)
-        celeryconfig = __import__(configname, {}, {}, [''])
+        celeryconfig = import_module(configname)
         usercfg = dict((key, getattr(celeryconfig, key))
                             for key in dir(celeryconfig)
                                 if wanted_module_item(key))
-        return self.setup_django_env(usercfg)
+        return self.setup_settings(usercfg)
 
     def on_worker_init(self):
         """Imports modules at worker init so tasks can be registered

+ 0 - 100
celery/loaders/djangoapp.py

@@ -1,100 +0,0 @@
-import imp
-import importlib
-
-from celery.loaders.base import BaseLoader
-
-_RACE_PROTECTION = False
-
-
-class Loader(BaseLoader):
-    """The Django loader."""
-    _db_reuse = 0
-
-    def read_configuration(self):
-        """Load configuration from Django settings."""
-        from django.conf import settings
-        return settings
-
-    def close_database(self):
-        from django.db import connection
-        db_reuse_max = getattr(self.conf, "CELERY_DB_REUSE_MAX", None)
-        if not db_reuse_max:
-            return connection.close()
-        if self._db_reuse >= db_reuse_max:
-            self._db_reuse = 0
-            return connection.close()
-        self._db_reuse += 1
-
-    def on_task_init(self, task_id, task):
-        """This method is called before a task is executed.
-
-        Does everything necessary for Django to work in a long-living,
-        multiprocessing environment.
-
-        """
-        # See http://groups.google.com/group/django-users/
-        #            browse_thread/thread/78200863d0c07c6d/
-        self.close_database()
-
-        # ## Reset cache connection only if using memcached/libmemcached
-        from django.core import cache
-        # XXX At Opera we use a custom memcached backend that uses
-        # libmemcached instead of libmemcache (cmemcache). Should find a
-        # better solution for this, but for now "memcached" should probably
-        # be unique enough of a string to not make problems.
-        cache_backend = cache.settings.CACHE_BACKEND
-        try:
-            parse_backend = cache.parse_backend_uri
-        except AttributeError:
-            parse_backend = lambda backend: backend.split(":", 1)
-        cache_scheme = parse_backend(cache_backend)[0]
-
-        if "memcached" in cache_scheme:
-            cache.cache.close()
-
-    def on_worker_init(self):
-        """Called when the worker starts.
-
-        Automatically discovers any ``tasks.py`` files in the applications
-        listed in ``INSTALLED_APPS``.
-
-        """
-        self.import_default_modules()
-        autodiscover()
-
-
-def autodiscover():
-    """Include tasks for all applications in :setting:`INSTALLED_APPS`."""
-    from django.conf import settings
-    global _RACE_PROTECTION
-
-    if _RACE_PROTECTION:
-        return
-    _RACE_PROTECTION = True
-    try:
-        return filter(None, [find_related_module(app, "tasks")
-                                for app in settings.INSTALLED_APPS])
-    finally:
-        _RACE_PROTECTION = False
-
-
-def find_related_module(app, related_name):
-    """Given an application name and a module name, tries to find that
-    module in the application."""
-
-    try:
-        app_path = importlib.import_module(app).__path__
-    except AttributeError:
-        return
-
-    try:
-        imp.find_module(related_name, app_path)
-    except ImportError:
-        return
-
-    module = importlib.import_module("%s.%s" % (app, related_name))
-
-    try:
-        return getattr(module, related_name)
-    except AttributeError:
-        return

+ 0 - 0
celery/management/commands/__init__.py


+ 0 - 18
celery/management/commands/camqadm.py

@@ -1,18 +0,0 @@
-"""
-
-Celery AMQP Administration Tool using the AMQP API.
-
-"""
-from django.core.management.base import BaseCommand
-
-from celery.bin.camqadm import camqadm, OPTION_LIST
-
-
-class Command(BaseCommand):
-    """Run the celery daemon."""
-    option_list = BaseCommand.option_list + OPTION_LIST
-    help = 'Celery AMQP Administration Tool using the AMQP API.'
-
-    def handle(self, *args, **options):
-        """Handle the management command."""
-        camqadm(*args, **options)

+ 0 - 18
celery/management/commands/celerybeat.py

@@ -1,18 +0,0 @@
-"""
-
-Start the celery clock service from the Django management command.
-
-"""
-from django.core.management.base import BaseCommand
-
-from celery.bin.celerybeat import run_clockservice, OPTION_LIST
-
-
-class Command(BaseCommand):
-    """Run the celery periodic task scheduler."""
-    option_list = BaseCommand.option_list + OPTION_LIST
-    help = 'Run the celery periodic task scheduler'
-
-    def handle(self, *args, **options):
-        """Handle the management command."""
-        run_clockservice(**options)

+ 0 - 18
celery/management/commands/celeryd.py

@@ -1,18 +0,0 @@
-"""
-
-Start the celery daemon from the Django management command.
-
-"""
-from django.core.management.base import BaseCommand
-
-from celery.bin.celeryd import run_worker, OPTION_LIST
-
-
-class Command(BaseCommand):
-    """Run the celery daemon."""
-    option_list = BaseCommand.option_list + OPTION_LIST
-    help = 'Run the celery daemon'
-
-    def handle(self, *args, **options):
-        """Handle the management command."""
-        run_worker(**options)

+ 0 - 37
celery/management/commands/celerymon.py

@@ -1,37 +0,0 @@
-"""
-
-Start the celery clock service from the Django management command.
-
-"""
-import sys
-from django.core.management.base import BaseCommand
-
-#try:
-from celerymonitor.bin.celerymond import run_monitor, OPTION_LIST
-#except ImportError:
-#    OPTION_LIST = ()
-#    run_monitor = None
-
-MISSING = """
-You don't have celerymon installed, please install it by running the following
-command:
-
-    $ easy_install celerymon
-
-or if you're using pip (like you should be):
-
-    $ pip install celerymon
-"""
-
-
-class Command(BaseCommand):
-    """Run the celery monitor."""
-    option_list = BaseCommand.option_list + OPTION_LIST
-    help = 'Run the celery monitor'
-
-    def handle(self, *args, **options):
-        """Handle the management command."""
-        if run_monitor is None:
-            sys.stderr.write(MISSING)
-        else:
-            run_monitor(**options)

+ 0 - 149
celery/managers.py

@@ -1,149 +0,0 @@
-from datetime import datetime
-from itertools import count
-
-from billiard.utils.functional import wraps
-
-from django.db import models
-from django.db import transaction
-from django.db.models.query import QuerySet
-
-
-def transaction_retry(max_retries=1):
-    """Decorator for methods doing database operations.
-
-    If the database operation fails, it will retry the operation
-    at most ``max_retries`` times.
-
-    """
-    def _outer(fun):
-
-        @wraps(fun)
-        def _inner(*args, **kwargs):
-            _max_retries = kwargs.pop("exception_retry_count", max_retries)
-            for retries in count(0):
-                try:
-                    return fun(*args, **kwargs)
-                except Exception: # pragma: no cover
-                    # Depending on the database backend used we can experience
-                    # various exceptions. E.g. psycopg2 raises an exception
-                    # if some operation breaks the transaction, so saving
-                    # the task result won't be possible until we rollback
-                    # the transaction.
-                    if retries >= _max_retries:
-                        raise
-                    transaction.rollback_unless_managed()
-
-        return _inner
-
-    return _outer
-
-
-def update_model_with_dict(obj, fields):
-    [setattr(obj, attr_name, attr_value)
-        for attr_name, attr_value in fields.items()]
-    obj.save()
-    return obj
-
-
-class ExtendedQuerySet(QuerySet):
-
-    def update_or_create(self, **kwargs):
-        obj, created = self.get_or_create(**kwargs)
-
-        if not created:
-            fields = dict(kwargs.pop("defaults", {}))
-            fields.update(kwargs)
-            update_model_with_dict(obj, fields)
-
-        return obj
-
-
-class ExtendedManager(models.Manager):
-
-    def get_query_set(self):
-        return ExtendedQuerySet(self.model)
-
-    def update_or_create(self, **kwargs):
-        return self.get_query_set().update_or_create(**kwargs)
-
-
-class ResultManager(ExtendedManager):
-
-    def get_all_expired(self):
-        """Get all expired task results."""
-        from celery import conf
-        expires = conf.TASK_RESULT_EXPIRES
-        return self.filter(date_done__lt=datetime.now() - expires)
-
-    def delete_expired(self):
-        """Delete all expired taskset results."""
-        self.get_all_expired().delete()
-
-
-class TaskManager(ResultManager):
-    """Manager for :class:`celery.models.Task` models."""
-
-    @transaction_retry(max_retries=1)
-    def get_task(self, task_id):
-        """Get task meta for task by ``task_id``.
-
-        :keyword exception_retry_count: How many times to retry by
-            transaction rollback on exception. This could theoretically
-            happen in a race condition if another worker is trying to
-            create the same task. The default is to retry once.
-
-        """
-        task, created = self.get_or_create(task_id=task_id)
-        return task
-
-    @transaction_retry(max_retries=2)
-    def store_result(self, task_id, result, status, traceback=None):
-        """Store the result and status of a task.
-
-        :param task_id: task id
-
-        :param result: The return value of the task, or an exception
-            instance raised by the task.
-
-        :param status: Task status. See
-            :meth:`celery.result.AsyncResult.get_status` for a list of
-            possible status values.
-
-        :keyword traceback: The traceback at the point of exception (if the
-            task failed).
-
-        :keyword exception_retry_count: How many times to retry by
-            transaction rollback on exception. This could theoretically
-            happen in a race condition if another worker is trying to
-            create the same task. The default is to retry twice.
-
-        """
-        return self.update_or_create(task_id=task_id, defaults={
-                                        "status": status,
-                                        "result": result,
-                                        "traceback": traceback})
-
-
-class TaskSetManager(ResultManager):
-    """Manager for :class:`celery.models.TaskSet` models."""
-
-
-    @transaction_retry(max_retries=1)
-    def restore_taskset(self, taskset_id):
-        """Get taskset meta for task by ``taskset_id``."""
-        try:
-            return self.get(taskset_id=taskset_id)
-        except self.model.DoesNotExist:
-            return None
-
-    @transaction_retry(max_retries=2)
-    def store_result(self, taskset_id, result):
-        """Store the result of a taskset.
-
-        :param taskset_id: task set id
-
-        :param result: The return value of the taskset
-
-        """
-        return self.update_or_create(taskset_id=taskset_id,
-                                     defaults={"result": result})

+ 3 - 1
celery/messaging.py

@@ -14,6 +14,7 @@ from billiard.utils.functional import wraps
 from celery import conf
 from celery import signals
 from celery.utils import gen_unique_id, mitemgetter, noop
+from celery.loaders import load_settings
 
 
 MSG_OPTIONS = ("mandatory", "priority",
@@ -209,7 +210,8 @@ class BroadcastConsumer(Consumer):
 
 def establish_connection(connect_timeout=conf.BROKER_CONNECTION_TIMEOUT):
     """Establish a connection to the message broker."""
-    return DjangoBrokerConnection(connect_timeout=connect_timeout)
+    return DjangoBrokerConnection(connect_timeout=connect_timeout,
+                                  settings=load_settings())
 
 
 def with_connection(fun):

+ 27 - 51
celery/models.py

@@ -1,67 +1,43 @@
-import django
-from django.db import models
-from django.utils.translation import ugettext_lazy as _
+"""
 
-from picklefield.fields import PickledObjectField
+celery.models has been moved to djcelery.models.
 
-from celery import conf
-from celery import states
-from celery.managers import TaskManager, TaskSetManager
+This file is deprecated and will be removed in Celery v1.4.0.
 
-TASK_STATUSES_CHOICES = zip(states.ALL_STATES, states.ALL_STATES)
+"""
+from django.core.exceptions import ImproperlyConfigured
 
+raise ImproperlyConfigured("""
 
-class TaskMeta(models.Model):
-    """Task result/status."""
-    task_id = models.CharField(_(u"task id"), max_length=255, unique=True)
-    status = models.CharField(_(u"task status"), max_length=50,
-            default=states.PENDING, choices=TASK_STATUSES_CHOICES)
-    result = PickledObjectField(null=True, default=None)
-    date_done = models.DateTimeField(_(u"done at"), auto_now=True)
-    traceback = models.TextField(_(u"traceback"), blank=True, null=True)
+======================================================
+ERROR: celery can't be added to INSTALLED_APPS anymore
+======================================================
 
-    objects = TaskManager()
+Please install the django-celery package and add:
 
-    class Meta:
-        """Model meta-data."""
-        verbose_name = _(u"task meta")
-        verbose_name_plural = _(u"task meta")
+    INSTALLED_APPS = "djcelery"
 
-    def to_dict(self):
-        return {"task_id": self.task_id,
-                "status": self.status,
-                "result": self.result,
-                "date_done": self.date_done,
-                "traceback": self.traceback}
+To install django-celery you can do one of the following:
 
-    def __unicode__(self):
-        return u"<Task: %s state->%s>" % (self.task_id, self.status)
+* Download from PyPI:
 
+    http://pypi.python.org/pypi/django-celery
 
-class TaskSetMeta(models.Model):
-    """TaskSet result"""
-    taskset_id = models.CharField(_(u"task id"), max_length=255, unique=True)
-    result = PickledObjectField()
-    date_done = models.DateTimeField(_(u"done at"), auto_now=True)
+* Install with pip:
 
-    objects = TaskSetManager()
+    pip install django-celery
 
-    class Meta:
-        """Model meta-data."""
-        verbose_name = _(u"taskset meta")
-        verbose_name_plural = _(u"taskset meta")
+* Install with easy_install:
 
-    def to_dict(self):
-        return {"taskset_id": self.taskset_id,
-                "result": self.result,
-                "date_done": self.date_done}
+    easy_install django-celery
 
-    def __unicode__(self):
-        return u"<TaskSet: %s>" % (self.taskset_id)
+* Clone the development repository:
 
-if (django.VERSION[0], django.VERSION[1]) >= (1, 1):
-    # keep models away from syncdb/reset if database backend is not
-    # being used.
-    if conf.RESULT_BACKEND != 'database':
-        TaskMeta._meta.managed = False
-        TaskSetMeta._meta.managed = False
+    http://github.com/ask/django-celery
+
+
+If you weren't aware of this already you should read the
+Celery 1.2.0 Changelog as well:
+    http://github.com/ask/celery/tree/djangofree/Changelog
+
+""")

+ 2 - 2
celery/result.py

@@ -176,8 +176,8 @@ class TaskSetResult(object):
     """Working with :class:`celery.task.TaskSet` results.
 
     An instance of this class is returned by
-    :meth:`celery.task.TaskSet.run()`. It lets you inspect the status and
-    return values of the taskset as a single entity.
+    :meth:`celery.task.TaskSet.apply_async()`. It lets you inspect the
+    status and return values of the taskset as a single entity.
 
     :option taskset_id: see :attr:`taskset_id`.
     :option subtasks: see :attr:`subtasks`.

+ 1 - 1
celery/signals.py

@@ -1,4 +1,4 @@
-from django.dispatch import Signal
+from celery.utils.dispatch import Signal
 
 task_sent = Signal(providing_args=["task_id", "task",
                                    "args", "kwargs",

+ 1 - 8
celery/task/base.py

@@ -1,5 +1,4 @@
 import sys
-import warnings
 from datetime import timedelta
 
 from billiard.serialization import pickle
@@ -111,6 +110,7 @@ class Task(object):
         can't be routed to a worker immediately.
 
     .. attribute:: priority:
+
         The message priority. A number from ``0`` to ``9``, where ``0`` is the
         highest. Note that RabbitMQ doesn't support priorities yet.
 
@@ -562,13 +562,6 @@ class TaskSet(object):
         self.arguments = args
         self.total = len(args)
 
-    def run(self, *args, **kwargs):
-        """Deprecated alias to :meth:`apply_async`"""
-        warnings.warn(DeprecationWarning(
-            "TaskSet.run will be deprecated in favor of TaskSet.apply_async "
-            "in celery v1.2.0"))
-        return self.apply_async(*args, **kwargs)
-
     def apply_async(self, connect_timeout=conf.BROKER_CONNECTION_TIMEOUT):
         """Run all tasks in the taskset.
 

+ 0 - 19
celery/task/rest.py

@@ -1,19 +0,0 @@
-from celery.task.http import (InvalidResponseError, RemoteExecuteError,
-                              UnknownStatusError)
-from celery.task.http import URL
-from celery.task.http import HttpDispatch as RESTProxy
-from celery.task.http import HttpDispatchTask as RESTProxyTask
-
-import warnings
-warnings.warn(DeprecationWarning(
-"""celery.task.rest has been deprecated and is scheduled for removal in
-v1.2. Please use celery.task.http instead.
-
-The following objects has been renamed:
-
-    celery.task.rest.RESTProxy -> celery.task.http.HttpDispatch
-    celery.task.rest.RESTProxyTask -> celery.task.http.HttpDispatchTask
-
-Other objects have the same name, just moved to the celery.task.http module.
-
-"""))

+ 0 - 19
celery/tests/runners.py

@@ -1,19 +0,0 @@
-from django.conf import settings
-from django.test.simple import run_tests as django_test_runner
-
-
-def run_tests(test_labels, verbosity=1, interactive=True, extra_tests=None,
-        **kwargs):
-    """ Test runner that only runs tests for the apps
-    listed in ``settings.TEST_APPS``.
-    """
-    extra_tests = extra_tests or []
-    app_labels = getattr(settings, "TEST_APPS", test_labels)
-
-    # Seems to be deleting the test database file twice :(
-    from celery.utils import noop
-    from django.db import connection
-    connection.creation.destroy_test_db = noop
-    return django_test_runner(app_labels,
-                              verbosity=verbosity, interactive=interactive,
-                              extra_tests=extra_tests, **kwargs)

+ 0 - 3
celery/tests/test_backends/__init__.py

@@ -1,7 +1,6 @@
 import unittest2 as unittest
 
 from celery import backends
-from celery.backends.database import DatabaseBackend
 from celery.backends.amqp import AMQPBackend
 from celery.backends.pyredis import RedisBackend
 
@@ -10,8 +9,6 @@ class TestBackends(unittest.TestCase):
 
     def test_get_backend_aliases(self):
         expects = [("amqp", AMQPBackend),
-                   ("database", DatabaseBackend),
-                   ("db", DatabaseBackend),
                    ("redis", RedisBackend)]
         for expect_name, expect_cls in expects:
             self.assertIsInstance(backends.get_backend_cls(expect_name)(),

+ 0 - 127
celery/tests/test_backends/test_cache.py

@@ -1,127 +0,0 @@
-import sys
-import unittest2 as unittest
-
-from billiard.serialization import pickle
-from django.core.cache.backends.base import InvalidCacheBackendError
-
-from celery import result
-from celery import states
-from celery.utils import gen_unique_id
-from celery.backends.cache import CacheBackend
-from celery.datastructures import ExceptionInfo
-
-
-class SomeClass(object):
-
-    def __init__(self, data):
-        self.data = data
-
-
-class TestCacheBackend(unittest.TestCase):
-
-    def test_mark_as_done(self):
-        cb = CacheBackend()
-
-        tid = gen_unique_id()
-
-        self.assertFalse(cb.is_successful(tid))
-        self.assertEqual(cb.get_status(tid), states.PENDING)
-        self.assertIsNone(cb.get_result(tid))
-
-        cb.mark_as_done(tid, 42)
-        self.assertTrue(cb.is_successful(tid))
-        self.assertEqual(cb.get_status(tid), states.SUCCESS)
-        self.assertEqual(cb.get_result(tid), 42)
-        self.assertTrue(cb.get_result(tid), 42)
-
-    def test_save_restore_taskset(self):
-        backend = CacheBackend()
-        taskset_id = gen_unique_id()
-        subtask_ids = [gen_unique_id() for i in range(10)]
-        subtasks = map(result.AsyncResult, subtask_ids)
-        res = result.TaskSetResult(taskset_id, subtasks)
-        res.save(backend=backend)
-        saved = result.TaskSetResult.restore(taskset_id, backend=backend)
-        self.assertListEqual(saved.subtasks, subtasks)
-        self.assertEqual(saved.taskset_id, taskset_id)
-
-    def test_is_pickled(self):
-        cb = CacheBackend()
-
-        tid2 = gen_unique_id()
-        result = {"foo": "baz", "bar": SomeClass(12345)}
-        cb.mark_as_done(tid2, result)
-        # is serialized properly.
-        rindb = cb.get_result(tid2)
-        self.assertEqual(rindb.get("foo"), "baz")
-        self.assertEqual(rindb.get("bar").data, 12345)
-
-    def test_mark_as_failure(self):
-        cb = CacheBackend()
-
-        einfo = None
-        tid3 = gen_unique_id()
-        try:
-            raise KeyError("foo")
-        except KeyError, exception:
-            einfo = ExceptionInfo(sys.exc_info())
-            pass
-        cb.mark_as_failure(tid3, exception, traceback=einfo.traceback)
-        self.assertFalse(cb.is_successful(tid3))
-        self.assertEqual(cb.get_status(tid3), states.FAILURE)
-        self.assertIsInstance(cb.get_result(tid3), KeyError)
-        self.assertEqual(cb.get_traceback(tid3), einfo.traceback)
-
-    def test_process_cleanup(self):
-        cb = CacheBackend()
-        cb.process_cleanup()
-
-
-class TestCustomCacheBackend(unittest.TestCase):
-
-    def test_custom_cache_backend(self):
-        from celery import conf
-        prev_backend = conf.CELERY_CACHE_BACKEND
-        prev_module = sys.modules["celery.backends.cache"]
-        conf.CELERY_CACHE_BACKEND = "dummy://"
-        sys.modules.pop("celery.backends.cache")
-        try:
-            from celery.backends.cache import cache
-            from django.core.cache import cache as django_cache
-            self.assertEqual(cache.__class__.__module__,
-                              "django.core.cache.backends.dummy")
-            self.assertIsNot(cache, django_cache)
-        finally:
-            conf.CELERY_CACHE_BACKEND = prev_backend
-            sys.modules["celery.backends.cache"] = prev_module
-
-
-class TestMemcacheWrapper(unittest.TestCase):
-
-    def test_memcache_wrapper(self):
-
-        try:
-            from django.core.cache.backends import memcached
-            from django.core.cache.backends import locmem
-        except InvalidCacheBackendError:
-            sys.stderr.write(
-                "\n* Memcache library is not installed. Skipping test.\n")
-            return
-        prev_cache_cls = memcached.CacheClass
-        memcached.CacheClass = locmem.CacheClass
-        prev_backend_module = sys.modules.pop("celery.backends.cache")
-        try:
-            from celery.backends.cache import cache, DjangoMemcacheWrapper
-            self.assertIsInstance(cache, DjangoMemcacheWrapper)
-
-            key = "cu.test_memcache_wrapper"
-            val = "The quick brown fox."
-            default = "The lazy dog."
-
-            self.assertEqual(cache.get(key, default=default), default)
-            cache.set(key, val)
-            self.assertEqual(pickle.loads(cache.get(key, default=default)),
-                              val)
-        finally:
-            memcached.CacheClass = prev_cache_cls
-            sys.modules["celery.backends.cache"] = prev_backend_module

+ 0 - 68
celery/tests/test_backends/test_database.py

@@ -1,68 +0,0 @@
-import unittest2 as unittest
-from datetime import timedelta
-
-from celery import states
-from celery.task import PeriodicTask
-from celery.utils import gen_unique_id
-from celery.backends.database import DatabaseBackend
-
-
-class SomeClass(object):
-
-    def __init__(self, data):
-        self.data = data
-
-
-class MyPeriodicTask(PeriodicTask):
-    name = "c.u.my-periodic-task-244"
-    run_every = timedelta(seconds=1)
-
-    def run(self, **kwargs):
-        return 42
-
-
-class TestDatabaseBackend(unittest.TestCase):
-
-    def test_backend(self):
-        b = DatabaseBackend()
-        tid = gen_unique_id()
-
-        self.assertFalse(b.is_successful(tid))
-        self.assertEqual(b.get_status(tid), states.PENDING)
-        self.assertIsNone(b.get_result(tid))
-
-        b.mark_as_done(tid, 42)
-        self.assertTrue(b.is_successful(tid))
-        self.assertEqual(b.get_status(tid), states.SUCCESS)
-        self.assertEqual(b.get_result(tid), 42)
-
-        tid2 = gen_unique_id()
-        result = {"foo": "baz", "bar": SomeClass(12345)}
-        b.mark_as_done(tid2, result)
-        # is serialized properly.
-        rindb = b.get_result(tid2)
-        self.assertEqual(rindb.get("foo"), "baz")
-        self.assertEqual(rindb.get("bar").data, 12345)
-
-        tid3 = gen_unique_id()
-        try:
-            raise KeyError("foo")
-        except KeyError, exception:
-            pass
-        b.mark_as_failure(tid3, exception)
-        self.assertFalse(b.is_successful(tid3))
-        self.assertEqual(b.get_status(tid3), states.FAILURE)
-        self.assertIsInstance(b.get_result(tid3), KeyError)
-
-    def test_taskset_store(self):
-        b = DatabaseBackend()
-        tid = gen_unique_id()
-
-        self.assertIsNone(b.restore_taskset(tid))
-
-        result = {"foo": "baz", "bar": SomeClass(12345)}
-        b.save_taskset(tid, result)
-        rindb = b.restore_taskset(tid)
-        self.assertIsNotNone(rindb)
-        self.assertEqual(rindb.get("foo"), "baz")
-        self.assertEqual(rindb.get("bar").data, 12345)

+ 0 - 36
celery/tests/test_conf.py

@@ -1,36 +0,0 @@
-import unittest2 as unittest
-
-from django.conf import settings
-
-from celery import conf
-
-
-SETTING_VARS = (
-    ("CELERY_DEFAULT_QUEUE", "DEFAULT_QUEUE"),
-    ("CELERY_DEFAULT_ROUTING_KEY", "DEFAULT_ROUTING_KEY"),
-    ("CELERY_DEFAULT_EXCHANGE_TYPE", "DEFAULT_EXCHANGE_TYPE"),
-    ("CELERY_DEFAULT_EXCHANGE", "DEFAULT_EXCHANGE"),
-    ("CELERYD_CONCURRENCY", "CELERYD_CONCURRENCY"),
-    ("CELERYD_LOG_FILE", "CELERYD_LOG_FILE"),
-    ("CELERYD_LOG_FORMAT", "CELERYD_LOG_FORMAT"),
-)
-
-
-class TestConf(unittest.TestCase):
-
-    def assertDefaultSetting(self, setting_name, result_var):
-        if hasattr(settings, setting_name):
-            self.assertEqual(getattr(conf, result_var),
-                              getattr(settings, setting_name),
-                              "Overwritten setting %s is written to %s" % (
-                                  setting_name, result_var))
-        else:
-            self.assertEqual(conf._DEFAULTS.get(setting_name),
-                             getattr(conf, result_var),
-                             "Default setting %s is written to %s" % (
-                                 setting_name, result_var))
-
-    def test_configuration_cls(self):
-        for setting_name, result_var in SETTING_VARS:
-            self.assertDefaultSetting(setting_name, result_var)
-        self.assertIsInstance(conf.CELERYD_LOG_LEVEL, int)

+ 0 - 28
celery/tests/test_discovery.py

@@ -1,28 +0,0 @@
-import unittest2 as unittest
-
-from django.conf import settings
-
-from celery.loaders.djangoapp import autodiscover
-from celery.task import tasks
-
-
-class TestDiscovery(unittest.TestCase):
-
-    def assertDiscovery(self):
-        apps = autodiscover()
-        self.assertTrue(apps)
-        self.assertIn("c.unittest.SomeAppTask", tasks)
-        self.assertEqual(tasks["c.unittest.SomeAppTask"].run(), 42)
-
-    def test_discovery(self):
-        if "someapp" in settings.INSTALLED_APPS:
-            self.assertDiscovery()
-
-    def test_discovery_with_broken(self):
-        if "someapp" in settings.INSTALLED_APPS:
-            installed_apps = list(settings.INSTALLED_APPS)
-            settings.INSTALLED_APPS = installed_apps + ["xxxnot.aexist"]
-            try:
-                self.assertRaises(ImportError, autodiscover)
-            finally:
-                settings.INSTALLED_APPS = installed_apps

+ 8 - 59
celery/tests/test_loaders.py

@@ -5,29 +5,25 @@ import unittest2 as unittest
 from celery import task
 from celery import loaders
 from celery.loaders import base
-from celery.loaders import djangoapp
 from celery.loaders import default
 
 from celery.tests.utils import with_environ
 
 
+
 class TestLoaders(unittest.TestCase):
 
     def test_get_loader_cls(self):
 
-        self.assertEqual(loaders.get_loader_cls("django"),
-                          loaders.DjangoLoader)
         self.assertEqual(loaders.get_loader_cls("default"),
-                          loaders.DefaultLoader)
+                          default.Loader)
         # Execute cached branch.
-        self.assertEqual(loaders.get_loader_cls("django"),
-                          loaders.DjangoLoader)
         self.assertEqual(loaders.get_loader_cls("default"),
-                          loaders.DefaultLoader)
+                          default.Loader)
 
     @with_environ("CELERY_LOADER", "default")
     def test_detect_loader_CELERY_LOADER(self):
-        self.assertEqual(loaders.detect_loader(), loaders.DefaultLoader)
+        self.assertIsInstance(loaders.setup_loader(), default.Loader)
 
 
 class DummyLoader(base.BaseLoader):
@@ -64,51 +60,6 @@ class TestLoaderBase(unittest.TestCase):
                               [os, sys, task])
 
 
-class TestDjangoLoader(unittest.TestCase):
-
-    def setUp(self):
-        self.loader = loaders.DjangoLoader()
-
-    def test_on_worker_init(self):
-        from django.conf import settings
-        old_imports = getattr(settings, "CELERY_IMPORTS", None)
-        settings.CELERY_IMPORTS = ("xxx.does.not.exist", )
-        try:
-            self.assertRaises(ImportError, self.loader.on_worker_init)
-        finally:
-            settings.CELERY_IMPORTS = old_imports
-
-    def test_race_protection(self):
-        djangoapp._RACE_PROTECTION = True
-        try:
-            self.assertFalse(self.loader.on_worker_init())
-        finally:
-            djangoapp._RACE_PROTECTION = False
-
-    def test_find_related_module_no_path(self):
-        self.assertFalse(djangoapp.find_related_module("sys", "tasks"))
-
-    def test_find_related_module_no_related(self):
-        self.assertFalse(djangoapp.find_related_module("someapp",
-                                                       "frobulators"))
-
-
-def modifies_django_env(fun):
-
-    def _protected(*args, **kwargs):
-        from django.conf import settings
-        current = dict((key, getattr(settings, key))
-                        for key in settings.get_all_members()
-                            if key.isupper())
-        try:
-            return fun(*args, **kwargs)
-        finally:
-            for key, value in current.items():
-                setattr(settings, key, value)
-
-    return _protected
-
-
 class TestDefaultLoader(unittest.TestCase):
 
     def test_wanted_module_item(self):
@@ -117,7 +68,6 @@ class TestDefaultLoader(unittest.TestCase):
         self.assertFalse(default.wanted_module_item("_foo"))
         self.assertFalse(default.wanted_module_item("__foo"))
 
-    @modifies_django_env
     def test_read_configuration(self):
         from types import ModuleType
 
@@ -126,17 +76,16 @@ class TestDefaultLoader(unittest.TestCase):
 
         celeryconfig = ConfigModule("celeryconfig")
         celeryconfig.CELERY_IMPORTS = ("os", "sys")
+        configname = os.environ.get("CELERY_CONFIG_MODULE") or "celeryconfig"
 
-        sys.modules["celeryconfig"] = celeryconfig
+        prevconfig = sys.modules[configname]
+        sys.modules[configname] = celeryconfig
         try:
             l = default.Loader()
             settings = l.read_configuration()
             self.assertTupleEqual(settings.CELERY_IMPORTS, ("os", "sys"))
-            from django.conf import settings
-            settings.configured = False
             settings = l.read_configuration()
             self.assertTupleEqual(settings.CELERY_IMPORTS, ("os", "sys"))
-            self.assertTrue(settings.configured)
             l.on_worker_init()
         finally:
-            sys.modules.pop("celeryconfig", None)
+            sys.modules[configname] = prevconfig

+ 0 - 74
celery/tests/test_models.py

@@ -1,74 +0,0 @@
-import unittest2 as unittest
-from datetime import datetime, timedelta
-
-from celery import states
-from celery.utils import gen_unique_id
-from celery.models import TaskMeta, TaskSetMeta
-
-
-class TestModels(unittest.TestCase):
-
-    def createTaskMeta(self):
-        id = gen_unique_id()
-        taskmeta, created = TaskMeta.objects.get_or_create(task_id=id)
-        return taskmeta
-
-    def createTaskSetMeta(self):
-        id = gen_unique_id()
-        tasksetmeta, created = TaskSetMeta.objects.get_or_create(taskset_id=id)
-        return tasksetmeta
-
-    def test_taskmeta(self):
-        m1 = self.createTaskMeta()
-        m2 = self.createTaskMeta()
-        m3 = self.createTaskMeta()
-        self.assertTrue(unicode(m1).startswith("<Task:"))
-        self.assertTrue(m1.task_id)
-        self.assertIsInstance(m1.date_done, datetime)
-
-        self.assertEqual(TaskMeta.objects.get_task(m1.task_id).task_id,
-                m1.task_id)
-        self.assertNotEqual(TaskMeta.objects.get_task(m1.task_id).status,
-                            states.SUCCESS)
-        TaskMeta.objects.store_result(m1.task_id, True, status=states.SUCCESS)
-        TaskMeta.objects.store_result(m2.task_id, True, status=states.SUCCESS)
-        self.assertEqual(TaskMeta.objects.get_task(m1.task_id).status,
-                         states.SUCCESS)
-        self.assertEqual(TaskMeta.objects.get_task(m2.task_id).status,
-                         states.SUCCESS)
-
-        # Have to avoid save() because it applies the auto_now=True.
-        TaskMeta.objects.filter(task_id=m1.task_id).update(
-                date_done=datetime.now() - timedelta(days=10))
-
-        expired = TaskMeta.objects.get_all_expired()
-        self.assertIn(m1, expired)
-        self.assertNotIn(m2, expired)
-        self.assertNotIn(m3, expired)
-
-        TaskMeta.objects.delete_expired()
-        self.assertNotIn(m1, TaskMeta.objects.all())
-
-    def test_tasksetmeta(self):
-        m1 = self.createTaskSetMeta()
-        m2 = self.createTaskSetMeta()
-        m3 = self.createTaskSetMeta()
-        self.assertTrue(unicode(m1).startswith("<TaskSet:"))
-        self.assertTrue(m1.taskset_id)
-        self.assertIsInstance(m1.date_done, datetime)
-
-        self.assertEqual(
-                TaskSetMeta.objects.restore_taskset(m1.taskset_id).taskset_id,
-                m1.taskset_id)
-
-        # Have to avoid save() because it applies the auto_now=True.
-        TaskSetMeta.objects.filter(taskset_id=m1.taskset_id).update(
-                date_done=datetime.now() - timedelta(days=10))
-
-        expired = TaskSetMeta.objects.get_all_expired()
-        self.assertIn(m1, expired)
-        self.assertNotIn(m2, expired)
-        self.assertNotIn(m3, expired)
-
-        TaskSetMeta.objects.delete_expired()
-        self.assertNotIn(m1, TaskSetMeta.objects.all())

+ 0 - 127
celery/tests/test_views.py

@@ -1,127 +0,0 @@
-import sys
-
-from django.http import HttpResponse
-from django.test.testcases import TestCase as DjangoTestCase
-from django.core.urlresolvers import reverse
-from django.template import TemplateDoesNotExist
-
-from anyjson import deserialize as JSON_load
-from billiard.utils.functional import curry
-
-from celery import conf
-from celery import states
-from celery.utils import gen_unique_id, get_full_cls_name
-from celery.backends import default_backend
-from celery.exceptions import RetryTaskError
-from celery.decorators import task
-from celery.datastructures import ExceptionInfo
-
-def reversestar(name, **kwargs):
-    return reverse(name, kwargs=kwargs)
-
-task_is_successful = curry(reversestar, "celery-is_task_successful")
-task_status = curry(reversestar, "celery-task_status")
-task_apply = curry(reverse, "celery-apply")
-
-scratch = {}
-@task()
-def mytask(x, y):
-    ret = scratch["result"] = int(x) * int(y)
-    return ret
-
-
-def create_exception(name, base=Exception):
-    return type(name, (base, ), {})
-
-
-def catch_exception(exception):
-    try:
-        raise exception
-    except exception.__class__, exc:
-        exc = default_backend.prepare_exception(exc)
-        return exc, ExceptionInfo(sys.exc_info()).traceback
-
-
-class ViewTestCase(DjangoTestCase):
-
-    def assertJSONEqual(self, json, py):
-        json = isinstance(json, HttpResponse) and json.content or json
-        try:
-            self.assertEqual(JSON_load(json), py)
-        except TypeError, exc:
-            raise TypeError("%s: %s" % (exc, json))
-
-
-class TestTaskApply(ViewTestCase):
-
-    def test_apply(self):
-        conf.ALWAYS_EAGER = True
-        try:
-            self.client.get(task_apply(kwargs={"task_name":
-                mytask.name}) + "?x=4&y=4")
-            self.assertEqual(scratch["result"], 16)
-        finally:
-            conf.ALWAYS_EAGER = False
-
-    def test_apply_raises_404_on_unregistered_task(self):
-        conf.ALWAYS_EAGER = True
-        try:
-            name = "xxx.does.not.exist"
-            action = curry(self.client.get, task_apply(kwargs={
-                        "task_name": name}) + "?x=4&y=4")
-            self.assertRaises(TemplateDoesNotExist, action)
-        finally:
-            conf.ALWAYS_EAGER = False
-
-
-class TestTaskStatus(ViewTestCase):
-
-    def assertStatusForIs(self, status, res, traceback=None):
-        uuid = gen_unique_id()
-        default_backend.store_result(uuid, res, status,
-                                     traceback=traceback)
-        json = self.client.get(task_status(task_id=uuid))
-        expect = dict(id=uuid, status=status, result=res)
-        if status in default_backend.EXCEPTION_STATES:
-            instore = default_backend.get_result(uuid)
-            self.assertEqual(str(instore.args), str(res.args))
-            expect["result"] = str(res.args[0])
-            expect["exc"] = get_full_cls_name(res.__class__)
-            expect["traceback"] = traceback
-
-        self.assertJSONEqual(json, dict(task=expect))
-
-    def test_task_status_success(self):
-        self.assertStatusForIs(states.SUCCESS, "The quick brown fox")
-
-    def test_task_status_failure(self):
-        exc, tb = catch_exception(KeyError("foo"))
-        self.assertStatusForIs(states.FAILURE, exc, tb)
-
-    def test_task_status_retry(self):
-        oexc, _ = catch_exception(KeyError("Resource not available"))
-        exc, tb = catch_exception(RetryTaskError(str(oexc), oexc))
-        self.assertStatusForIs(states.RETRY, exc, tb)
-
-
-class TestTaskIsSuccessful(ViewTestCase):
-
-    def assertStatusForIs(self, status, outcome):
-        uuid = gen_unique_id()
-        result = gen_unique_id()
-        default_backend.store_result(uuid, result, status)
-        json = self.client.get(task_is_successful(task_id=uuid))
-        self.assertJSONEqual(json, {"task": {"id": uuid,
-                                             "executed": outcome}})
-
-    def test_is_successful_success(self):
-        self.assertStatusForIs(states.SUCCESS, True)
-
-    def test_is_successful_pending(self):
-        self.assertStatusForIs(states.PENDING, False)
-
-    def test_is_successful_failure(self):
-        self.assertStatusForIs(states.FAILURE, False)
-
-    def test_is_successful_retry(self):
-        self.assertStatusForIs(states.RETRY, False)

+ 16 - 83
celery/tests/test_worker_job.py

@@ -5,17 +5,16 @@ import unittest2 as unittest
 import simplejson
 from StringIO import StringIO
 
-from django.core import cache
 from carrot.backends.base import BaseMessage
 
 from celery import states
 from celery.log import setup_logger
 from celery.task.base import Task
 from celery.utils import gen_unique_id
-from celery.models import TaskMeta
 from celery.result import AsyncResult
 from celery.worker.job import WorkerTaskTrace, TaskWrapper
 from celery.worker.pool import TaskPool
+from celery.backends import default_backend
 from celery.exceptions import RetryTaskError, NotRegistered
 from celery.decorators import task as task_dec
 from celery.datastructures import ExceptionInfo
@@ -63,13 +62,6 @@ def mytask_raising(i, **kwargs):
     raise KeyError(i)
 
 
-@task_dec()
-def get_db_connection(i, **kwargs):
-    from django.db import connection
-    return id(connection)
-get_db_connection.ignore_result = True
-
-
 class TestRetryTaskError(unittest.TestCase):
 
     def test_retry_task_error(self):
@@ -100,65 +92,6 @@ class TestJail(unittest.TestCase):
         self.assertEqual(ret, 256)
         self.assertFalse(AsyncResult(task_id).ready())
 
-    def test_django_db_connection_is_closed(self):
-        from django.db import connection
-        connection._was_closed = False
-        old_connection_close = connection.close
-
-        def monkeypatched_connection_close(*args, **kwargs):
-            connection._was_closed = True
-            return old_connection_close(*args, **kwargs)
-
-        connection.close = monkeypatched_connection_close
-        try:
-            jail(gen_unique_id(), get_db_connection.name, [2], {})
-            self.assertTrue(connection._was_closed)
-        finally:
-            connection.close = old_connection_close
-
-    def test_django_cache_connection_is_closed(self):
-        old_cache_close = getattr(cache.cache, "close", None)
-        old_backend = cache.settings.CACHE_BACKEND
-        cache.settings.CACHE_BACKEND = "libmemcached"
-        cache._was_closed = False
-        old_cache_parse_backend = getattr(cache, "parse_backend_uri", None)
-        if old_cache_parse_backend: # checks to make sure attr exists
-            delattr(cache, 'parse_backend_uri')
-
-        def monkeypatched_cache_close(*args, **kwargs):
-            cache._was_closed = True
-
-        cache.cache.close = monkeypatched_cache_close
-
-        jail(gen_unique_id(), mytask.name, [4], {})
-        self.assertTrue(cache._was_closed)
-        cache.cache.close = old_cache_close
-        cache.settings.CACHE_BACKEND = old_backend
-        if old_cache_parse_backend:
-            cache.parse_backend_uri = old_cache_parse_backend
-
-    def test_django_cache_connection_is_closed_django_1_1(self):
-        old_cache_close = getattr(cache.cache, "close", None)
-        old_backend = cache.settings.CACHE_BACKEND
-        cache.settings.CACHE_BACKEND = "libmemcached"
-        cache._was_closed = False
-        old_cache_parse_backend = getattr(cache, "parse_backend_uri", None)
-        cache.parse_backend_uri = lambda uri: ["libmemcached", "1", "2"]
-
-        def monkeypatched_cache_close(*args, **kwargs):
-            cache._was_closed = True
-
-        cache.cache.close = monkeypatched_cache_close
-
-        jail(gen_unique_id(), mytask.name, [4], {})
-        self.assertTrue(cache._was_closed)
-        cache.cache.close = old_cache_close
-        cache.settings.CACHE_BACKEND = old_backend
-        if old_cache_parse_backend:
-            cache.parse_backend_uri = old_cache_parse_backend
-        else:
-            del(cache.parse_backend_uri)
-
 
 class MockEventDispatcher(object):
 
@@ -325,44 +258,44 @@ class TestTaskWrapper(unittest.TestCase):
         tid = gen_unique_id()
         tw = TaskWrapper(mytask.name, tid, [4], {"f": "x"})
         self.assertEqual(tw.execute(), 256)
-        meta = TaskMeta.objects.get(task_id=tid)
-        self.assertEqual(meta.result, 256)
-        self.assertEqual(meta.status, states.SUCCESS)
+        meta = default_backend._get_task_meta_for(tid)
+        self.assertEqual(meta["result"], 256)
+        self.assertEqual(meta["status"], states.SUCCESS)
 
     def test_execute_success_no_kwargs(self):
         tid = gen_unique_id()
         tw = TaskWrapper(mytask_no_kwargs.name, tid, [4], {})
         self.assertEqual(tw.execute(), 256)
-        meta = TaskMeta.objects.get(task_id=tid)
-        self.assertEqual(meta.result, 256)
-        self.assertEqual(meta.status, states.SUCCESS)
+        meta = default_backend._get_task_meta_for(tid)
+        self.assertEqual(meta["result"], 256)
+        self.assertEqual(meta["status"], states.SUCCESS)
 
     def test_execute_success_some_kwargs(self):
         tid = gen_unique_id()
         tw = TaskWrapper(mytask_some_kwargs.name, tid, [4], {})
         self.assertEqual(tw.execute(logfile="foobaz.log"), 256)
-        meta = TaskMeta.objects.get(task_id=tid)
+        meta = default_backend._get_task_meta_for(tid)
         self.assertEqual(some_kwargs_scratchpad.get("logfile"), "foobaz.log")
-        self.assertEqual(meta.result, 256)
-        self.assertEqual(meta.status, states.SUCCESS)
+        self.assertEqual(meta["result"], 256)
+        self.assertEqual(meta["status"], states.SUCCESS)
 
     def test_execute_ack(self):
         tid = gen_unique_id()
         tw = TaskWrapper(mytask.name, tid, [4], {"f": "x"},
                         on_ack=on_ack)
         self.assertEqual(tw.execute(), 256)
-        meta = TaskMeta.objects.get(task_id=tid)
+        meta = default_backend._get_task_meta_for(tid)
         self.assertTrue(scratch["ACK"])
-        self.assertEqual(meta.result, 256)
-        self.assertEqual(meta.status, states.SUCCESS)
+        self.assertEqual(meta["result"], 256)
+        self.assertEqual(meta["status"], states.SUCCESS)
 
     def test_execute_fail(self):
         tid = gen_unique_id()
         tw = TaskWrapper(mytask_raising.name, tid, [4], {"f": "x"})
         self.assertIsInstance(tw.execute(), ExceptionInfo)
-        meta = TaskMeta.objects.get(task_id=tid)
-        self.assertEqual(meta.status, states.FAILURE)
-        self.assertIsInstance(meta.result, KeyError)
+        meta = default_backend._get_task_meta_for(tid)
+        self.assertEqual(meta["status"], states.FAILURE)
+        self.assertIsInstance(meta["result"], KeyError)
 
     def test_execute_using_pool(self):
         tid = gen_unique_id()

+ 0 - 16
celery/urls.py

@@ -1,16 +0,0 @@
-"""
-
-URLs defined for celery.
-
-"""
-from django.conf.urls.defaults import patterns, url
-
-from celery import views
-
-
-urlpatterns = patterns("",
-    url(r'^(?P<task_id>[\w\d\-]+)/done/?$', views.is_task_successful,
-        name="celery-is_task_successful"),
-    url(r'^(?P<task_id>[\w\d\-]+)/status/?$', views.task_status,
-        name="celery-task_status"),
-)

+ 1 - 0
celery/utils/dispatch/__init__.py

@@ -0,0 +1 @@
+from celery.utils.dispatch.signal import Signal

+ 36 - 0
celery/utils/dispatch/license.txt

@@ -0,0 +1,36 @@
+django.dispatch was originally forked from PyDispatcher.
+
+PyDispatcher License:
+
+    Copyright (c) 2001-2003, Patrick K. O'Brien and Contributors
+    All rights reserved.
+    
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions
+    are met:
+    
+        Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+    
+        Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials
+        provided with the distribution.
+    
+        The name of Patrick K. O'Brien, or the name of any Contributor,
+        may not be used to endorse or promote products derived from this 
+        software without specific prior written permission.
+    
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+    COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+    OF THE POSSIBILITY OF SUCH DAMAGE. 
+

+ 275 - 0
celery/utils/dispatch/saferef.py

@@ -0,0 +1,275 @@
+"""
+"Safe weakrefs", originally from pyDispatcher.
+
+Provides a way to safely weakref any function, including bound methods (which
+aren't handled by the core weakref module).
+"""
+
+import weakref
+import traceback
+
+
+def safe_ref(target, on_delete=None):
+    """Return a *safe* weak reference to a callable target
+
+    :param target: the object to be weakly referenced, if it's a
+        bound method reference, will create a :class:`BoundMethodWeakref`,
+        otherwise creates a simple :class:`weakref.ref`.
+
+    :keyword on_delete: if provided, will have a hard reference stored
+        to the callable to be called after the safe reference
+        goes out of scope with the reference object, (either a
+        :class:`weakref.ref` or a :class:`BoundMethodWeakref`) as argument.
+    """
+    if getattr(target, "im_self", None) is not None:
+        # Turn a bound method into a BoundMethodWeakref instance.
+        # Keep track of these instances for lookup by disconnect().
+        assert hasattr(target, 'im_func'), \
+            """safe_ref target %r has im_self, but no im_func, " \
+            "don't know how to create reference""" % (target, )
+        return get_bound_method_weakref(target=target,
+                                        on_delete=on_delete)
+    if callable(on_delete):
+        return weakref.ref(target, on_delete)
+    else:
+        return weakref.ref(target)
+
+
+class BoundMethodWeakref(object):
+    """'Safe' and reusable weak references to instance methods.
+
+    BoundMethodWeakref objects provide a mechanism for
+    referencing a bound method without requiring that the
+    method object itself (which is normally a transient
+    object) is kept alive.  Instead, the BoundMethodWeakref
+    object keeps weak references to both the object and the
+    function which together define the instance method.
+
+    .. attribute:: key
+        the identity key for the reference, calculated
+        by the class's :meth:`calculate_key` method applied to the
+        target instance method
+
+    .. attribute:: deletion_methods
+
+        sequence of callable objects taking
+        single argument, a reference to this object which
+        will be called when *either* the target object or
+        target function is garbage collected (i.e. when
+        this object becomes invalid).  These are specified
+        as the on_delete parameters of :func:`safe_ref` calls.
+
+    .. attribute:: weak_self
+        weak reference to the target object
+
+    .. attribute:: weak_func
+        weak reference to the target function
+
+    .. attribute:: _all_instances
+        class attribute pointing to all live
+        BoundMethodWeakref objects indexed by the class's
+        :meth:`calculate_key(target)` method applied to the target
+        objects. This weak value dictionary is used to
+        short-circuit creation so that multiple references
+        to the same (object, function) pair produce the
+        same BoundMethodWeakref instance.
+
+    """
+
+    _all_instances = weakref.WeakValueDictionary()
+
+    def __new__(cls, target, on_delete=None, *arguments, **named):
+        """Create new instance or return current instance
+
+        Basically this method of construction allows us to
+        short-circuit creation of references to already-
+        referenced instance methods.  The key corresponding
+        to the target is calculated, and if there is already
+        an existing reference, that is returned, with its
+        deletionMethods attribute updated.  Otherwise the
+        new instance is created and registered in the table
+        of already-referenced methods.
+
+        """
+        key = cls.calculate_key(target)
+        current = cls._all_instances.get(key)
+        if current is not None:
+            current.deletion_methods.append(on_delete)
+            return current
+        else:
+            base = super(BoundMethodWeakref, cls).__new__(cls)
+            cls._all_instances[key] = base
+            base.__init__(target, on_delete, *arguments, **named)
+            return base
+
+    def __init__(self, target, on_delete=None):
+        """Return a weak-reference-like instance for a bound method
+
+        :param target: the instance-method target for the weak
+            reference, must have ``im_self`` and ``im_func`` attributes
+            and be reconstructable via::
+
+                target.im_func.__get__(target.im_self)
+
+            which is true of built-in instance methods.
+
+        :keyword on_delete: optional callback which will be called
+            when this weak reference ceases to be valid
+            (i.e. either the object or the function is garbage
+            collected).  Should take a single argument,
+            which will be passed a pointer to this object.
+
+        """
+        def remove(weak, self=self):
+            """Set self.is_dead to true when method or instance is destroyed"""
+            methods = self.deletion_methods[:]
+            del(self.deletion_methods[:])
+            try:
+                del(self.__class__._all_instances[self.key])
+            except KeyError:
+                pass
+            for function in methods:
+                try:
+                    if callable(function):
+                        function(self)
+                except Exception, exc:
+                    try:
+                        traceback.print_exc()
+                    except AttributeError, err:
+                        print("Exception during saferef %s cleanup function "
+                              "%s: %s" % (self, function, exc))
+
+        self.deletion_methods = [on_delete]
+        self.key = self.calculate_key(target)
+        self.weak_self = weakref.ref(target.im_self, remove)
+        self.weak_func = weakref.ref(target.im_func, remove)
+        self.self_name = str(target.im_self)
+        self.func_name = str(target.im_func.__name__)
+
+    def calculate_key(cls, target):
+        """Calculate the reference key for this reference
+
+        Currently this is a two-tuple of the ``id()``'s of the
+        target object and the target function respectively.
+        """
+        return id(target.im_self), id(target.im_func)
+    calculate_key = classmethod(calculate_key)
+
+    def __str__(self):
+        """Give a friendly representation of the object"""
+        return """%s( %s.%s )""" % (
+            self.__class__.__name__,
+            self.self_name,
+            self.func_name,
+        )
+
+    __repr__ = __str__
+
+    def __nonzero__(self):
+        """Whether we are still a valid reference"""
+        return self() is not None
+
+    def __cmp__(self, other):
+        """Compare with another reference"""
+        if not isinstance(other, self.__class__):
+            return cmp(self.__class__, type(other))
+        return cmp(self.key, other.key)
+
+    def __call__(self):
+        """Return a strong reference to the bound method
+
+        If the target cannot be retrieved, then will
+        return None, otherwise returns a bound instance
+        method for our object and function.
+
+        Note:
+            You may call this method any number of times,
+            as it does not invalidate the reference.
+        """
+        target = self.weak_self()
+        if target is not None:
+            function = self.weak_func()
+            if function is not None:
+                return function.__get__(target)
+        return None
+
+class BoundNonDescriptorMethodWeakref(BoundMethodWeakref):
+    """A specialized :class:`BoundMethodWeakref`, for platforms where
+    instance methods are not descriptors.
+
+    It assumes that the function name and the target attribute name are the
+    same, instead of assuming that the function is a descriptor. This approach
+    is equally fast, but not 100% reliable because functions can be stored on an
+    attribute named differenty than the function's name such as in::
+
+        >>> class A(object):
+        ...     pass
+
+        >>> def foo(self):
+        ...     return "foo"
+        >>> A.bar = foo
+
+    But this shouldn't be a common use case. So, on platforms where methods
+    aren't descriptors (such as Jython) this implementation has the advantage
+    of working in the most cases.
+
+    """
+    def __init__(self, target, on_delete=None):
+        """Return a weak-reference-like instance for a bound method
+
+        :param target: the instance-method target for the weak
+            reference, must have ``im_self`` and ``im_func`` attributes
+            and be reconstructable via::
+
+                target.im_func.__get__(target.im_self)
+
+            which is true of built-in instance methods.
+
+        :keyword on_delete: optional callback which will be called
+            when this weak reference ceases to be valid
+            (i.e. either the object or the function is garbage
+            collected). Should take a single argument,
+            which will be passed a pointer to this object.
+
+        """
+        assert getattr(target.im_self, target.__name__) == target, \
+               "method %s isn't available as the attribute %s of %s" % (
+                    target, target.__name__, target.im_self)
+        super(BoundNonDescriptorMethodWeakref, self).__init__(target,
+                                                              on_delete)
+
+    def __call__(self):
+        """Return a strong reference to the bound method
+
+        If the target cannot be retrieved, then will
+        return None, otherwise returns a bound instance
+        method for our object and function.
+
+        Note:
+            You may call this method any number of times,
+            as it does not invalidate the reference.
+
+        """
+        target = self.weak_self()
+        if target is not None:
+            function = self.weak_func()
+            if function is not None:
+                # Using curry() would be another option, but it erases the
+                # "signature" of the function. That is, after a function is
+                # curried, the inspect module can't be used to determine how
+                # many arguments the function expects, nor what keyword
+                # arguments it supports, and pydispatcher needs this
+                # information.
+                return getattr(target, function.__name__)
+        return None
+
+
+def get_bound_method_weakref(target, on_delete):
+    """Instantiates the appropiate :class:`BoundMethodWeakRef`, depending
+    on the details of the underlying class method implementation."""
+    if hasattr(target, '__get__'):
+        # target method is a descriptor, so the default implementation works:
+        return BoundMethodWeakref(target=target, on_delete=on_delete)
+    else:
+        # no luck, use the alternative implementation:
+        return BoundNonDescriptorMethodWeakref(target=target, on_delete=on_delete)

+ 209 - 0
celery/utils/dispatch/signal.py

@@ -0,0 +1,209 @@
+"""Signal class."""
+
+import weakref
+try:
+    set
+except NameError:
+    from sets import Set as set # Python 2.3 fallback
+
+from celery.utils.dispatch import saferef
+
+WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref)
+
+
+def _make_id(target):
+    if hasattr(target, 'im_func'):
+        return (id(target.im_self), id(target.im_func))
+    return id(target)
+
+
+class Signal(object):
+    """Base class for all signals
+
+
+    .. attribute:: receivers
+        Internal attribute, holds a dictionary of
+        ``{receriverkey (id): weakref(receiver)}`` mappings.
+
+    """
+
+    def __init__(self, providing_args=None):
+        """Create a new signal.
+
+        :param providing_args: A list of the arguments this signal can pass
+            along in a :meth:`send` call.
+
+        """
+        self.receivers = []
+        if providing_args is None:
+            providing_args = []
+        self.providing_args = set(providing_args)
+
+    def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):
+        """Connect receiver to sender for signal.
+
+        :param receiver: A function or an instance method which is to
+            receive signals. Receivers must be hashable objects.
+
+            if weak is ``True``, then receiver must be weak-referencable (more
+            precisely :func:`saferef.safe_ref()` must be able to create a reference
+            to the receiver).
+
+            Receivers must be able to accept keyword arguments.
+
+            If receivers have a ``dispatch_uid`` attribute, the receiver will
+            not be added if another receiver already exists with that
+            ``dispatch_uid``.
+
+        :keyword sender: The sender to which the receiver should respond.
+            Must either be of type :class:`Signal`, or ``None`` to receive
+            events from any sender.
+
+        :keyword weak: Whether to use weak references to the receiver.
+            By default, the module will attempt to use weak references to the
+            receiver objects. If this parameter is false, then strong
+            references will be used.
+
+        :keyword dispatch_uid: An identifier used to uniquely identify a
+            particular instance of a receiver. This will usually be a
+            string, though it may be anything hashable.
+
+        """
+        if dispatch_uid:
+            lookup_key = (dispatch_uid, _make_id(sender))
+        else:
+            lookup_key = (_make_id(receiver), _make_id(sender))
+
+        if weak:
+            receiver = saferef.safe_ref(receiver, on_delete=self._remove_receiver)
+
+        for r_key, _ in self.receivers:
+            if r_key == lookup_key:
+                break
+        else:
+            self.receivers.append((lookup_key, receiver))
+
+    def disconnect(self, receiver=None, sender=None, weak=True, dispatch_uid=None):
+        """Disconnect receiver from sender for signal.
+
+        If weak references are used, disconnect need not be called. The receiver
+        will be remove from dispatch automatically.
+
+        :keyword receiver: The registered receiver to disconnect. May be
+            none if ``dispatch_uid`` is specified.
+
+        :keyword sender: The registered sender to disconnect.
+
+        :keyword weak: The weakref state to disconnect.
+
+        :keyword dispatch_uid: the unique identifier of the receiver
+            to disconnect
+
+        """
+        if dispatch_uid:
+            lookup_key = (dispatch_uid, _make_id(sender))
+        else:
+            lookup_key = (_make_id(receiver), _make_id(sender))
+
+        for index in xrange(len(self.receivers)):
+            (r_key, _) = self.receivers[index]
+            if r_key == lookup_key:
+                del self.receivers[index]
+                break
+
+    def send(self, sender, **named):
+        """Send signal from sender to all connected receivers.
+
+        If any receiver raises an error, the error propagates back through send,
+        terminating the dispatch loop, so it is quite possible to not have all
+        receivers called if a raises an error.
+
+        :param sender: The sender of the signal. Either a specific
+            object or ``None``.
+
+        :keyword \*\*named: Named arguments which will be passed to receivers.
+
+        :returns: a list of tuple pairs: ``[(receiver, response), ... ]``.
+
+        """
+        responses = []
+        if not self.receivers:
+            return responses
+
+        for receiver in self._live_receivers(_make_id(sender)):
+            response = receiver(signal=self, sender=sender, **named)
+            responses.append((receiver, response))
+        return responses
+
+    def send_robust(self, sender, **named):
+        """Send signal from sender to all connected receivers catching errors.
+
+        :param sender: The sender of the signal. Can be any python object
+            (normally one registered with a connect if you actually want
+            something to occur).
+
+        :keyword \*\*named: Named arguments which will be passed to receivers.
+            These arguments must be a subset of the argument names defined in
+            :attr:`providing_args`.
+
+        :returns: a list of tuple pairs: ``[(receiver, response), ... ]``.
+
+        :raises DispatcherKeyError:
+
+        if any receiver raises an error (specifically any subclass of
+        :exc:`Exception`), the error instance is returned as the result for that
+        receiver.
+
+        """
+        responses = []
+        if not self.receivers:
+            return responses
+
+        # Call each receiver with whatever arguments it can accept.
+        # Return a list of tuple pairs [(receiver, response), ... ].
+        for receiver in self._live_receivers(_make_id(sender)):
+            try:
+                response = receiver(signal=self, sender=sender, **named)
+            except Exception, err:
+                responses.append((receiver, err))
+            else:
+                responses.append((receiver, response))
+        return responses
+
+    def _live_receivers(self, senderkey):
+        """Filter sequence of receivers to get resolved, live receivers.
+
+        This checks for weak references and resolves them, then returning only
+        live receivers.
+
+        """
+        none_senderkey = _make_id(None)
+        receivers = []
+
+        for (receiverkey, r_senderkey), receiver in self.receivers:
+            if r_senderkey == none_senderkey or r_senderkey == senderkey:
+                if isinstance(receiver, WEAKREF_TYPES):
+                    # Dereference the weak reference.
+                    receiver = receiver()
+                    if receiver is not None:
+                        receivers.append(receiver)
+                else:
+                    receivers.append(receiver)
+        return receivers
+
+    def _remove_receiver(self, receiver):
+        """Remove dead receivers from connections."""
+
+        to_remove = []
+        for key, connected_receiver in self.receivers:
+            if connected_receiver == receiver:
+                to_remove.append(key)
+        for key in to_remove:
+            for idx, (r_key, _) in enumerate(self.receivers):
+                if r_key == key:
+                    del self.receivers[idx]
+
+    def __repr__(self):
+        return '<Signal: %s>' % (self.__class__.__name__, )
+
+    __str__ = __repr__

+ 24 - 0
celery/utils/mail.py

@@ -0,0 +1,24 @@
+from mailer import Message, Mailer
+
+from celery.loaders import load_settings
+
+
+def mail_admins(subject, message, fail_silently=False):
+    """Send a message to the admins in settings.ADMINS."""
+    settings = load_settings()
+    if not settings.ADMINS:
+        return
+    to = ", ".join(admin_email for _, admin_email in settings.ADMINS)
+    username = settings.EMAIL_HOST_USER
+    password = settings.EMAIL_HOST_PASSWORD
+
+    message = Message(From=settings.SERVER_EMAIL, To=to,
+                      Subject=subject, Message=message)
+
+    try:
+        mailer = Mailer(settings.EMAIL_HOST, settings.EMAIL_PORT)
+        username and mailer.login(username, password)
+        mailer.send(message)
+    except Exception:
+        if not fail_silently:
+            raise

+ 0 - 106
celery/views.py

@@ -1,106 +0,0 @@
-from django.http import HttpResponse, Http404
-
-from anyjson import serialize as JSON_dump
-from billiard.utils.functional import wraps
-
-from celery.utils import get_full_cls_name
-from celery.result import AsyncResult
-from celery.registry import tasks
-from celery.backends import default_backend
-
-
-def task_view(task):
-    """Decorator turning any task into a view that applies the task
-    asynchronously.
-
-    Returns a JSON dictionary containing the keys ``ok``, and
-        ``task_id``.
-
-    """
-
-    def _applier(request, **options):
-        kwargs = request.method == "POST" and \
-            request.POST.copy() or request.GET.copy()
-        kwargs = dict((key.encode("utf-8"), value)
-                    for key, value in kwargs.items())
-
-        result = task.apply_async(kwargs=kwargs)
-        response_data = {"ok": "true", "task_id": result.task_id}
-        return HttpResponse(JSON_dump(response_data),
-                            mimetype="application/json")
-
-    return _applier
-
-
-def apply(request, task_name):
-    """View applying a task.
-
-    **Note:** Please use this with caution. Preferably you shouldn't make this
-        publicly accessible without ensuring your code is safe!
-
-    """
-    try:
-        task = tasks[task_name]
-    except KeyError:
-        raise Http404("apply: no such task")
-    return task_view(task)(request)
-
-
-def is_task_successful(request, task_id):
-    """Returns task execute status in JSON format."""
-    response_data = {"task": {"id": task_id,
-                              "executed": AsyncResult(task_id).successful()}}
-    return HttpResponse(JSON_dump(response_data), mimetype="application/json")
-
-
-def task_status(request, task_id):
-    """Returns task status and result in JSON format."""
-    status = default_backend.get_status(task_id)
-    res = default_backend.get_result(task_id)
-    response_data = dict(id=task_id, status=status, result=res)
-    if status in default_backend.EXCEPTION_STATES:
-        traceback = default_backend.get_traceback(task_id)
-        response_data.update({"result": str(res.args[0]),
-                              "exc": get_full_cls_name(res.__class__),
-                              "traceback": traceback})
-
-    return HttpResponse(JSON_dump({"task": response_data}),
-            mimetype="application/json")
-
-
-def task_webhook(fun):
-    """Decorator turning a function into a task webhook.
-
-    If an exception is raised within the function, the decorated
-    function catches this and returns an error JSON response, otherwise
-    it returns the result as a JSON response.
-
-
-    Example:
-
-    .. code-block:: python
-
-        @task_webhook
-        def add(request):
-            x = int(request.GET["x"])
-            y = int(request.GET["y"])
-            return x + y
-
-        >>> response = add(request)
-        >>> response.content
-        '{"status": "success", "retval": 100}'
-
-    """
-
-    @wraps(fun)
-    def _inner(*args, **kwargs):
-        try:
-            retval = fun(*args, **kwargs)
-        except Exception, exc:
-            response = {"status": "failure", "reason": str(exc)}
-        else:
-            response = {"status": "success", "retval": retval}
-
-        return HttpResponse(JSON_dump(response), mimetype="application/json")
-
-    return _inner

+ 1 - 1
celery/worker/job.py

@@ -8,12 +8,12 @@ import time
 import socket
 import warnings
 
-from django.core.mail import mail_admins
 
 from celery import conf
 from celery import platform
 from celery.log import get_default_logger
 from celery.utils import noop, fun_takes_kwargs
+from celery.utils.mail import mail_admins
 from celery.loaders import current_loader
 from celery.execute.trace import TaskTrace
 from celery.registry import tasks

+ 5 - 27
contrib/debian/init.d/celeryd

@@ -9,16 +9,8 @@
 # Short-Description:	celery task worker daemon
 ### END INIT INFO
 
-# To use this with Django set your DJANGO_PROJECT_DIR in /etc/default/celeryd:
-#
-#   echo "DJANGO_PROJECT_DIR=/opt/Myapp" > /etc/default/celeryd
-#
-# The django project dir is the directory that contains settings and
-# manage.py.
-
 set -e
 
-DJANGO_SETTINGS_MODULE=settings
 CELERYD_PID_FILE="/var/run/celeryd.pid"
 CELERYD_LOG_FILE="/var/log/celeryd.log"
 CELERYD_LOG_LEVEL="INFO"
@@ -30,22 +22,10 @@ if test -f /etc/default/celeryd; then
     . /etc/default/celeryd
 fi
 
-export DJANGO_SETTINGS_MODULE
-export DJANGO_PROJECT_DIR
-
-if [ -z "$CELERYD" ]; then
-    if [ ! -z "$DJANGO_PROJECT_DIR" ]; then
-        CELERYD="$DJANGO_PROJECT_DIR/manage.py"
-        CELERYD_OPTS="celeryd $CELERYD_OPTS"
-    else
-        CELERYD=$DEFAULT_CELERYD
-    fi
-fi
+export CELERY_LOADER
 
 . /lib/lsb/init-functions
 
-cd $DJANGO_PROJECT_DIR
-
 CELERYD_OPTS="$CELERYD_OPTS -f $CELERYD_LOG_FILE -l $CELERYD_LOG_LEVEL"
 
 if [ -n "$2" ]; then
@@ -83,9 +63,7 @@ check_dev_null() {
 export PATH="${PATH:+$PATH:}/usr/sbin:/sbin"
 if [ ! -z "$VIRTUALENV" ]; then
     export PATH="$VIRTUALENV/bin:$PATH"
-    if [ -z "$DJANGO_PROJECT_DIR" ]; then
-    	CELERYD="$VIRTUALENV/bin/$CELERYD"
-    fi
+    CELERYD="$VIRTUALENV/bin/$CELERYD"
 fi
 
 
@@ -102,7 +80,7 @@ case "$1" in
   start)
     check_dev_null
     log_daemon_msg "Starting celery task worker server" "celeryd"
-    if start-stop-daemon --start $DAEMON_OPTS --quiet --oknodo --background --chdir $DJANGO_PROJECT_DIR --make-pidfile --pidfile $CELERYD_PID_FILE --exec $CELERYD -- $CELERYD_OPTS; then
+    if start-stop-daemon --start $DAEMON_OPTS --quiet --oknodo --background --make-pidfile --pidfile $CELERYD_PID_FILE --exec $CELERYD -- $CELERYD_OPTS; then
         log_end_msg 0
     else
         log_end_msg 1
@@ -124,7 +102,7 @@ case "$1" in
     log_daemon_msg "Restarting celery task worker server" "celeryd"
     start-stop-daemon --stop --quiet --oknodo --retry 30 --pidfile $CELERYD_PID_FILE
     check_dev_null log_end_msg
-    if start-stop-daemon --start $DAEMON_OPTS --quiet --oknodo --background --chdir $DJANGO_PROJECT_DIR --make-pidfile --pidfile $CELERYD_PID_FILE --exec $CELERYD -- $CELERYD_OPTS; then log_end_msg 0
+    if start-stop-daemon --start $DAEMON_OPTS --quiet --oknodo --background --make-pidfile --pidfile $CELERYD_PID_FILE --exec $CELERYD -- $CELERYD_OPTS; then log_end_msg 0
     else
         log_end_msg 1
     fi
@@ -140,7 +118,7 @@ case "$1" in
         0)
 		# old daemon stopped
 		check_dev_null log_end_msg
-		if start-stop-daemon --start $DAEMON_OPTS --quiet --oknodo --background --make-pidfile --chdir $DJANGO_PROJECT_DIR --pidfile $CELERYD_PID_FILE --exec $CELERYD -- $CELERYD_OPTS; then
+		if start-stop-daemon --start $DAEMON_OPTS --quiet --oknodo --background --make-pidfile --pidfile $CELERYD_PID_FILE --exec $CELERYD -- $CELERYD_OPTS; then
 		    log_end_msg 0
 		else
 		    log_end_msg 1

+ 1 - 1
contrib/release/doc4allmods

@@ -2,7 +2,7 @@
 
 PACKAGE="$1"
 SKIP_PACKAGES="$PACKAGE tests management urls"
-SKIP_FILES="celery.bin.rst celery.task.rest.rst celery.contrib.rst
+SKIP_FILES="celery.bin.rst celery.contrib.rst
             celery.contrib.batches.rst"
 
 modules=$(find "$PACKAGE" -name "*.py")

+ 2 - 1
contrib/requirements/default.txt

@@ -1,5 +1,6 @@
-django
+mailer
 python-dateutil
+sqlalchemy
 anyjson
 carrot>=0.10.4
 django-picklefield

+ 0 - 1
contrib/requirements/test.txt

@@ -2,7 +2,6 @@ unittest2>=0.4.0
 simplejson
 nose
 nose-cover3
-django-nose
 coverage>=3.0
 mock>=0.6.0
 pytyrant

+ 0 - 18
contrib/supervisord/django/celerybeat.conf

@@ -1,18 +0,0 @@
-; ==========================================
-;  celerybeat supervisor example for Django
-; ==========================================
-
-[program:celerybeat]
-command=/path/to/project/manage.py celerybeat --schedule=/var/lib/celery/celerybeat-schedule --loglevel=INFO
-directory=/path/to/project
-user=nobody
-numprocs=1
-stdout_logfile=/var/log/celerybeat.log
-stderr_logfile=/var/log/celerybeat.log
-autostart=true
-autorestart=true
-startsecs=10
-
-; if rabbitmq is supervised, set its priority higher
-; so it starts first
-priority=999

+ 0 - 22
contrib/supervisord/django/celeryd.conf

@@ -1,22 +0,0 @@
-; =======================================
-;  celeryd supervisor example for Django
-; =======================================
-
-[program:celery]
-command=/path/to/project/manage.py celeryd --loglevel=INFO
-directory=/path/to/project
-user=nobody
-numprocs=1
-stdout_logfile=/var/log/celeryd.log
-stderr_logfile=/var/log/celeryd.log
-autostart=true
-autorestart=true
-startsecs=10
-
-; Need to wait for currently executing tasks to finish at shutdown.
-; Increase this if you have very long running tasks.
-stopwaitsecs = 600
-
-; if rabbitmq is supervised, set its priority higher
-; so it starts first
-priority=998

+ 0 - 112
docs/_ext/djangodocs.py

@@ -1,112 +0,0 @@
-"""
-Sphinx plugins for Django documentation.
-"""
-
-import docutils.nodes
-import docutils.transforms
-import sphinx
-import sphinx.addnodes
-import sphinx.directives
-import sphinx.environment
-import sphinx.roles
-from docutils import nodes
-
-
-def setup(app):
-    app.add_crossref_type(
-        directivename = "setting",
-        rolename = "setting",
-        indextemplate = "pair: %s; setting",
-    )
-    app.add_crossref_type(
-        directivename = "templatetag",
-        rolename = "ttag",
-        indextemplate = "pair: %s; template tag",
-    )
-    app.add_crossref_type(
-        directivename = "templatefilter",
-        rolename = "tfilter",
-        indextemplate = "pair: %s; template filter",
-    )
-    app.add_crossref_type(
-        directivename = "fieldlookup",
-        rolename = "lookup",
-        indextemplate = "pair: %s, field lookup type",
-    )
-    app.add_description_unit(
-        directivename = "django-admin",
-        rolename = "djadmin",
-        indextemplate = "pair: %s; django-admin command",
-        parse_node = parse_django_admin_node,
-    )
-    app.add_description_unit(
-        directivename = "django-admin-option",
-        rolename = "djadminopt",
-        indextemplate = "pair: %s; django-admin command-line option",
-        parse_node = lambda env, sig, signode: \
-                sphinx.directives.parse_option_desc(signode, sig),
-    )
-    app.add_config_value('django_next_version', '0.0', True)
-    app.add_directive('versionadded', parse_version_directive, 1, (1, 1, 1))
-    app.add_directive('versionchanged', parse_version_directive, 1, (1, 1, 1))
-    app.add_transform(SuppressBlockquotes)
-
-
-def parse_version_directive(name, arguments, options, content, lineno,
-                      content_offset, block_text, state, state_machine):
-    env = state.document.settings.env
-    is_nextversion = env.config.django_next_version == arguments[0]
-    ret = []
-    node = sphinx.addnodes.versionmodified()
-    ret.append(node)
-    if not is_nextversion:
-        if len(arguments) == 1:
-            linktext = 'Please, see the release notes <releases-%s>' % (
-                    arguments[0])
-            xrefs = sphinx.roles.xfileref_role('ref', linktext, linktext,
-                                               lineno, state)
-            node.extend(xrefs[0])
-        node['version'] = arguments[0]
-    else:
-        node['version'] = "Development version"
-    node['type'] = name
-    if len(arguments) == 2:
-        inodes, messages = state.inline_text(arguments[1], lineno+1)
-        node.extend(inodes)
-        if content:
-            state.nested_parse(content, content_offset, node)
-        ret = ret + messages
-    env.note_versionchange(node['type'], node['version'], node, lineno)
-    return ret
-
-
-class SuppressBlockquotes(docutils.transforms.Transform):
-    """
-    Remove the default blockquotes that encase indented list, tables, etc.
-    """
-    default_priority = 300
-
-    suppress_blockquote_child_nodes = (
-        docutils.nodes.bullet_list,
-        docutils.nodes.enumerated_list,
-        docutils.nodes.definition_list,
-        docutils.nodes.literal_block,
-        docutils.nodes.doctest_block,
-        docutils.nodes.line_block,
-        docutils.nodes.table,
-    )
-
-    def apply(self):
-        for node in self.document.traverse(docutils.nodes.block_quote):
-            if len(node.children) == 1 and \
-                    isinstance(node.children[0],
-                               self.suppress_blockquote_child_nodes):
-                node.replace_self(node.children[0])
-
-
-def parse_django_admin_node(env, sig, signode):
-    command = sig.split(' ')[0]
-    env._django_curr_admin_command = command
-    title = "django-admin.py %s" % sig
-    signode += sphinx.addnodes.desc_name(title, title)
-    return sig

+ 2 - 9
docs/conf.py

@@ -6,20 +6,13 @@ import os
 # If your extensions are in another directory, add it here. If the directory
 # is relative to the documentation root, use os.path.abspath to make it
 # absolute, like shown here.
-sys.path.append("../celery")
-sys.path.append("../tests")
-import settings
-from django.core.management import setup_environ
-from django.conf import settings as dsettings
-setup_environ(settings)
-dsettings.configure()
+sys.path.append(os.path.join(os.pardir, "tests"))
 import celery
-sys.path.append(os.path.join(os.path.dirname(__file__), "_ext"))
 
 # General configuration
 # ---------------------
 
-extensions = ['sphinx.ext.autodoc', 'sphinx.ext.coverage', 'djangodocs']
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.coverage']
 
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ['.templates']

+ 0 - 1
docs/cookbook/index.rst

@@ -7,7 +7,6 @@
 
     tasks
     daemonizing
-    unit-testing
 
 This page contains common recipes and techniques.
 Whenever a setting is mentioned, you should use ``celeryconf.py`` if using

+ 0 - 56
docs/cookbook/unit-testing.rst

@@ -1,56 +0,0 @@
-================
- Unit Testing
-================
-
-Testing with Django
--------------------
-
-The problem that you'll first run in to when trying to write a test that runs a
-task is that Django's test runner doesn't use the same database that your celery
-daemon is using. If you're using the database backend, this means that your
-tombstones won't show up in your test database and you won't be able to check
-on your tasks to get the return value or check the status.
-
-There are two ways to get around this. You can either take advantage of
-``CELERY_ALWAYS_EAGER = True`` to skip the daemon, or you can avoid testing
-anything that needs to check the status or result of a task.
-
-Using a custom test runner to test with celery
-----------------------------------------------
-
-If you're going the ``CELERY_ALWAYS_EAGER`` route, which is probably better than
-just never testing some parts of your app, a custom Django test runner does the
-trick. Celery provides a simple test runner, but it's easy enough to roll your
-own if you have other things that need to be done.
-http://docs.djangoproject.com/en/dev/topics/testing/#defining-a-test-runner
-
-For this example, we'll use the ``celery.contrib.test_runner`` to test the
-``add`` task from the :doc:`User Guide: Tasks<../userguide/tasks>` examples.
-
-To enable the test runner, set the following settings:
-
-.. code-block:: python
-
-    TEST_RUNNER = 'celery.contrib.test_runner.run_tests'
-
-
-Then we can write our actually test in a ``tests.py`` somewhere:
-
-.. code-block:: python
-
-    from django.test import TestCase
-    from myapp.tasks import add
-
-    class AddTestCase(TestCase):
-
-        def testNoError(self):
-            """Test that the ``add`` task runs with no errors,
-            and returns the correct result."""
-            result = add.delay(8, 8)
-
-            self.assertEquals(result.get(), 16)
-            self.assertTrue(result.successful())
-
-
-This test assumes that you put your example ``add`` task in ``maypp.tasks``
-so of course adjust the import for wherever you actually put the class.

+ 0 - 119
docs/getting-started/first-steps-with-django.rst

@@ -1,119 +0,0 @@
-=========================
- First steps with Django
-=========================
-
-Configuring your Django project to use Celery
-=============================================
-
-You only need three simple steps to use celery with your Django project.
-
-    1. Add ``celery`` to ``INSTALLED_APPS``.
-
-    2. Create the celery database tables::
-
-            $ python manage.py syncdb
-
-    3. Configure celery to use the AMQP user and virtual host we created
-        before, by adding the following to your ``settings.py``::
-
-            BROKER_HOST = "localhost"
-            BROKER_PORT = 5672
-            BROKER_USER = "myuser"
-            BROKER_PASSWORD = "mypassword"
-            BROKER_VHOST = "myvhost"
-
-
-That's it.
-
-There are more options available, like how many processes you want to
-work in parallel (the ``CELERY_CONCURRENCY`` setting). You can also
-configure the backend used for storing task statuses. For now though,
-this should do. For all of the options available, please see the 
-:doc:`configuration directive reference<../configuration>`.
-
-**Note:** If you're using SQLite as the Django database back-end,
-``celeryd`` will only be able to process one task at a time, this is
-because SQLite doesn't allow concurrent writes.
-
-
-
-Running the celery worker server
-================================
-
-To test this we'll be running the worker server in the foreground, so we can
-see what's going on without consulting the logfile::
-
-    $ python manage.py celeryd
-
-However, in production you probably want to run the worker in the
-background as a daemon. To do this you need to use to tools provided by your
-platform. See :doc:`daemon mode reference<../cookbook/daemonizing>`.
-
-For a complete listing of the command line options available, use the help command::
-
-    $ python manage.py help celeryd
-
-
-Defining and executing tasks
-============================
-
-**Please note:** All the tasks have to be stored in a real module, they can't
-be defined in the python shell or ipython/bpython. This is because the celery
-worker server needs access to the task function to be able to run it.
-Put them in the ``tasks`` module of your Django application. The
-worker server  will automatically load any ``tasks.py`` file for all
-of the applications listed in ``settings.INSTALLED_APPS``.
-Executing tasks using ``delay`` and ``apply_async`` can be done from the
-python shell, but keep in mind that since arguments are pickled, you can't
-use custom classes defined in the shell session.
-
-This is a task that adds two numbers:
-
-.. code-block:: python
-
-    from celery.decorators import task
-
-    @task()
-    def add(x, y):
-        return x + y
-
-To execute this task, we can use the ``delay`` method of the task class.
-This is a handy shortcut to the ``apply_async`` method which gives
-greater control of the task execution.
-See :doc:`Executing Tasks<../userguide/executing>` for more information.
-
-    >>> from myapp.tasks import MyTask
-    >>> MyTask.delay(some_arg="foo")
-
-At this point, the task has been sent to the message broker. The message
-broker will hold on to the task until a celery worker server has successfully
-picked it up.
-
-*Note:* If everything is just hanging when you execute ``delay``, please check
-that RabbitMQ is running, and that the user/password has access to the virtual
-host you configured earlier.
-
-Right now we have to check the celery worker log files to know what happened
-with the task. This is because we didn't keep the ``AsyncResult`` object
-returned by ``delay``.
-
-The ``AsyncResult`` lets us find the state of the task, wait for the task to
-finish and get its return value (or exception if the task failed).
-
-So, let's execute the task again, but this time we'll keep track of the task:
-
-    >>> result = add.delay(4, 4)
-    >>> result.ready() # returns True if the task has finished processing.
-    False
-    >>> result.result # task is not ready, so no return value yet.
-    None
-    >>> result.get()   # Waits until the task is done and returns the retval.
-    8
-    >>> result.result # direct access to result, doesn't re-raise errors.
-    8
-    >>> result.successful() # returns True if the task didn't end in failure.
-    True
-
-If the task raises an exception, the return value of ``result.successful()``
-will be ``False``, and ``result.result`` will contain the exception instance
-raised by the task.

+ 0 - 1
docs/getting-started/index.rst

@@ -11,6 +11,5 @@
     introduction
     broker-installation
     first-steps-with-celery
-    first-steps-with-django
     periodic-tasks
     resources

+ 12 - 27
docs/getting-started/periodic-tasks.rst

@@ -7,33 +7,27 @@ Here's an example of a periodic task:
 
 .. code-block:: python
 
-    from celery.task import PeriodicTask
-    from celery.registry import tasks
+    from celery.decorators import periodic_task
     from datetime import timedelta
 
-    class MyPeriodicTask(PeriodicTask):
-        run_every = timedelta(seconds=30)
-
-        def run(self, **kwargs):
-            logger = self.get_logger(**kwargs)
-            logger.info("Running periodic task!")
-    >>> tasks.register(MyPeriodicTask)
+    @periodic_task(run_every=timedelta(seconds=30))
+    def every_30_seconds(\*\*kwargs):
+        logger = self.get_logger(\*\*kwargs)
+        logger.info("Running periodic task!")
 
 If you want a little more control over when the task is executed, for example,
-a particular time of day or day of the week, you can use ``crontab`` to set
-the ``run_every`` property:
+a particular time of day or day of the week, you can use the ``crontab`` schedule
+type:
 
 .. code-block:: python
 
-    from celery.task import PeriodicTask
     from celery.task.schedules import crontab
+    from celery.decorators import periodic_task
 
-    class EveryMondayMorningTask(PeriodicTask):
-        run_every = crontab(hour=7, minute=30, day_of_week=1)
-
-        def run(self, **kwargs):
-            logger = self.get_logger(**kwargs)
-            logger.info("Execute every Monday at 7:30AM.")
+    @periodoc_task(run_every=crontab(hour=7, minute=30, day_of_week=1))
+    def every_monday_morning(\*\*kwargs):
+        logger = self.get_logger(\*\*kwargs)
+        logger.info("Execute every Monday at 7:30AM.")
 
 If you want to use periodic tasks you need to start the ``celerybeat``
 service. You have to make sure only one instance of this server is running at
@@ -43,16 +37,7 @@ To start the ``celerybeat`` service::
 
     $ celerybeat
 
-or if using Django::
-
-    $ python manage.py celerybeat
-
-
 You can also start ``celerybeat`` with ``celeryd`` by using the ``-B`` option,
 this is convenient if you only have one server::
 
     $ celeryd -B
-
-or if using Django::
-
-    $ python manage.py celeryd  -B

+ 14 - 10
docs/includes/introduction.txt

@@ -1,11 +1,11 @@
 .. image:: http://cloud.github.com/downloads/ask/celery/celery_favicon_128.png
 
-:Version: 1.0.3
+:Version: 1.1.0
 :Web: http://celeryproject.org/
 :Download: http://pypi.python.org/pypi/celery/
 :Source: http://github.com/ask/celery/
 :Keywords: task queue, job queue, asynchronous, rabbitmq, amqp, redis,
-  django, python, webhooks, queue, distributed
+  python, webhooks, queue, distributed
 
 --
 
@@ -18,14 +18,20 @@ more worker servers. Tasks can execute asynchronously (in the background) or syn
 
 Celery is already used in production to process millions of tasks a day.
 
-Celery was originally created for use with Django, but is now usable
-from any Python project. It can
-also `operate with other languages via webhooks`_.
+Celery is written in Python, but the protocol can be implemented in any
+language. It can also `operate with other languages using webhooks`_.
 
-The recommended message broker is `RabbitMQ`_, but support for Redis and
-databases is also available.
+The recommended message broker is `RabbitMQ`_, but support for `Redis`_ and
+databases (`SQLAlchemy`_) is also available.
 
-.. _`operate with other languages via webhooks`:
+You may also be pleased to know that full Django integration exists
+via the `django-celery`_ package.
+
+.. _`RabbitMQ`: http://www.rabbitmq.com/
+.. _`Redis`: http://code.google.com/p/redis/
+.. _`SQLAlchemy`: http://www.sqlalchemy.org/
+.. _`django-celery`: http://pypi.python.org/pypi/django-celery
+.. _`operate with other languages using webhooks`:
     http://ask.github.com/celery/userguide/remote-tasks.html
 
 Overview
@@ -147,12 +153,10 @@ Features
     +-----------------+----------------------------------------------------+
 
 
-.. _`RabbitMQ`: http://www.rabbitmq.com/
 .. _`clustering`: http://www.rabbitmq.com/clustering.html
 .. _`AMQP`: http://www.amqp.org/
 .. _`Stomp`: http://stomp.codehaus.org/
 .. _`MongoDB`: http://www.mongodb.org/
-.. _`Redis`: http://code.google.com/p/redis/
 .. _`Tokyo Tyrant`: http://tokyocabinet.sourceforge.net/
 
 Documentation

+ 0 - 26
docs/internals/moduleindex.rst

@@ -63,12 +63,6 @@ celery.task.base
 celery.task.http
 ----------------
 
-celery.task.rest
-----------------
-
-Backward compatible interface to :mod:`celery.task.http`.
-Will be deprecated in future versions.
-
 celery.task.control
 -------------------
 
@@ -98,23 +92,6 @@ Messaging
 celery.messaging
 ----------------
 
-Django-specific
-===============
-
-celery.models
--------------
-
-celery.managers
----------------
-
-celery.views
-------------
-
-celery.urls
------------
-
-celery.management
-
 Result backends
 ===============
 
@@ -145,9 +122,6 @@ celery.loaders.base - Loader base classes
 celery.loaders.default - The default loader
 -------------------------------------------
 
-celery.loaders.djangoapp - The Django loader
---------------------------------------------
-
 CeleryBeat
 ==========
 

+ 0 - 8
docs/internals/reference/celery.backends.cache.rst

@@ -1,8 +0,0 @@
-=======================================
-Backend: Cache - celery.backends.cache
-=======================================
-
-.. currentmodule:: celery.backends.cache
-
-.. automodule:: celery.backends.cache
-    :members:

+ 0 - 8
docs/internals/reference/celery.backends.database.rst

@@ -1,8 +0,0 @@
-=============================================
-Backend: Database - celery.backends.database
-=============================================
-
-.. currentmodule:: celery.backends.database
-
-.. automodule:: celery.backends.database
-    :members:

+ 0 - 8
docs/internals/reference/celery.managers.rst

@@ -1,8 +0,0 @@
-========================================
-Django Model Managers - celery.managers
-========================================
-
-.. currentmodule:: celery.managers
-
-.. automodule:: celery.managers
-    :members:

+ 0 - 76
docs/internals/reference/celery.models.rst

@@ -1,76 +0,0 @@
-===============================
-Django Models - celery.models
-===============================
-
-.. data:: TASK_STATUS_PENDING
-
-    The string status of a pending task.
-
-.. data:: TASK_STATUS_RETRY
-   
-    The string status of a task which is to be retried.
-
-.. data:: TASK_STATUS_FAILURE
-   
-    The string status of a failed task.
-
-.. data:: TASK_STATUS_DONE
-   
-    The string status of a task that was successfully executed.
-
-.. data:: TASK_STATUSES
-   
-    List of possible task statuses.
-
-.. data:: TASK_STATUSES_CHOICES
-   
-    Django choice tuple of possible task statuses, for usage in model/form
-    fields ``choices`` argument.
-
-.. class:: TaskMeta
-   
-    Model for storing the result and status of a task.
-    
-    *Note* Only used if you're running the ``database`` backend.
-
-    .. attribute:: task_id
-
-        The unique task id.
-
-    .. attribute:: status
-
-        The current status for this task.
-
-    .. attribute:: result
-        
-        The result after successful/failed execution. If the task failed,
-        this contains the execption it raised.
-
-    .. attribute:: date_done
-
-        The date this task changed status.
-
-.. class:: PeriodicTaskMeta
-   
-    Metadata model for periodic tasks.
-
-    .. attribute:: name
-       
-        The name of this task, as registered in the task registry.
-
-    .. attribute:: last_run_at
-
-        The date this periodic task was last run. Used to find out
-        when it should be run next.
-
-    .. attribute:: total_run_count
-       
-        The number of times this periodic task has been run.
-
-    .. attribute:: task
-       
-        The class/function for this task.
-
-    .. method:: delay()
-        Delay the execution of a periodic task, and increment its total
-        run count.

+ 0 - 4
docs/internals/reference/index.rst

@@ -24,8 +24,6 @@
     celery.backends
     celery.backends.base
     celery.backends.amqp
-    celery.backends.database
-    celery.backends.cache
     celery.backends.mongodb
     celery.backends.pyredis
     celery.backends.tyrant
@@ -38,5 +36,3 @@
     celery.utils.compat
     celery.utils.patch
     celery.platform
-    celery.managers
-    celery.models

+ 0 - 8
docs/reference/celery.bin.celeryinit.rst

@@ -1,8 +0,0 @@
-===========================================
- Celery Initialize - celery.bin.celeryinit
-===========================================
-
-.. currentmodule:: celery.bin.celeryinit
-
-.. automodule:: celery.bin.celeryinit
-    :members:

+ 2 - 3
docs/reference/celery.conf.rst

@@ -143,8 +143,7 @@ Configuration - celery.conf
 
 .. data:: CELERY_CACHE_BACKEND
 
-    Use a custom cache backend for celery. If not set the django-global
-    cache backend in ``CACHE_BACKEND`` will be used.
+    Celery cache backend.
 
 .. data:: SEND_EVENTS
 
@@ -231,7 +230,7 @@ Configuration - celery.conf
 .. data:: CELERYD_POOL
 
     Name of the task pool class used by the worker.
-    Default is ``"celery.worker.pool.TaskPool"`.
+    Default is ``"celery.worker.pool.TaskPool"``.
 
 .. data:: CELERYD_LISTENER
 

+ 0 - 8
docs/reference/celery.contrib.test_runner.rst

@@ -1,8 +0,0 @@
-===================================================
- Contrib: Test runner - celery.contrib.test_runner
-===================================================
-
-.. currentmodule:: celery.contrib.test_runner
-
-.. automodule:: celery.contrib.test_runner
-    :members:

+ 0 - 8
docs/reference/celery.loaders.djangoapp.rst

@@ -1,8 +0,0 @@
-==========================================
- Django Loader - celery.loaders.djangoapp
-==========================================
-
-.. currentmodule:: celery.loaders.djangoapp
-
-.. automodule:: celery.loaders.djangoapp
-    :members:

+ 0 - 8
docs/reference/celery.views.rst

@@ -1,8 +0,0 @@
-========================================
-Django Views - celery.views
-========================================
-
-.. currentmodule:: celery.views
-
-.. automodule:: celery.views
-    :members:

+ 0 - 4
docs/reference/index.rst

@@ -23,15 +23,11 @@
     celery.loaders
     celery.loaders.base
     celery.loaders.default
-    celery.loaders.djangoapp
     celery.registry
     celery.states
     celery.messaging
-    celery.contrib.test_runner
     celery.contrib.abortable
-    celery.views
     celery.events
     celery.bin.celeryd
     celery.bin.celerybeat
-    celery.bin.celeryinit
     celery.bin.camqadm

+ 10 - 12
docs/userguide/tasks.rst

@@ -415,24 +415,22 @@ yourself:
     >>> from celery import task
     >>> registry.tasks
     {'celery.delete_expired_task_meta':
-      <celery.task.builtins.DeleteExpiredTaskMetaTask object at 0x101d1f510>,
-    'celery.execute_remote':
-      <celery.task.base.ExecuteRemoteTask object at 0x101d17890>,
-    'celery.task.rest.RESTProxyTask':
-      <celery.task.rest.RESTProxyTask object at 0x101d1f410>,
-    'celery.task.rest.Task': <celery.task.rest.Task object at 0x101d1f4d0>,
-    'celery.map_async':
-      <celery.task.base.AsynchronousMapTask object at 0x101d17910>,
-    'celery.ping': <celery.task.builtins.PingTask object at 0x101d1f550>}
+        <PeriodicTask: celery.delete_expired_task_meta (periodic)>,
+     'celery.task.http.HttpDispatchTask':
+        <Task: celery.task.http.HttpDispatchTask (regular)>,
+     'celery.execute_remote':
+        <Task: celery.execute_remote (regular)>,
+     'celery.map_async':
+        <Task: celery.map_async (regular)>,
+     'celery.ping':
+        <Task: celery.ping (regular)>}
 
 This is the list of tasks built-in to celery. Note that we had to import
 ``celery.task`` first for these to show up. This is because the tasks will
 only be registered when the module they are defined in is imported.
 
 The default loader imports any modules listed in the
-``CELERY_IMPORTS`` setting. If using Django it loads all ``tasks.py`` modules
-for the applications listed in ``INSTALLED_APPS``. If you want to do something
-special you can create your own loader to do what you want.
+``CELERY_IMPORTS`` setting. 
 
 The entity responsible for registering your task in the registry is a
 meta class, :class:`TaskType`. This is the default meta class for

+ 0 - 4
examples/README.rst

@@ -7,10 +7,6 @@
 
 Example Python project using celery.
 
-* django
-
-Example Django project using celery.
-
 * httpexample
 
 Example project using remote tasks (webhook tasks)

+ 1 - 1
examples/celery_http_gateway/settings.py

@@ -95,5 +95,5 @@ INSTALLED_APPS = (
     'django.contrib.contenttypes',
     'django.contrib.sessions',
     'django.contrib.sites',
-    'celery',
+    'djcelery',
 )

+ 1 - 1
examples/celery_http_gateway/urls.py

@@ -1,7 +1,7 @@
 from django.conf.urls.defaults import *
 
 from celery.task.builtins import PingTask
-from celery import views as celery_views
+from djcelery import views as celery_views
 
 # Uncomment the next two lines to enable the admin:
 # from django.contrib import admin

+ 0 - 0
examples/django/demoproject/__init__.py


+ 0 - 0
examples/django/demoproject/demoapp/__init__.py


+ 0 - 3
examples/django/demoproject/demoapp/models.py

@@ -1,3 +0,0 @@
-from django.db import models
-
-# Create your models here.

+ 0 - 6
examples/django/demoproject/demoapp/tasks.py

@@ -1,6 +0,0 @@
-from celery.decorators import task
-
-
-@task
-def add(x, y):
-    return x + y

+ 0 - 1
examples/django/demoproject/demoapp/views.py

@@ -1 +0,0 @@
-# Create your views here.

+ 0 - 15
examples/django/demoproject/manage.py

@@ -1,15 +0,0 @@
-#!/usr/bin/env python
-from django.core.management import execute_manager
-try:
-    import settings # Assumed to be in the same directory.
-except ImportError:
-    import sys
-    sys.stderr.write("""Error: Can't find the file 'settings.py' in the \
-        directory containing %r. It appears you've customized things.
-        You'll have to run django-admin.py, passing it your settings module.
-        (If the file settings.py does indeed exist, it's causing an
-        ImportError somehow.)\n""" % __file__)
-    sys.exit(1)
-
-if __name__ == "__main__":
-    execute_manager(settings)

+ 0 - 80
examples/django/demoproject/settings.py

@@ -1,80 +0,0 @@
-# Django settings for demoproject project.
-
-DEBUG = True
-TEMPLATE_DEBUG = DEBUG
-
-ADMINS = (
-    # ('Your Name', 'your_email@domain.com'),
-)
-
-CELERY_RESULT_BACKEND = "database"
-BROKER_HOST = "localhost"
-BROKER_PORT = 5672
-BROKER_USER = "guest"
-BROKER_PASSWORD = "guest"
-BROKER_VHOST = "/"
-
-MANAGERS = ADMINS
-
-DATABASE_ENGINE = 'sqlite3'
-DATABASE_NAME = 'testdb.sqlite'
-DATABASE_USER = ''             # Not used with sqlite3.
-DATABASE_PASSWORD = ''         # Not used with sqlite3.
-DATABASE_HOST = ''             # Set to empty string for localhost.
-                               # Not used with sqlite3.
-DATABASE_PORT = ''             # Set to empty string for default.
-                               # Not used with sqlite3.
-
-INSTALLED_APPS = (
-    'django.contrib.auth',
-    'django.contrib.contenttypes',
-    'django.contrib.sessions',
-    'django.contrib.sites',
-    'celery',
-    'demoapp',
-    'twitterfollow',
-)
-
-TIME_ZONE = 'America/Chicago'
-LANGUAGE_CODE = 'en-us'
-SITE_ID = 1
-USE_I18N = True
-
-# Absolute path to the directory that holds media.
-# Example: "/home/media/media.lawrence.com/"
-MEDIA_ROOT = ''
-
-# URL that handles the media served from MEDIA_ROOT. Make sure to use a
-# trailing slash if there is a path component (optional in other cases).
-# Examples: "http://media.lawrence.com", "http://example.com/media/"
-MEDIA_URL = ''
-
-# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
-# trailing slash.
-# Examples: "http://foo.com/media/", "/media/".
-ADMIN_MEDIA_PREFIX = '/media/'
-
-# Make this unique, and don't share it with anybody.
-SECRET_KEY = '!j@j8x^6hr&ue7#n1w@d8zr@#=xqb#br4tjcjy50wuv_5rs7r('
-
-# List of callables that know how to import templates from various sources.
-TEMPLATE_LOADERS = (
-    'django.template.loaders.filesystem.load_template_source',
-    'django.template.loaders.app_directories.load_template_source',
-#     'django.template.loaders.eggs.load_template_source',
-)
-
-MIDDLEWARE_CLASSES = (
-    'django.middleware.common.CommonMiddleware',
-    'django.contrib.sessions.middleware.SessionMiddleware',
-    'django.contrib.auth.middleware.AuthenticationMiddleware',
-)
-
-ROOT_URLCONF = 'demoproject.urls'
-
-TEMPLATE_DIRS = (
-    # Put strings here, like "/home/html/django_templates" or
-    # "C:/www/django/templates".
-    # Always use forward slashes, even on Windows.
-    # Don't forget to use absolute paths, not relative paths.
-)

+ 0 - 0
examples/django/demoproject/twitterfollow/__init__.py


+ 0 - 20
examples/django/demoproject/twitterfollow/models.py

@@ -1,20 +0,0 @@
-from django.db import models
-
-
-class User(models.Model):
-    userid = models.PositiveIntegerField(unique=True)
-    screen_name = models.CharField(max_length=200)
-    name = models.CharField(max_length=200)
-    description = models.CharField(max_length=200)
-    favourites_count = models.PositiveIntegerField(default=0)
-    followers_count = models.PositiveIntegerField(default=0)
-    location = models.CharField(max_length=200)
-    statuses_count = models.PositiveIntegerField(default=0)
-    url = models.URLField(verify_exists=False)
-
-
-class Status(models.Model):
-    status_id = models.PositiveIntegerField()
-    screen_name = models.CharField(max_length=200)
-    created_at = models.DateTimeField()
-    text = models.CharField(max_length=200)

+ 0 - 0
examples/django/demoproject/twitterfollow/tasks.py


+ 0 - 1
examples/django/demoproject/twitterfollow/views.py

@@ -1 +0,0 @@
-# Create your views here.

+ 0 - 17
examples/django/demoproject/urls.py

@@ -1,17 +0,0 @@
-from django.conf.urls.defaults import patterns, include, url
-
-# Uncomment the next two lines to enable the admin:
-# from django.contrib import admin
-# admin.autodiscover()
-
-urlpatterns = patterns('',
-    # Example:
-    # (r'^demoproject/', include('demoproject.foo.urls')),
-
-    # Uncomment the admin/doc line below and add 'django.contrib.admindocs'
-    # to INSTALLED_APPS to enable admin documentation:
-    # (r'^admin/doc/', include('django.contrib.admindocs.urls')),
-
-    # Uncomment the next line to enable the admin:
-    # (r'^admin/', include(admin.site.urls)),
-)

+ 7 - 2
setup.cfg

@@ -1,3 +1,10 @@
+[nosetests]
+where = celery/tests
+cover3-branch = 1
+cover3-html = 1
+cover3-package = celery
+cover3-exclude = celery.__init__,celery.conf,celery.tests.*,celery.bin.celerybeat,celery.utils.patch,celery.utils.compat,celery.platform,celery.backends.mongodb,celery.backends.tyrant
+
 [build_sphinx]
 source-dir = docs/
 build-dir = docs/.build
@@ -10,9 +17,7 @@ upload-dir = docs/.build/html
 requires = python-uuid
            python-importlib
            python-multiprocessing==2.6.2.1
-           python-django
            python-dateutil
            python-anyjson
            python-carrot>=0.10.4
-           python-django-picklefield
            python-billiard>=0.3.0

+ 13 - 32
setup.py

@@ -7,41 +7,26 @@ import platform
 
 try:
     from setuptools import setup, find_packages, Command
+    from setuptools.command.test import test as TestCommand
 except ImportError:
     from ez_setup import use_setuptools
     use_setuptools()
     from setuptools import setup, find_packages, Command
+    from setuptools.command.test import test as TestCommand
 
 import celery as distmeta
 
 
-class RunTests(Command):
-    description = "Run the django test suite from the tests dir."
-    user_options = []
+class RunTests(TestCommand):
+    env = dict(CELERY_LOADER="default",
+               CELERY_CONFIG_MODULE="tests.celeryconfig",
+               CELERYINIT=1)
     extra_env = {}
 
-    def run(self):
-        for env_name, env_value in self.extra_env.items():
+    def run(self, *args, **kwargs):
+        for env_name, env_value in dict(self.env, **self.extra_env).items():
             os.environ[env_name] = str(env_value)
-
-        this_dir = os.getcwd()
-        testproj_dir = os.path.join(this_dir, "tests")
-        os.chdir(testproj_dir)
-        sys.path.append(testproj_dir)
-        from django.core.management import execute_manager
-        os.environ["DJANGO_SETTINGS_MODULE"] = os.environ.get(
-                        "DJANGO_SETTINGS_MODULE", "settings")
-        settings_file = os.environ["DJANGO_SETTINGS_MODULE"]
-        settings_mod = __import__(settings_file, {}, {}, [''])
-        execute_manager(settings_mod, argv=[
-            __file__, "test"])
-        os.chdir(this_dir)
-
-    def initialize_options(self):
-        pass
-
-    def finalize_options(self):
-        pass
+        TestCommand.run(self, *args, **kwargs)
 
 
 class QuickRunTests(RunTests):
@@ -49,11 +34,6 @@ class QuickRunTests(RunTests):
 
 install_requires = []
 
-try:
-    import django
-except ImportError:
-    install_requires.append("django")
-
 try:
     import importlib
 except ImportError:
@@ -62,9 +42,10 @@ except ImportError:
 
 install_requires.extend([
     "python-dateutil",
+    "mailer",
+    "sqlalchemy",
     "anyjson",
     "carrot>=0.10.4",
-    "django-picklefield",
     "billiard>=0.3.0"])
 
 py_version = sys.version_info
@@ -89,13 +70,13 @@ setup(
     platforms=["any"],
     license="BSD",
     packages=find_packages(exclude=['ez_setup', 'tests', 'tests.*']),
-    scripts=["bin/celeryd", "bin/celeryinit", "bin/celerybeat", "bin/camqadm"],
+    scripts=["bin/celeryd", "bin/celerybeat", "bin/camqadm"],
     zip_safe=False,
     install_requires=install_requires,
     cmdclass = {"test": RunTests, "quicktest": QuickRunTests},
+    test_suite="nose.collector",
     classifiers=[
         "Development Status :: 5 - Production/Stable",
-        "Framework :: Django",
         "Operating System :: OS Independent",
         "Programming Language :: Python",
         "Environment :: No Input/Output (Daemon)",

+ 17 - 0
tests/celeryconfig.py

@@ -0,0 +1,17 @@
+import atexit
+
+BROKER_HOST = "localhost"
+BROKER_PORT = 5672
+BROKER_USER = "guest"
+BROKER_PASSWORD = "guest"
+BROKER_VHOST = "/"
+
+CELERY_RESULT_BACKEND = "database"
+CELERY_RESULT_DBURI = "sqlite:///test.db"
+CELERY_SEND_TASK_ERROR_EMAILS = False
+
+@atexit.register
+def teardown_testdb():
+    import os
+    if os.path.exists("test.db"):
+        os.remove("test.db")

+ 0 - 16
tests/manage.py

@@ -1,16 +0,0 @@
-#!/usr/bin/env python
-from django.core.management import execute_manager
-try:
-    import settings # Assumed to be in the same directory.
-except ImportError:
-    import sys
-    sys.stderr.write(
-            "Error: Can't find the file 'settings.py' in the directory \
-            containing %r. It appears you've customized things.\n\
-            You'll have to run django-admin.py, passing it your settings\
-            module.\n(If the file settings.py does indeed exist, it's\
-            causing an ImportError somehow.)\n" % __file__)
-    sys.exit(1)
-
-if __name__ == "__main__":
-    execute_manager(settings)

+ 0 - 78
tests/settings.py

@@ -1,78 +0,0 @@
-# Django settings for testproj project.
-
-import os
-import sys
-# import source code dir
-sys.path.insert(0, os.getcwd())
-sys.path.insert(0, os.path.join(os.getcwd(), os.pardir))
-
-SITE_ID = 300
-
-DEBUG = True
-TEMPLATE_DEBUG = DEBUG
-
-ROOT_URLCONF = "urls"
-
-ADMINS = (
-    # ('Your Name', 'your_email@domain.com'),
-)
-
-TEST_RUNNER = "django_nose.run_tests"
-here = os.path.abspath(os.path.dirname(__file__))
-COVERAGE_EXCLUDE_MODULES = ("celery.__init__",
-                            "celery.conf",
-                            "celery.tests.*",
-                            "celery.management.*",
-                            "celery.contrib.*",
-                            "celery.bin.celeryinit",
-                            "celery.bin.celerybeat",
-                            "celery.utils.patch",
-                            "celery.utils.compat",
-                            "celery.task.rest",
-                            "celery.platform", # FIXME
-                            "celery.backends.mongodb", # FIXME
-                            "celery.backends.tyrant", # FIXME
-)
-
-NOSE_ARGS = [os.path.join(here, os.pardir, "celery", "tests"),
-            os.environ.get("NOSE_VERBOSE") and "--verbose" or "",
-            "--cover3-package=celery",
-            "--cover3-branch",
-            "--cover3-exclude=%s" % ",".join(COVERAGE_EXCLUDE_MODULES)]
-
-BROKER_HOST = "localhost"
-BROKER_PORT = 5672
-BROKER_VHOST = "/"
-BROKER_USER = "guest"
-BROKER_PASSWORD = "guest"
-
-TT_HOST = "localhost"
-TT_PORT = 1978
-
-CELERY_DEFAULT_EXCHANGE = "testcelery"
-CELERY_DEFAULT_ROUTING_KEY = "testcelery"
-CELERY_DEFAULT_QUEUE = "testcelery"
-
-CELERY_QUEUES = {"testcelery": {"binding_key": "testcelery"}}
-
-MANAGERS = ADMINS
-
-DATABASE_ENGINE = 'sqlite3'
-DATABASE_NAME = ':memory'
-DATABASE_USER = ''
-DATABASE_PASSWORD = ''
-DATABASE_HOST = ''
-DATABASE_PORT = ''
-
-INSTALLED_APPS = (
-    'django.contrib.auth',
-    'django.contrib.contenttypes',
-    'django.contrib.sessions',
-    'django.contrib.sites',
-    'django_nose',
-    'celery',
-    'someapp',
-    'someappwotask',
-)
-
-CELERY_SEND_TASK_ERROR_EMAILS = False

+ 0 - 0
tests/someapp/__init__.py


+ 0 - 3
tests/someapp/models.py

@@ -1,3 +0,0 @@
-from django.db import models
-
-# Create your models here.

+ 0 - 8
tests/someapp/tasks.py

@@ -1,8 +0,0 @@
-from celery.task import tasks, Task
-
-
-class SomeAppTask(Task):
-    name = "c.unittest.SomeAppTask"
-
-    def run(self, **kwargs):
-        return 42

Some files were not shown because too many files changed in this diff