Browse Source

Merge branch 'master' into app

Conflicts:
	celery/apps/beat.py
	celery/task/base.py
	celery/tests/test_bin/test_celerybeat.py
	celery/tests/test_worker_job.py
	celery/worker/job.py
Ask Solem 14 years ago
parent
commit
324947e105

+ 1 - 0
AUTHORS

@@ -39,3 +39,4 @@ Ordered by date of first contribution:
   Frédéric Junod <frederic.junod@camptocamp.com>
   Lukas Linhart <lukas.linhart@centrumholdings.com>
   Clay Gerrard
+  David Miller <il.livid.dream@gmail.com>

+ 12 - 12
celery/tests/test_bin/test_celerybeat.py

@@ -5,7 +5,7 @@ import unittest2 as unittest
 from celery import beat
 from celery import platform
 from celery.bin import celerybeat as celerybeat_bin
-from celery.apps import beat as celerybeat
+from celery.apps import beat as beatapp
 
 
 class MockService(beat.Service):
@@ -19,14 +19,14 @@ class MockService(beat.Service):
         self.__class__.in_sync = True
 
 
-class MockBeat(celerybeat.Beat):
+class MockBeat(beatapp.Beat):
     running = False
 
     def run(self):
         self.__class__.running = True
 
 
-class MockBeat2(celerybeat.Beat):
+class MockBeat2(beatapp.Beat):
     Service = MockService
 
     def install_sync_handler(self, b):
@@ -36,22 +36,22 @@ class MockBeat2(celerybeat.Beat):
 class test_Beat(unittest.TestCase):
 
     def test_loglevel_string(self):
-        b = celerybeat.Beat(loglevel="DEBUG")
+        b = beatapp.Beat(loglevel="DEBUG")
         self.assertEqual(b.loglevel, logging.DEBUG)
 
-        b2 = celerybeat.Beat(loglevel=logging.DEBUG)
+        b2 = beatapp.Beat(loglevel=logging.DEBUG)
         self.assertEqual(b2.loglevel, logging.DEBUG)
 
     def test_init_loader(self):
-        b = celerybeat.Beat()
+        b = beatapp.Beat()
         b.init_loader()
 
     def test_startup_info(self):
-        b = celerybeat.Beat()
+        b = beatapp.Beat()
         self.assertIn("@stderr", b.startup_info())
 
     def test_process_title(self):
-        b = celerybeat.Beat()
+        b = beatapp.Beat()
         b.set_process_title()
 
     def test_run(self):
@@ -74,7 +74,7 @@ class test_Beat(unittest.TestCase):
             platform.install_signal_handler = p
 
     def test_install_sync_handler(self):
-        b = celerybeat.Beat()
+        b = beatapp.Beat()
         clock = MockService()
         MockService.in_sync = False
         handlers = self.psig(b.install_sync_handler, clock)
@@ -87,10 +87,10 @@ class test_Beat(unittest.TestCase):
 class test_div(unittest.TestCase):
 
     def setUp(self):
-        self.prev, celerybeat.Beat = celerybeat.Beat, MockBeat
+        self.prev, beatapp.Beat = beatapp.Beat, MockBeat
 
     def tearDown(self):
-        celerybeat.Beat = self.prev
+        beatapp.Beat = self.prev
 
     def test_main(self):
         sys.argv = [sys.argv[0], "-s", "foo"]
@@ -102,7 +102,7 @@ class test_div(unittest.TestCase):
 
     def test_run_celerybeat(self):
         try:
-            celerybeat.run_celerybeat()
+            beatapp.run_celerybeat()
             self.assertTrue(MockBeat.running)
         finally:
             MockBeat.running = False

+ 34 - 10
celery/utils/compat.py

@@ -290,10 +290,31 @@ except ImportError:
     collections.defaultdict = defaultdict # Pickle needs this.
 
 ############## logging.LoggerAdapter ########################################
+import inspect
 import logging
 import multiprocessing
 import sys
 
+from logging import LogRecord
+
+# The func argument to LogRecord was added in 2.5
+if "func" not in inspect.getargspec(LogRecord.__init__)[0]:
+    def LogRecord(name, level, fn, lno, msg, args, exc_info, func):
+        return logging.LogRecord(name, level, fn, lno, msg, args, exc_info)
+
+
+def _checkLevel(level):
+    if isinstance(level, int):
+        rv = level
+    elif str(level) == level:
+        if level not in logging._levelNames:
+            raise ValueError("Unknown level: %r" % level)
+        rv = logging._levelNames[level]
+    else:
+        raise TypeError("Level not an integer or a valid string: %r" % level)
+    return rv
+
+
 class _CompatLoggerAdapter(object):
 
     def __init__(self, logger, extra):
@@ -301,42 +322,41 @@ class _CompatLoggerAdapter(object):
         self.extra = extra
 
     def setLevel(self, level):
-        self.logger.level = logging._checkLevel(level)
+        self.logger.level = _checkLevel(level)
 
     def process(self, msg, kwargs):
         kwargs["extra"] = self.extra
         return msg, kwargs
 
     def debug(self, msg, *args, **kwargs):
-        self.log(logging.DEBUG, msg, args, **kwargs)
+        self.log(logging.DEBUG, msg, *args, **kwargs)
 
     def info(self, msg, *args, **kwargs):
-        self.log(logging.INFO, msg, args, **kwargs)
+        self.log(logging.INFO, msg, *args, **kwargs)
 
     def warning(self, msg, *args, **kwargs):
-        self.log(logging.WARNING, msg, args, **kwargs)
+        self.log(logging.WARNING, msg, *args, **kwargs)
     warn = warning
 
     def error(self, msg, *args, **kwargs):
-        self.log(logging.ERROR, msg, args, **kwargs)
+        self.log(logging.ERROR, msg, *args, **kwargs)
 
     def exception(self, msg, *args, **kwargs):
         kwargs.setdefault("exc_info", 1)
         self.error(msg, *args, **kwargs)
 
     def critical(self, msg, *args, **kwargs):
-        self.log(logging.CRITICAL, msg, args, **kwargs)
+        self.log(logging.CRITICAL, msg, *args, **kwargs)
     fatal = critical
 
-    def log(self, level, msg, args, **kwargs):
+    def log(self, level, msg, *args, **kwargs):
         if self.logger.isEnabledFor(level):
             msg, kwargs = self.process(msg, kwargs)
             self._log(level, msg, args, **kwargs)
 
     def makeRecord(self, name, level, fn, lno, msg, args, exc_info,
             func=None, extra=None):
-        rv = logging.LogRecord(name, level, fn, lno,
-                               msg, args, exc_info, func)
+        rv = LogRecord(name, level, fn, lno, msg, args, exc_info, func)
         if extra is not None:
             for key, value in extra.items():
                 if key in ("message", "asctime") or key in rv.__dict__:
@@ -362,7 +382,7 @@ class _CompatLoggerAdapter(object):
             if not isinstance(exc_info, tuple):
                 exc_info = sys.exc_info()
         record = self.makeRecord(self.logger.name, level, fn, lno, msg,
-                                    args, exc_info, func, extra)
+                                 args, exc_info, func, extra)
         self.logger.handle(record)
 
     def isEnabledFor(self, level):
@@ -374,6 +394,10 @@ class _CompatLoggerAdapter(object):
     def removeHandler(self, hdlr):
         self.logger.removeHandler(hdlr)
 
+    @property
+    def level(self):
+        return self.logger.level
+
 
 try:
     from logging import LoggerAdapter

+ 6 - 1
celery/utils/timer2.py

@@ -151,7 +151,12 @@ class Timer(Thread):
             if sleep is None:
                 break
             sleep(delay)
-        self._stopped.set()
+        try:
+            self._stopped.set()
+        except TypeError:
+            # we lost the race at interpreter shutdown,
+            # so gc collected built-in modules.
+            pass
 
     def stop(self):
         if self.running:

+ 7 - 1
celery/worker/heartbeat.py

@@ -38,7 +38,13 @@ class Heart(threading.Thread):
 
         last_beat = None
         while 1:
-            now = time()
+            try:
+                now = time()
+            except TypeError:
+                # we lost the race at interpreter shutdown,
+                # so time has been collected by gc.
+                return
+
             if not last_beat or now > last_beat + (60.0 / bpm):
                 last_beat = now
                 dispatch("worker-heartbeat")

+ 8 - 9
docs/userguide/executing.rst

@@ -66,18 +66,17 @@ specified date and time has passed, but not necessarily at that exact time.
 
 While ``countdown`` is an integer, ``eta`` must be a :class:`~datetime.datetime` object,
 specifying an exact date and time in the future. This is good if you already
-have a :class:`~datetime.datetime`` object and need to modify it with a
+have a :class:`~datetime.datetime` object and need to modify it with a
 :class:`~datetime.timedelta`, or when using time in seconds is not very readable.
 
 .. code-block:: python
 
     from datetime import datetime, timedelta
 
-    def quickban(username):
-        """Ban user for 24 hours."""
-        ban(username)
+    def add_tomorrow(username):
+        """Add this tomorrow."""
         tomorrow = datetime.now() + timedelta(days=1)
-        UnbanTask.apply_async(args=[username], eta=tomorrow)
+        add.apply_async(args=[10, 10], eta=tomorrow)
 
 
 Serializers
@@ -198,15 +197,15 @@ listen to different queues:
 
 .. code-block:: python
 
-    >>> CompressVideoTask.apply_async(args=[filename],
+    >>> add.apply_async(args=[filename],
     ...                               routing_key="video.compress")
 
-    >>> ImageRotateTask.apply_async(args=[filename, 360],
+    >>> add.apply_async(args=[filename, 360],
     ...                             routing_key="image.rotate")
 
-    >>> ImageCropTask.apply_async(args=[filename, selection],
+    >>> add.apply_async(args=[filename, selection],
     ...                           routing_key="image.crop")
-    >>> UpdateReccomendationsTask.apply_async(routing_key="misc.recommend")
+    >>> add.apply_async(routing_key="misc.recommend")
 
 
 Later, if the crop task is consuming a lot of resources,