From c5a381fbe95f0c08c3a5771247cd69b79b41a7de Mon Sep 17 00:00:00 2001 From: Malthe Borch Date: Wed, 11 Dec 2013 10:47:42 +0100 Subject: [PATCH] Remove dependency on 'times' library (see issue #286). Basically, for the functionality needed, a dependency on 'times' (which in turn depends on 'python-dateutil') seem unnecessary. --- CHANGES.md | 2 ++ rq/job.py | 15 +++++++-------- rq/queue.py | 6 +++--- rq/utils.py | 13 +++++++++++++ rq/worker.py | 9 ++++----- setup.py | 2 +- tests/helpers.py | 6 +++--- tests/test_job.py | 18 +++++++++--------- tests/test_worker.py | 4 ++-- 9 files changed, 44 insertions(+), 31 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 32b1fc5..3af5328 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,8 @@ ### 0.4.0 (not released yet) +- Removed dependency on the `times` library. Thanks, Malthe! + - Job dependencies! Thanks, Selwin. - `Queue.all()` and `rqinfo` now report empty queues, too. Thanks, Rob! diff --git a/rq/job.py b/rq/job.py index ed95b84..ebe38a0 100644 --- a/rq/job.py +++ b/rq/job.py @@ -1,5 +1,4 @@ import inspect -import times from uuid import uuid4 try: from cPickle import loads, dumps, UnpicklingError @@ -8,7 +7,7 @@ except ImportError: # noqa from .local import LocalStack from .connections import resolve_connection from .exceptions import UnpickleError, NoSuchJobError -from .utils import import_attribute +from .utils import import_attribute, utcnow, utcformat, utcparse from rq.compat import text_type, decode_redis_hash, as_text @@ -192,7 +191,7 @@ class Job(object): def __init__(self, id=None, connection=None): self.connection = resolve_connection(connection) self._id = id - self.created_at = times.now() + self.created_at = utcnow() self._func_name = None self._instance = None self._args = None @@ -293,9 +292,9 @@ class Job(object): def to_date(date_str): if date_str is None: - return None + return else: - return times.to_universal(as_text(date_str)) + return utcparse(as_text(date_str)) try: self.data = obj['data'] @@ -323,7 +322,7 @@ class Job(object): def dump(self): """Returns a serialization of the current job instance""" obj = {} - obj['created_at'] = times.format(self.created_at or times.now(), 'UTC') + obj['created_at'] = utcformat(self.created_at or utcnow()) if self.func_name is not None: obj['data'] = dumps(self.job_tuple) @@ -332,9 +331,9 @@ class Job(object): if self.description is not None: obj['description'] = self.description if self.enqueued_at is not None: - obj['enqueued_at'] = times.format(self.enqueued_at, 'UTC') + obj['enqueued_at'] = utcformat(self.enqueued_at) if self.ended_at is not None: - obj['ended_at'] = times.format(self.ended_at, 'UTC') + obj['ended_at'] = utcformat(self.ended_at) if self._result is not None: obj['result'] = dumps(self._result) if self.exc_info is not None: diff --git a/rq/queue.py b/rq/queue.py index 02308e0..8b6e0c6 100644 --- a/rq/queue.py +++ b/rq/queue.py @@ -1,8 +1,8 @@ -import times import uuid from .connections import resolve_connection from .job import Job, Status +from .utils import utcnow from .exceptions import (DequeueTimeout, InvalidJobOperationError, NoSuchJobError, UnpickleError) @@ -225,7 +225,7 @@ class Queue(object): if set_meta_data: job.origin = self.name - job.enqueued_at = times.now() + job.enqueued_at = utcnow() if job.timeout is None: job.timeout = self.DEFAULT_TIMEOUT @@ -371,7 +371,7 @@ class FailedQueue(Queue): must not be overridden (e.g. `origin` or `enqueued_at`) and other meta data must be inserted (`ended_at` and `exc_info`). """ - job.ended_at = times.now() + job.ended_at = utcnow() job.exc_info = exc_info return self.enqueue_job(job, set_meta_data=False) diff --git a/rq/utils.py b/rq/utils.py index 7927a0d..f182210 100644 --- a/rq/utils.py +++ b/rq/utils.py @@ -6,6 +6,7 @@ The formatter for ANSI colored console output is heavily based on Pygments terminal colorizing code, originally by Georg Brandl. """ import importlib +import datetime import logging import os import sys @@ -168,3 +169,15 @@ def import_attribute(name): module_name, attribute = name.rsplit('.', 1) module = importlib.import_module(module_name) return getattr(module, attribute) + + +def utcnow(): + return datetime.datetime.utcnow() + + +def utcformat(dt): + return dt.strftime(u"%Y-%m-%dT%H:%M:%SZ") + + +def utcparse(string): + return datetime.datetime.strptime(string, "%Y-%m-%dT%H:%M:%SZ") diff --git a/rq/worker.py b/rq/worker.py index 69b54a1..f5b1f09 100644 --- a/rq/worker.py +++ b/rq/worker.py @@ -3,7 +3,6 @@ import os import errno import random import time -import times try: from procname import setprocname except ImportError: @@ -16,7 +15,7 @@ import logging from .queue import Queue, get_failed_queue from .connections import get_current_connection from .job import Job, Status -from .utils import make_colorizer +from .utils import make_colorizer, utcnow, utcformat from .logutils import setup_loghandlers from .exceptions import NoQueueError, UnpickleError, DequeueTimeout from .timeouts import death_penalty_after @@ -197,7 +196,7 @@ class Worker(object): queues = ','.join(self.queue_names()) with self.connection._pipeline() as p: p.delete(key) - p.hset(key, 'birth', times.format(times.now(), 'UTC')) + p.hset(key, 'birth', utcformat(utcnow())) p.hset(key, 'queues', queues) p.sadd(self.redis_workers_keys, key) p.expire(key, self.default_worker_ttl) @@ -210,7 +209,7 @@ class Worker(object): # We cannot use self.state = 'dead' here, because that would # rollback the pipeline p.srem(self.redis_workers_keys, self.key) - p.hset(self.key, 'death', times.format(times.now(), 'UTC')) + p.hset(self.key, 'death', utcformat(utcnow())) p.expire(self.key, 60) p.execute() @@ -410,7 +409,7 @@ class Worker(object): # use the same exc handling when pickling fails job._result = rv job._status = Status.FINISHED - job.ended_at = times.now() + job.ended_at = utcnow() result_ttl = job.get_ttl(self.default_result_ttl) pipeline = self.connection._pipeline() diff --git a/setup.py b/setup.py index 2d0b06c..c5c5741 100644 --- a/setup.py +++ b/setup.py @@ -17,7 +17,7 @@ def get_version(): def get_dependencies(): - deps = ['redis >= 2.4.13', 'times'] + deps = ['redis >= 2.4.13'] if sys.version_info < (2, 7) or \ (sys.version_info >= (3, 0) and sys.version_info < (3, 1)): deps += ['importlib'] diff --git a/tests/helpers.py b/tests/helpers.py index 32d47ed..16f1717 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -1,6 +1,6 @@ -import times +from datetime import timedelta -def strip_milliseconds(date): - return times.to_universal(times.format(date, 'UTC')) +def strip_microseconds(date): + return date - timedelta(microseconds=date.microsecond) diff --git a/tests/test_job.py b/tests/test_job.py index ac7f85d..03d8155 100644 --- a/tests/test_job.py +++ b/tests/test_job.py @@ -1,8 +1,7 @@ -import times from datetime import datetime from tests import RQTestCase from tests.fixtures import Number, some_calculation, say_hello, access_self -from tests.helpers import strip_milliseconds +from tests.helpers import strip_microseconds try: from cPickle import loads except ImportError: @@ -11,6 +10,7 @@ from rq.compat import as_text from rq.job import Job, get_current_job from rq.exceptions import NoSuchJobError, UnpickleError from rq.queue import Queue +from rq.utils import utcformat class TestJob(RQTestCase): @@ -91,7 +91,7 @@ class TestJob(RQTestCase): self.testconn.hset('rq:job:some_id', 'data', "(S'tests.fixtures.some_calculation'\nN(I3\nI4\nt(dp1\nS'z'\nI2\nstp2\n.") self.testconn.hset('rq:job:some_id', 'created_at', - '2012-02-07 22:13:24+0000') + '2012-02-07T22:13:24Z') # Fetch returns a job job = Job.fetch('some_id') @@ -108,11 +108,11 @@ class TestJob(RQTestCase): job = Job() job.save() - expected_date = strip_milliseconds(job.created_at) + expected_date = strip_microseconds(job.created_at) stored_date = self.testconn.hget(job.key, 'created_at').decode('utf-8') self.assertEquals( - times.to_universal(stored_date), - expected_date) + stored_date, + utcformat(expected_date)) # ... and no other keys are stored self.assertEqual( @@ -124,11 +124,11 @@ class TestJob(RQTestCase): job = Job.create(func=some_calculation, args=(3, 4), kwargs=dict(z=2)) job.save() - expected_date = strip_milliseconds(job.created_at) + expected_date = strip_microseconds(job.created_at) stored_date = self.testconn.hget(job.key, 'created_at').decode('utf-8') self.assertEquals( - times.to_universal(stored_date), - expected_date) + stored_date, + utcformat(expected_date)) # ... and no other keys are stored self.assertEqual( diff --git a/tests/test_worker.py b/tests/test_worker.py index 7a6cfa7..2720526 100644 --- a/tests/test_worker.py +++ b/tests/test_worker.py @@ -3,7 +3,7 @@ from time import sleep from tests import RQTestCase, slow from tests.fixtures import say_hello, div_by_zero, do_nothing, create_file, \ create_file_after_timeout -from tests.helpers import strip_milliseconds +from tests.helpers import strip_microseconds from rq import Queue, Worker, get_failed_queue from rq.job import Job, Status @@ -87,7 +87,7 @@ class TestWorker(RQTestCase): self.assertEquals(q.count, 1) # keep for later - enqueued_at_date = strip_milliseconds(job.enqueued_at) + enqueued_at_date = strip_microseconds(job.enqueued_at) w = Worker([q]) w.work(burst=True) # should silently pass