Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(335)

Side by Side Diff: third_party/twisted_8_1/twisted/protocols/memcache.py

Issue 12261012: Remove third_party/twisted_8_1 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 # -*- test-case-name: twisted.test.test_memcache -*-
2 # Copyright (c) 2007 Twisted Matrix Laboratories.
3 # See LICENSE for details.
4
5 """
6 Memcache client protocol. Memcached is a caching server, storing data in the
7 form of pairs key/value, and memcache is the protocol to talk with it.
8
9 To connect to a server, create a factory for L{MemCacheProtocol}::
10
11 from twisted.internet import reactor, protocol
12 from twisted.protocols.memcache import MemCacheProtocol, DEFAULT_PORT
13 d = protocol.ClientCreator(reactor, MemCacheProtocol
14 ).connectTCP("localhost", DEFAULT_PORT)
15 def doSomething(proto):
16 # Here you call the memcache operations
17 return proto.set("mykey", "a lot of data")
18 d.addCallback(doSomething)
19 reactor.run()
20
21 All the operations of the memcache protocol are present, but
22 L{MemCacheProtocol.set} and L{MemCacheProtocol.get} are the more important.
23
24 See U{http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt} for
25 more information about the protocol.
26 """
27
28 try:
29 from collections import deque
30 except ImportError:
31 class deque(list):
32 def popleft(self):
33 return self.pop(0)
34
35
36 from twisted.protocols.basic import LineReceiver
37 from twisted.protocols.policies import TimeoutMixin
38 from twisted.internet.defer import Deferred, fail, TimeoutError
39 from twisted.python import log
40
41
42
43 DEFAULT_PORT = 11211
44
45
46
47 class NoSuchCommand(Exception):
48 """
49 Exception raised when a non existent command is called.
50 """
51
52
53
54 class ClientError(Exception):
55 """
56 Error caused by an invalid client call.
57 """
58
59
60
61 class ServerError(Exception):
62 """
63 Problem happening on the server.
64 """
65
66
67
68 class Command(object):
69 """
70 Wrap a client action into an object, that holds the values used in the
71 protocol.
72
73 @ivar _deferred: the L{Deferred} object that will be fired when the result
74 arrives.
75 @type _deferred: L{Deferred}
76
77 @ivar command: name of the command sent to the server.
78 @type command: C{str}
79 """
80
81 def __init__(self, command, **kwargs):
82 """
83 Create a command.
84
85 @param command: the name of the command.
86 @type command: C{str}
87
88 @param kwargs: this values will be stored as attributes of the object
89 for future use
90 """
91 self.command = command
92 self._deferred = Deferred()
93 for k, v in kwargs.items():
94 setattr(self, k, v)
95
96
97 def success(self, value):
98 """
99 Shortcut method to fire the underlying deferred.
100 """
101 self._deferred.callback(value)
102
103
104 def fail(self, error):
105 """
106 Make the underlying deferred fails.
107 """
108 self._deferred.errback(error)
109
110
111
112 class MemCacheProtocol(LineReceiver, TimeoutMixin):
113 """
114 MemCache protocol: connect to a memcached server to store/retrieve values.
115
116 @ivar persistentTimeOut: the timeout period used to wait for a response.
117 @type persistentTimeOut: C{int}
118
119 @ivar _current: current list of requests waiting for an answer from the
120 server.
121 @type _current: C{deque} of L{Command}
122
123 @ivar _lenExpected: amount of data expected in raw mode, when reading for
124 a value.
125 @type _lenExpected: C{int}
126
127 @ivar _getBuffer: current buffer of data, used to store temporary data
128 when reading in raw mode.
129 @type _getBuffer: C{list}
130
131 @ivar _bufferLength: the total amount of bytes in C{_getBuffer}.
132 @type _bufferLength: C{int}
133 """
134 MAX_KEY_LENGTH = 250
135
136 def __init__(self, timeOut=60):
137 """
138 Create the protocol.
139
140 @param timeOut: the timeout to wait before detecting that the
141 connection is dead and close it. It's expressed in seconds.
142 @type timeOut: C{int}
143 """
144 self._current = deque()
145 self._lenExpected = None
146 self._getBuffer = None
147 self._bufferLength = None
148 self.persistentTimeOut = self.timeOut = timeOut
149
150
151 def timeoutConnection(self):
152 """
153 Close the connection in case of timeout.
154 """
155 for cmd in self._current:
156 cmd.fail(TimeoutError("Connection timeout"))
157 self.transport.loseConnection()
158
159
160 def sendLine(self, line):
161 """
162 Override sendLine to add a timeout to response.
163 """
164 if not self._current:
165 self.setTimeout(self.persistentTimeOut)
166 LineReceiver.sendLine(self, line)
167
168
169 def rawDataReceived(self, data):
170 """
171 Collect data for a get.
172 """
173 self.resetTimeout()
174 self._getBuffer.append(data)
175 self._bufferLength += len(data)
176 if self._bufferLength >= self._lenExpected + 2:
177 data = "".join(self._getBuffer)
178 buf = data[:self._lenExpected]
179 rem = data[self._lenExpected + 2:]
180 val = buf
181 self._lenExpected = None
182 self._getBuffer = None
183 self._bufferLength = None
184 cmd = self._current[0]
185 cmd.value = val
186 self.setLineMode(rem)
187
188
189 def cmd_STORED(self):
190 """
191 Manage a success response to a set operation.
192 """
193 self._current.popleft().success(True)
194
195
196 def cmd_NOT_STORED(self):
197 """
198 Manage a specific 'not stored' response to a set operation: this is not
199 an error, but some condition wasn't met.
200 """
201 self._current.popleft().success(False)
202
203
204 def cmd_END(self):
205 """
206 This the end token to a get or a stat operation.
207 """
208 cmd = self._current.popleft()
209 if cmd.command == "get":
210 cmd.success((cmd.flags, cmd.value))
211 elif cmd.command == "gets":
212 cmd.success((cmd.flags, cmd.cas, cmd.value))
213 elif cmd.command == "stats":
214 cmd.success(cmd.values)
215
216
217 def cmd_NOT_FOUND(self):
218 """
219 Manage error response for incr/decr/delete.
220 """
221 self._current.popleft().success(False)
222
223
224 def cmd_VALUE(self, line):
225 """
226 Prepare the reading a value after a get.
227 """
228 cmd = self._current[0]
229 if cmd.command == "get":
230 key, flags, length = line.split()
231 cas = ""
232 else:
233 key, flags, length, cas = line.split()
234 self._lenExpected = int(length)
235 self._getBuffer = []
236 self._bufferLength = 0
237 if cmd.key != key:
238 raise RuntimeError("Unexpected commands answer.")
239 cmd.flags = int(flags)
240 cmd.length = self._lenExpected
241 cmd.cas = cas
242 self.setRawMode()
243
244
245 def cmd_STAT(self, line):
246 """
247 Reception of one stat line.
248 """
249 cmd = self._current[0]
250 key, val = line.split(" ", 1)
251 cmd.values[key] = val
252
253
254 def cmd_VERSION(self, versionData):
255 """
256 Read version token.
257 """
258 self._current.popleft().success(versionData)
259
260
261 def cmd_ERROR(self):
262 """
263 An non-existent command has been sent.
264 """
265 log.err("Non-existent command sent.")
266 cmd = self._current.popleft()
267 cmd.fail(NoSuchCommand())
268
269
270 def cmd_CLIENT_ERROR(self, errText):
271 """
272 An invalid input as been sent.
273 """
274 log.err("Invalid input: %s" % (errText,))
275 cmd = self._current.popleft()
276 cmd.fail(ClientError(errText))
277
278
279 def cmd_SERVER_ERROR(self, errText):
280 """
281 An error has happened server-side.
282 """
283 log.err("Server error: %s" % (errText,))
284 cmd = self._current.popleft()
285 cmd.fail(ServerError(errText))
286
287
288 def cmd_DELETED(self):
289 """
290 A delete command has completed successfully.
291 """
292 self._current.popleft().success(True)
293
294
295 def cmd_OK(self):
296 """
297 The last command has been completed.
298 """
299 self._current.popleft().success(True)
300
301
302 def cmd_EXISTS(self):
303 """
304 A C{checkAndSet} update has failed.
305 """
306 self._current.popleft().success(False)
307
308
309 def lineReceived(self, line):
310 """
311 Receive line commands from the server.
312 """
313 self.resetTimeout()
314 token = line.split(" ", 1)[0]
315 # First manage standard commands without space
316 cmd = getattr(self, "cmd_%s" % (token,), None)
317 if cmd is not None:
318 args = line.split(" ", 1)[1:]
319 if args:
320 cmd(args[0])
321 else:
322 cmd()
323 else:
324 # Then manage commands with space in it
325 line = line.replace(" ", "_")
326 cmd = getattr(self, "cmd_%s" % (line,), None)
327 if cmd is not None:
328 cmd()
329 else:
330 # Increment/Decrement response
331 cmd = self._current.popleft()
332 val = int(line)
333 cmd.success(val)
334 if not self._current:
335 # No pending request, remove timeout
336 self.setTimeout(None)
337
338
339 def increment(self, key, val=1):
340 """
341 Increment the value of C{key} by given value (default to 1).
342 C{key} must be consistent with an int. Return the new value.
343
344 @param key: the key to modify.
345 @type key: C{str}
346
347 @param val: the value to increment.
348 @type val: C{int}
349
350 @return: a deferred with will be called back with the new value
351 associated with the key (after the increment).
352 @rtype: L{Deferred}
353 """
354 return self._incrdecr("incr", key, val)
355
356
357 def decrement(self, key, val=1):
358 """
359 Decrement the value of C{key} by given value (default to 1).
360 C{key} must be consistent with an int. Return the new value, coerced to
361 0 if negative.
362
363 @param key: the key to modify.
364 @type key: C{str}
365
366 @param val: the value to decrement.
367 @type val: C{int}
368
369 @return: a deferred with will be called back with the new value
370 associated with the key (after the decrement).
371 @rtype: L{Deferred}
372 """
373 return self._incrdecr("decr", key, val)
374
375
376 def _incrdecr(self, cmd, key, val):
377 """
378 Internal wrapper for incr/decr.
379 """
380 if not isinstance(key, str):
381 return fail(ClientError(
382 "Invalid type for key: %s, expecting a string" % (type(key),)))
383 if len(key) > self.MAX_KEY_LENGTH:
384 return fail(ClientError("Key too long"))
385 fullcmd = "%s %s %d" % (cmd, key, int(val))
386 self.sendLine(fullcmd)
387 cmdObj = Command(cmd, key=key)
388 self._current.append(cmdObj)
389 return cmdObj._deferred
390
391
392 def replace(self, key, val, flags=0, expireTime=0):
393 """
394 Replace the given C{key}. It must already exist in the server.
395
396 @param key: the key to replace.
397 @type key: C{str}
398
399 @param val: the new value associated with the key.
400 @type val: C{str}
401
402 @param flags: the flags to store with the key.
403 @type flags: C{int}
404
405 @param expireTime: if different from 0, the relative time in seconds
406 when the key will be deleted from the store.
407 @type expireTime: C{int}
408
409 @return: a deferred that will fire with C{True} if the operation has
410 succeeded, and C{False} with the key didn't previously exist.
411 @rtype: L{Deferred}
412 """
413 return self._set("replace", key, val, flags, expireTime, "")
414
415
416 def add(self, key, val, flags=0, expireTime=0):
417 """
418 Add the given C{key}. It must not exist in the server.
419
420 @param key: the key to add.
421 @type key: C{str}
422
423 @param val: the value associated with the key.
424 @type val: C{str}
425
426 @param flags: the flags to store with the key.
427 @type flags: C{int}
428
429 @param expireTime: if different from 0, the relative time in seconds
430 when the key will be deleted from the store.
431 @type expireTime: C{int}
432
433 @return: a deferred that will fire with C{True} if the operation has
434 succeeded, and C{False} with the key already exists.
435 @rtype: L{Deferred}
436 """
437 return self._set("add", key, val, flags, expireTime, "")
438
439
440 def set(self, key, val, flags=0, expireTime=0):
441 """
442 Set the given C{key}.
443
444 @param key: the key to set.
445 @type key: C{str}
446
447 @param val: the value associated with the key.
448 @type val: C{str}
449
450 @param flags: the flags to store with the key.
451 @type flags: C{int}
452
453 @param expireTime: if different from 0, the relative time in seconds
454 when the key will be deleted from the store.
455 @type expireTime: C{int}
456
457 @return: a deferred that will fire with C{True} if the operation has
458 succeeded.
459 @rtype: L{Deferred}
460 """
461 return self._set("set", key, val, flags, expireTime, "")
462
463
464 def checkAndSet(self, key, val, cas, flags=0, expireTime=0):
465 """
466 Change the content of C{key} only if the C{cas} value matches the
467 current one associated with the key. Use this to store a value which
468 hasn't been modified since last time you fetched it.
469
470 @param key: The key to set.
471 @type key: C{str}
472
473 @param val: The value associated with the key.
474 @type val: C{str}
475
476 @param cas: Unique 64-bit value returned by previous call of C{get}.
477 @type cas: C{str}
478
479 @param flags: The flags to store with the key.
480 @type flags: C{int}
481
482 @param expireTime: If different from 0, the relative time in seconds
483 when the key will be deleted from the store.
484 @type expireTime: C{int}
485
486 @return: A deferred that will fire with C{True} if the operation has
487 succeeded, C{False} otherwise.
488 @rtype: L{Deferred}
489 """
490 return self._set("cas", key, val, flags, expireTime, cas)
491
492
493 def _set(self, cmd, key, val, flags, expireTime, cas):
494 """
495 Internal wrapper for setting values.
496 """
497 if not isinstance(key, str):
498 return fail(ClientError(
499 "Invalid type for key: %s, expecting a string" % (type(key),)))
500 if len(key) > self.MAX_KEY_LENGTH:
501 return fail(ClientError("Key too long"))
502 if not isinstance(val, str):
503 return fail(ClientError(
504 "Invalid type for value: %s, expecting a string" %
505 (type(val),)))
506 if cas:
507 cas = " " + cas
508 length = len(val)
509 fullcmd = "%s %s %d %d %d%s" % (
510 cmd, key, flags, expireTime, length, cas)
511 self.sendLine(fullcmd)
512 self.sendLine(val)
513 cmdObj = Command(cmd, key=key, flags=flags, length=length)
514 self._current.append(cmdObj)
515 return cmdObj._deferred
516
517
518 def append(self, key, val):
519 """
520 Append given data to the value of an existing key.
521
522 @param key: The key to modify.
523 @type key: C{str}
524
525 @param val: The value to append to the current value associated with
526 the key.
527 @type val: C{str}
528
529 @return: A deferred that will fire with C{True} if the operation has
530 succeeded, C{False} otherwise.
531 @rtype: L{Deferred}
532 """
533 # Even if flags and expTime values are ignored, we have to pass them
534 return self._set("append", key, val, 0, 0, "")
535
536
537 def prepend(self, key, val):
538 """
539 Prepend given data to the value of an existing key.
540
541 @param key: The key to modify.
542 @type key: C{str}
543
544 @param val: The value to prepend to the current value associated with
545 the key.
546 @type val: C{str}
547
548 @return: A deferred that will fire with C{True} if the operation has
549 succeeded, C{False} otherwise.
550 @rtype: L{Deferred}
551 """
552 # Even if flags and expTime values are ignored, we have to pass them
553 return self._set("prepend", key, val, 0, 0, "")
554
555
556 def get(self, key, withIdentifier=False):
557 """
558 Get the given C{key}. It doesn't support multiple keys. If
559 C{withIdentifier} is set to C{True}, the command issued is a C{gets},
560 that will return the current identifier associated with the value. This
561 identifier has to be used when issuing C{checkAndSet} update later,
562 using the corresponding method.
563
564 @param key: The key to retrieve.
565 @type key: C{str}
566
567 @param withIdentifier: If set to C{True}, retrieve the current
568 identifier along with the value and the flags.
569 @type withIdentifier: C{bool}
570
571 @return: A deferred that will fire with the tuple (flags, value) if
572 C{withIdentifier} is C{False}, or (flags, cas identifier, value)
573 if C{True}.
574 @rtype: L{Deferred}
575 """
576 if not isinstance(key, str):
577 return fail(ClientError(
578 "Invalid type for key: %s, expecting a string" % (type(key),)))
579 if len(key) > self.MAX_KEY_LENGTH:
580 return fail(ClientError("Key too long"))
581 if withIdentifier:
582 cmd = "gets"
583 else:
584 cmd = "get"
585 fullcmd = "%s %s" % (cmd, key)
586 self.sendLine(fullcmd)
587 cmdObj = Command(cmd, key=key, value=None, flags=0, cas="")
588 self._current.append(cmdObj)
589 return cmdObj._deferred
590
591
592 def stats(self):
593 """
594 Get some stats from the server. It will be available as a dict.
595
596 @return: a deferred that will fire with a C{dict} of the available
597 statistics.
598 @rtype: L{Deferred}
599 """
600 self.sendLine("stats")
601 cmdObj = Command("stats", values={})
602 self._current.append(cmdObj)
603 return cmdObj._deferred
604
605
606 def version(self):
607 """
608 Get the version of the server.
609
610 @return: a deferred that will fire with the string value of the
611 version.
612 @rtype: L{Deferred}
613 """
614 self.sendLine("version")
615 cmdObj = Command("version")
616 self._current.append(cmdObj)
617 return cmdObj._deferred
618
619
620 def delete(self, key):
621 """
622 Delete an existing C{key}.
623
624 @param key: the key to delete.
625 @type key: C{str}
626
627 @return: a deferred that will be called back with C{True} if the key
628 was successfully deleted, or C{False} if not.
629 @rtype: L{Deferred}
630 """
631 if not isinstance(key, str):
632 return fail(ClientError(
633 "Invalid type for key: %s, expecting a string" % (type(key),)))
634 self.sendLine("delete %s" % key)
635 cmdObj = Command("delete", key=key)
636 self._current.append(cmdObj)
637 return cmdObj._deferred
638
639
640 def flushAll(self):
641 """
642 Flush all cached values.
643
644 @return: a deferred that will be called back with C{True} when the
645 operation has succeeded.
646 @rtype: L{Deferred}
647 """
648 self.sendLine("flush_all")
649 cmdObj = Command("flush_all")
650 self._current.append(cmdObj)
651 return cmdObj._deferred
652
653
654
655 __all__ = ["MemCacheProtocol", "DEFAULT_PORT", "NoSuchCommand", "ClientError",
656 "ServerError"]
657
OLDNEW
« no previous file with comments | « third_party/twisted_8_1/twisted/protocols/loopback.py ('k') | third_party/twisted_8_1/twisted/protocols/mice/__init__.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698