This repository has been archived by the owner on Jun 5, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 130
/
__init__.py
245 lines (193 loc) · 7.52 KB
/
__init__.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
__version__ = '0.5.0'
from redis import Redis
import pyres.json_parser as json
import logging
def my_import(name):
"""Helper function for walking import calls when searching for classes by string names."""
mod = __import__(name)
components = name.split('.')
for comp in components[1:]:
mod = getattr(mod, comp)
return mod
def safe_str_to_class(s):
"""Helper function to map string class names to module classes."""
lst = s.split(".")
klass = lst[-1]
mod_list = lst[:-1]
module = ".".join(mod_list)
mod = my_import(module)
if hasattr(mod, klass):
return getattr(mod, klass)
else:
raise ImportError('')
def str_to_class(s):
"""Alternate helper function to map string class names to module classes."""
lst = s.split(".")
klass = lst[-1]
mod_list = lst[:-1]
module = ".".join(mod_list)
try:
mod = __import__(module)
if hasattr(mod, klass):
return getattr(mod, klass)
else:
return None
except ImportError:
return None
class ResQ(object):
"""The ResQ class defines the Redis server object to which we will
enqueue jobs into various queues.
The ``__init__`` takes these keyword arguments:
``server`` -- IP address and port of the Redis server to which you want to connect. Default is `localhost:6379`.
``password`` -- The password, if required, of your Redis server. Default is "None".
``timeout`` -- The timeout keyword is in the signature, but is unused. Default is "None".
``retry_connection`` -- This keyword is in the signature but is deprecated. Default is "True".
Both ``timeout`` and ``retry_connection`` will be removed as the python-redis client
no longer uses them.
Example usage::
>>> from pyres import *
>>> r = ResQ(server="192.168.1.10:6379", password="some_pwd")
# Assuming redis is running on default port with no password
**r** is a resque object on which we can enqueue tasks.::
>>>> r.enqueue(SomeClass, args)
SomeClass can be any python class with a *perform* method and a *queue*
attribute on it.
"""
def __init__(self, server="localhost:6379", password=None):
self.redis = server
if password:
self.redis.auth(password)
self._watched_queues = set()
def push(self, queue, item):
self.watch_queue(queue)
self.redis.rpush("resque:queue:%s" % queue, ResQ.encode(item))
def pop(self, queue):
ret = self.redis.lpop("resque:queue:%s" % queue)
if ret:
return ResQ.decode(ret)
return ret
def size(self, queue):
return int(self.redis.llen("resque:queue:%s" % queue))
def watch_queue(self, queue):
if queue in self._watched_queues:
return
else:
if self.redis.sadd('resque:queues',str(queue)):
self._watched_queues.add(queue)
def peek(self, queue, start=0, count=1):
return self.list_range('resque:queue:%s' % queue, start, count)
def list_range(self, key, start, count):
items = self.redis.lrange(key, start,start+count-1) or []
ret_list = []
for i in items:
ret_list.append(ResQ.decode(i))
return ret_list
def _get_redis(self):
return self._redis
def _set_redis(self, server):
if isinstance(server, basestring):
self.dsn = server
host, port = server.split(':')
self._redis = Redis(host=host, port=int(port))
elif isinstance(server, Redis):
self.dsn = '%s:%s' % (server.host,server.port)
self._redis = server
else:
raise Exception("I don't know what to do with %s" % str(server))
redis = property(_get_redis, _set_redis)
def enqueue(self, klass, *args):
"""Enqueue a job into a specific queue. Make sure the class you are passing
has **queue** attribute and a **perform** method on it.
"""
queue = getattr(klass,'queue', None)
if queue:
class_name = '%s.%s' % (klass.__module__, klass.__name__)
self.push(queue, {'class':class_name,'args':args})
logging.info("enqueued '%s' job" % class_name)
if args:
logging.debug("job arguments: %s" % str(args))
else:
logging.debug("no arguments passed in.")
else:
logging.warning("unable to enqueue job with class %s" % str(klass))
def enqueue_from_string(self, klass_as_string, queue, *args):
self.push(queue, {'class':klass_as_string,'args':args})
logging.info("enqueued '%s' job" % klass_as_string)
if args:
logging.debug("job arguments: %s" % str(args))
else:
logging.debug("no arguments passed in.")
def queues(self):
return self.redis.smembers("resque:queues") or []
def info(self):
"""Returns a dictionary of the current status of the pending jobs,
processed, no. of queues, no. of workers, no. of failed jobs.
"""
pending = 0
for q in self.queues():
pending += self.size(q)
return {
'pending' : pending,
'processed' : Stat('processed',self).get(),
'queues' : len(self.queues()),
'workers' : len(self.workers()),
#'working' : len(self.working()),
'failed' : Stat('failed',self).get(),
'servers' : ['%s:%s' % (self.redis.host, self.redis.port)]
}
def keys(self):
return [key.replace('resque:','') for key in self.redis.keys('*')]
def reserve(self, queue):
from pyres.job import Job
return Job.reserve(queue, self)
def __str__(self):
return "PyRes Client connected to %s" % self.redis.server
def workers(self):
from pyres.worker import Worker
return Worker.all(self)
def working(self):
from pyres.worker import Worker
return Worker.working(self)
def remove_queue(self, queue):
if queue in self._watched_queues:
self._watched_queues.remove(queue)
self.redis.srem('resque:queues',queue)
del self.redis['resque:queue:%s' % queue]
def close(self):
"""Close the underlying redis connection.
"""
self.redis.disconnect()
@classmethod
def encode(cls, item):
return json.dumps(item)
@classmethod
def decode(cls, item):
if item:
ret = json.loads(item)
return ret
return None
@classmethod
def _enqueue(cls, klass, *args):
queue = getattr(klass,'queue', None)
_self = cls()
if queue:
class_name = '%s.%s' % (klass.__module__, klass.__name__)
_self.push(queue, {'class':class_name,'args':args})
class Stat(object):
"""A Stat class which shows the current status of the queue.
"""
def __init__(self, name, resq):
self.name = name
self.key = "resque:stat:%s" % self.name
self.resq = resq
def get(self):
val = self.resq.redis.get(self.key)
if val:
return int(val)
return 0
def incr(self, ammount=1):
self.resq.redis.incr(self.key, ammount)
def decr(self, ammount=1):
self.resq.redis.decr(self.key, ammount)
def clear(self):
self.resq.redis.delete(self.key)