Memcache Client for Python¶
Experimental memcached client library for python. This project is in WIP status, please don’t use it in production environment.
Key features:
Based on memcached’s new meta commands;
Asyncio support;
Type hints.
Installation¶
$ pip install memcache
API document¶
Core client¶
- class memcache.AsyncMemcache(addr: tuple[str, int] | list[tuple[str, int]] | None = None, *, pool_size: int | None = 23, pool_timeout: int | None = 1, load_func: ~collections.abc.Callable[[str | bytes, bytes, int], ~typing.Any] = <function load>, dump_func: ~collections.abc.Callable[[str | bytes, ~typing.Any], tuple[bytes, int]] = <function dump>, username: str | None = None, password: str | None = None)¶
Async Memcache client.
- Parameters:
addr –
memcached server addresses to be connected.
The address can be a two elements tuple, as
(ip, port)format.The address can be None, thus the default server
("localhost", 11211)should be used.The address can be a list of tuple, like
[("192.168.1.10", 11211), ("192.168.1.11", 11211)]. In this situation, the keys will be hashed to one of those servers by consistent hash algorithm.pool_size – The connection pool size. This size will be used as the max number to keep the connections for future uses.
pool_timeout – If the there is no available connection in the pool, and the
pool_sizeis reached, wait the specified time to get an available connection, or a asyncio.TimeoutError is raised.load_func – Function to load the bytes content from memcached to python values.
dump_func – Function to dump the python values to bytes content to store in memcached.
username – Memcached ASCII protocol authentication username.
password – Memcached ASCII protocol authentication password.
- async add(key: bytes | str, value: Any, *, expire: int | None = None) bool¶
- async append(key: bytes | str, value: Any) bool¶
- async cas(key: bytes | str, value: Any, cas_token: int, *, expire: int | None = None) None¶
Store a value using compare-and-swap operation.
- Parameters:
key – The key to store
value – The value to store
cas_token – The CAS token from a previous gets operation
expire – Optional expiration time in seconds
- Raises:
MemcacheError – If the CAS token doesn’t match or other error occurs
- async close() None¶
- async decr(key: bytes | str, value: int = 1) int¶
- async delete(key: bytes | str) bool¶
- async execute_meta_command(command: MetaCommand) MetaResult¶
- async flush_all() None¶
- async get(key: bytes | str) Any | None¶
- async get_many(keys: list[bytes | str]) dict[str, Any]¶
- async gets(key: bytes | str) tuple[Any, int] | None¶
Get a value and its CAS token from memcached.
- Parameters:
key – The key to retrieve
- Returns:
A tuple of (value, cas_token) or None if key doesn’t exist
- async incr(key: bytes | str, value: int = 1) int¶
- async prepend(key: bytes | str, value: Any) bool¶
- async replace(key: bytes | str, value: Any, *, expire: int | None = None) bool¶
- async set(key: bytes | str, value: Any, *, expire: int | None = None) None¶
- async touch(key: bytes | str, expire: int) bool¶
- class memcache.Memcache(addr: tuple[str, int] | list[tuple[str, int]] | None = None, *, pool_size: int | None = 23, pool_timeout: int | None = 1, load_func: ~collections.abc.Callable[[str | bytes, bytes, int], ~typing.Any] = <function load>, dump_func: ~collections.abc.Callable[[str | bytes, ~typing.Any], tuple[bytes, int]] = <function dump>, username: str | None = None, password: str | None = None)¶
Memcache client.
- Parameters:
addr –
memcached server addresses to be connected.
The address can be a two elements tuple, as
(ip, port)format.The address can be None, thus the default server
("localhost", 11211)should be used.The address can be a list of tuple, like
[("192.168.1.10", 11211), ("192.168.1.11", 11211)]. In this situation, the keys will be hashed to one of those servers by consistent hash algorithm.pool_size – The connection pool size. This size will be used as the max number to keep the connections for future uses.
pool_timeout – If the there is no available connection in the pool, and the
pool_sizeis reached, wait the specified time to get an available connection, or a queue.Empty is raised.load_func – Function to load the bytes content from memcached to python values.
dump_func – Function to dump the python values to bytes content to store in memcached.
username – Memcached ASCII protocol authentication username.
password – Memcached ASCII protocol authentication password.
- add(key: bytes | str, value: Any, *, expire: int | None = None) bool¶
- append(key: bytes | str, value: Any) bool¶
- cas(key: bytes | str, value: Any, cas_token: int, *, expire: int | None = None) None¶
Store a value using compare-and-swap operation.
- Parameters:
key – The key to store
value – The value to store
cas_token – The CAS token from a previous gets operation
expire – Optional expiration time in seconds
- Raises:
MemcacheError – If the CAS token doesn’t match or other error occurs
- close() None¶
- decr(key: bytes | str, value: int = 1) int¶
- delete(key: bytes | str) bool¶
- execute_meta_command(command: MetaCommand) MetaResult¶
- flush_all() None¶
- get(key: bytes | str) Any | None¶
- get_many(keys: list[bytes | str]) dict[str, Any]¶
- gets(key: bytes | str) tuple[Any, int] | None¶
Get a value and its CAS token from memcached.
- Parameters:
key – The key to retrieve
- Returns:
A tuple of (value, cas_token) or None if key doesn’t exist
- incr(key: bytes | str, value: int = 1) int¶
- prepend(key: bytes | str, value: Any) bool¶
- replace(key: bytes | str, value: Any, *, expire: int | None = None) bool¶
- set(key: bytes | str, value: Any, *, expire: int | None = None) None¶
- touch(key: bytes | str, expire: int) bool¶
- exception memcache.MemcacheError¶
Meta client (experimental)¶
Note
MetaClient and AsyncMetaClient live under memcache.experiment and their API may change in any minor release. If you depend on them, pin the minor version in your dependency spec (e.g. memcache~=0.14.0).
MetaClient is a high-level client for memcached’s meta protocol. The core methods (get/set/delete/increment/batch) cover the full protocol surface, while convenience wrappers such as add, cas, invalidate and get_with_lease package common usage patterns with safer defaults. Every result has an explicit status.
from memcache.experiment import Get, GetStatus, Meta, MetaClient, Set
with MetaClient(("localhost", 11211)) as client:
client.set("key", {"message": "value"}, ttl=60)
result = client.get("key", meta=Meta.CAS | Meta.TTL | Meta.SIZE)
if result.status is GetStatus.HIT:
print(result.value, result.item.cas, result.item.ttl)
AsyncMetaClient has the same concepts and call shape; its methods and lease fulfill() are awaited.
For protocol experts, client.meta maps the wire commands one-to-one (mg/ms/md/ma/me) with one keyword argument per protocol flag. It works on raw bytes and returns lightly parsed responses without serialization or semantic mapping.
- exception memcache.experiment.AmbiguousWriteError(result: Any | None = None)¶
A request was sent but its terminal response was not received.
- class memcache.experiment.Arithmetic(key: 'Key', delta: 'int' = 1, decrement: 'bool' = False, initial: 'int | None' = None, initial_ttl: 'int | None' = None, ttl: 'int | None' = None, compare_cas: 'int | None' = None, version: 'int | None' = None, return_cas: 'bool' = False, return_ttl: 'bool' = False)¶
- compare_cas: int | None = None¶
- decrement: bool = False¶
- delta: int = 1¶
- initial: int | None = None¶
- initial_ttl: int | None = None¶
- key: str | bytes¶
- return_cas: bool = False¶
- return_ttl: bool = False¶
- ttl: int | None = None¶
- version: int | None = None¶
- class memcache.experiment.ArithmeticResult(key: 'Key', status: 'MutationStatus', value: 'int | None' = None, item: 'ItemMeta' = ItemMeta(cas=None, ttl=None, size=None, last_access=None, hit_before=None), error: 'BaseException | None' = None)¶
- error: BaseException | None = None¶
- key: str | bytes¶
- status: MutationStatus¶
- value: int | None = None¶
- class memcache.experiment.AsyncMetaClient(addr: tuple[str, int] | list[tuple[str, int]] | None = None, *, pool_size: int | None = 23, pool_timeout: int | None = 1, timeout: float | None = 1.0, serializer: Serializer | None = None, username: str | None = None, password: str | None = None)¶
High-level meta protocol client with a batch-first executor.
- async add(key: str | bytes, value: Any, *, ttl: int | None = None, version: int | None = None, return_cas: bool = False, timeout: float | None = None) MutationResult¶
Store only if the key does not exist; ALREADY_EXISTS otherwise.
- async append(key: str | bytes, value: bytes, *, vivify_ttl: int | None = None, timeout: float | None = None) MutationResult¶
Append bytes to an existing value; serialization is skipped.
- async batch(operations: Sequence[Get | Set | Delete | Arithmetic], *, timeout: float | None = None) BatchResult¶
- async cas(key: str | bytes, value: Any, cas_token: int, *, ttl: int | None = None, version: int | None = None, return_cas: bool = False, timeout: float | None = None) MutationResult¶
Store only if the item’s CAS still matches; CAS_MISMATCH otherwise.
- async close() None¶
- async decrement(key: str | bytes, delta: int = 1, *, initial: int | None = None, initial_ttl: int | None = None, ttl: int | None = None, compare_cas: int | None = None, version: int | None = None, return_cas: bool = False, return_ttl: bool = False, timeout: float | None = None) ArithmeticResult¶
Decrement a counter; saturates at zero instead of underflowing.
- async delete(key: str | bytes, *, compare_cas: int | None = None, timeout: float | None = None) MutationResult¶
- async execute_meta_command(command: MetaCommand, *, timeout: float | None = None) MetaResult¶
- async flush_all(delay: int = 0) None¶
- async get(key: str | bytes, *, value: bool = True, meta: Meta = <Meta.NONE: 0>, touch: int | None = None, no_lru_bump: bool = False, unless_cas: int | None = None, lease_ttl: int | None = None, refresh_before: int | None = None, timeout: float | None = None) GetResult[Any]¶
Read a key; covers every mg capability.
With
lease_ttlthe result is aLeaseResult; useget_with_lease()for the statically typed variant.
- async get_with_lease(key: str | bytes, *, lease_ttl: int, refresh_before: int | None = None, meta: Meta = <Meta.NONE: 0>, timeout: float | None = None) LeaseResult[Any]¶
- async increment(key: str | bytes, delta: int = 1, *, initial: int | None = None, initial_ttl: int | None = None, ttl: int | None = None, compare_cas: int | None = None, version: int | None = None, return_cas: bool = False, return_ttl: bool = False, timeout: float | None = None) ArithmeticResult¶
Increment a counter; overflows wrap around (unsigned 64-bit).
- async inspect(key: str | bytes, *, meta: Meta = <Meta.CAS|TTL|SIZE: 7>, no_lru_bump: bool = True, timeout: float | None = None) GetResult[Any]¶
- async invalidate(key: str | bytes, *, stale_for: int | None = None, compare_cas: int | None = None, timeout: float | None = None) MutationResult¶
- async prepend(key: str | bytes, value: bytes, *, vivify_ttl: int | None = None, timeout: float | None = None) MutationResult¶
Prepend bytes to an existing value; serialization is skipped.
- async set(key: str | bytes, value: Any, *, ttl: int | None = None, mode: str = 'set', compare_cas: int | None = None, version: int | None = None, return_cas: bool = False, vivify_ttl: int | None = None, timeout: float | None = None) MutationResult¶
Store a key; covers every ms capability.
modeis one ofset/add/replace/append/prepend; the concatenation modes take bytes only and skip serialization.
- async touch(key: str | bytes, ttl: int, *, timeout: float | None = None) MutationResult¶
- class memcache.experiment.BatchResult(results: Sequence[GetResult[Any] | MutationResult | ArithmeticResult])¶
- class memcache.experiment.CompressedSerializer(serializer: Serializer, *, min_size: int = 1024, level: int | None = None)¶
Wraps another serializer, compressing large payloads with zstd.
zstd dominates the cache-hot-path tradeoff: better ratio than zlib at a fraction of the CPU, with decompression speed where a read-heavy cache spends its time (stdlib on 3.14+,
backports.zstdbefore). Reads sniff the payload header instead of trusting a codec label, so zstd, zlib, gzip, bz2, and lzma/xz payloads are all readable regardless of which client or library version wrote them.FLAG_COMPRESSEDis set only when compression actually shrank the payload; incompressible values are stored as-is.FLAG_INTvalues are never compressed because the server-side arithmetic commands parse the stored bytes directly.- dump(key: str | bytes, value: Any) tuple[bytes, int]¶
- load(key: str | bytes, value: bytes, flags: int) Any¶
- class memcache.experiment.Delete(key: 'Key', compare_cas: 'int | None' = None, invalidate: 'bool' = False, stale_for: 'int | None' = None)¶
- compare_cas: int | None = None¶
- invalidate: bool = False¶
- key: str | bytes¶
- stale_for: int | None = None¶
- class memcache.experiment.Get(key: 'Key', meta: 'Meta' = <Meta.NONE: 0>, touch: 'int | None' = None, no_lru_bump: 'bool' = False, unless_cas: 'int | None' = None, value: 'bool' = True, lease_ttl: 'int | None' = None, refresh_before: 'int | None' = None)¶
- key: str | bytes¶
- lease_ttl: int | None = None¶
- no_lru_bump: bool = False¶
- refresh_before: int | None = None¶
- touch: int | None = None¶
- unless_cas: int | None = None¶
- value: bool = True¶
- class memcache.experiment.GetResult(*, key: str | bytes, status: GetStatus, value: Any = <object object>, item: ItemMeta | None = None, value_state: ValueState = ValueState.MISSING, lease_state: LeaseState = LeaseState.NONE, error: BaseException | None = None)¶
- property already_won: bool¶
- property cas_token: int | None¶
- property hit_before: bool | None¶
- property is_stale: bool¶
- property last_access: int | None¶
- property size: int | None¶
- property ttl: int | None¶
- property value: T¶
- value_or(default: T) T¶
- property won_recache: bool¶
- class memcache.experiment.GetStatus(*values)¶
- AMBIGUOUS = 6¶
- FAILED = 5¶
- HIT = 1¶
- MISS = 2¶
- PENDING = 3¶
- UNCHANGED = 4¶
- class memcache.experiment.ItemMeta(cas: 'int | None' = None, ttl: 'int | None' = None, size: 'int | None' = None, last_access: 'int | None' = None, hit_before: 'bool | None' = None)¶
- cas: int | None = None¶
- hit_before: bool | None = None¶
- last_access: int | None = None¶
- size: int | None = None¶
- ttl: int | None = None¶
- class memcache.experiment.JsonSerializer¶
Falls back to JSON for arbitrary objects.
Cross-language but lossy: tuples come back as lists, dict keys are coerced to str, and bytes inside containers are rejected.
- dump_object(key: str | bytes, value: Any) tuple[bytes, int]¶
- load_object(key: str | bytes, value: bytes, flags: int) Any¶
- class memcache.experiment.LeaseResult(*, fulfill: Callable[[...], Any], **kwargs: Any)¶
- fulfill(value: T, *, ttl: int | None = None, version: int | None = None, return_cas: bool = False, timeout: float | None = None) Any¶
- class memcache.experiment.Meta(*values)¶
- CAS = 1¶
- HIT_BEFORE = 16¶
- LAST_ACCESS = 8¶
- NONE = 0¶
- SIZE = 4¶
- TTL = 2¶
- class memcache.experiment.MetaClient(addr: tuple[str, int] | list[tuple[str, int]] | None = None, *, pool_size: int | None = 23, pool_timeout: int | None = 1, timeout: float | None = 1.0, serializer: Serializer | None = None, username: str | None = None, password: str | None = None)¶
Native synchronous meta protocol client.
- add(key: str | bytes, value: Any, *, ttl: int | None = None, version: int | None = None, return_cas: bool = False, timeout: float | None = None) MutationResult¶
Store only if the key does not exist; ALREADY_EXISTS otherwise.
- append(key: str | bytes, value: bytes, *, vivify_ttl: int | None = None, timeout: float | None = None) MutationResult¶
Append bytes to an existing value; serialization is skipped.
- batch(operations: Sequence[Get | Set | Delete | Arithmetic], *, timeout: float | None = None) BatchResult¶
- cas(key: str | bytes, value: Any, cas_token: int, *, ttl: int | None = None, version: int | None = None, return_cas: bool = False, timeout: float | None = None) MutationResult¶
Store only if the item’s CAS still matches; CAS_MISMATCH otherwise.
- close() None¶
- decrement(key: str | bytes, delta: int = 1, *, initial: int | None = None, initial_ttl: int | None = None, ttl: int | None = None, compare_cas: int | None = None, version: int | None = None, return_cas: bool = False, return_ttl: bool = False, timeout: float | None = None) ArithmeticResult¶
Decrement a counter; saturates at zero instead of underflowing.
- delete(key: str | bytes, *, compare_cas: int | None = None, timeout: float | None = None) MutationResult¶
- execute_meta_command(command: MetaCommand, *, timeout: float | None = None) MetaResult¶
- flush_all(delay: int = 0) None¶
- get(key: str | bytes, *, value: bool = True, meta: Meta = <Meta.NONE: 0>, touch: int | None = None, no_lru_bump: bool = False, unless_cas: int | None = None, lease_ttl: int | None = None, refresh_before: int | None = None, timeout: float | None = None) GetResult[Any]¶
Read a key; covers every mg capability.
With
lease_ttlthe result is aLeaseResult; useget_with_lease()for the statically typed variant.
- get_with_lease(key: str | bytes, *, lease_ttl: int, refresh_before: int | None = None, meta: Meta = <Meta.NONE: 0>, timeout: float | None = None) LeaseResult[Any]¶
- increment(key: str | bytes, delta: int = 1, *, initial: int | None = None, initial_ttl: int | None = None, ttl: int | None = None, compare_cas: int | None = None, version: int | None = None, return_cas: bool = False, return_ttl: bool = False, timeout: float | None = None) ArithmeticResult¶
Increment a counter; overflows wrap around (unsigned 64-bit).
- inspect(key: str | bytes, *, meta: Meta = <Meta.CAS|TTL|SIZE: 7>, no_lru_bump: bool = True, timeout: float | None = None) GetResult[Any]¶
- invalidate(key: str | bytes, *, stale_for: int | None = None, compare_cas: int | None = None, timeout: float | None = None) MutationResult¶
- prepend(key: str | bytes, value: bytes, *, vivify_ttl: int | None = None, timeout: float | None = None) MutationResult¶
Prepend bytes to an existing value; serialization is skipped.
- set(key: str | bytes, value: Any, *, ttl: int | None = None, mode: str = 'set', compare_cas: int | None = None, version: int | None = None, return_cas: bool = False, vivify_ttl: int | None = None, timeout: float | None = None) MutationResult¶
Store a key; covers every ms capability.
modeis one ofset/add/replace/append/prepend; the concatenation modes take bytes only and skip serialization.
- touch(key: str | bytes, ttl: int, *, timeout: float | None = None) MutationResult¶
- class memcache.experiment.MetaCommandResult(rc: bytes, value: bytes | None = None, cas: int | None = None, ttl: int | None = None, client_flags: int | None = None, size: int | None = None, last_access: int | None = None, hit_before: bool | None = None, key: bytes | None = None, opaque: bytes | None = None, won: bool = False, busy: bool = False, stale: bool = False, flags: tuple[bytes, ...] = ())¶
A lightly parsed meta protocol response.
rcis the wire return code (HD,VA,EN,NS,EX,NF);valueis the raw data block, if any. The remaining fields are response flags decoded to native types; a field isNone(orFalsefor markers) when the server did not send the flag.- busy: bool = False¶
- cas: int | None = None¶
- client_flags: int | None = None¶
- flags: tuple[bytes, ...] = ()¶
- hit_before: bool | None = None¶
- key: bytes | None = None¶
- last_access: int | None = None¶
- property ok: bool¶
- opaque: bytes | None = None¶
- rc: bytes¶
- size: int | None = None¶
- stale: bool = False¶
- ttl: int | None = None¶
- value: bytes | None = None¶
- won: bool = False¶
- class memcache.experiment.MutationResult(key: 'Key', status: 'MutationStatus', cas: 'int | None' = None, error: 'BaseException | None' = None)¶
- cas: int | None = None¶
- error: BaseException | None = None¶
- key: str | bytes¶
- status: MutationStatus¶
- class memcache.experiment.MutationStatus(*values)¶
- ALREADY_EXISTS = 3¶
- AMBIGUOUS = 6¶
- CAS_MISMATCH = 4¶
- FAILED = 5¶
- NOT_FOUND = 2¶
- STORED = 1¶
- class memcache.experiment.PickleSerializer¶
Falls back to pickle for arbitrary objects.
pickle.loadsexecutes code embedded in the payload; use this only when every writer to the cache is trusted.- dump_object(key: str | bytes, value: Any) tuple[bytes, int]¶
- load_object(key: str | bytes, value: bytes, flags: int) Any¶
- exception memcache.experiment.ProtocolError¶
The server returned a malformed or unsupported protocol response.
- exception memcache.experiment.ResultValueError¶
Raised when a result does not carry a usable value.
- exception memcache.experiment.SerializeError¶
- class memcache.experiment.Serializer(*args, **kwargs)¶
Paired value codec;
loadmust understand every flagdumpemits.- dump(key: str | bytes, value: Any) tuple[bytes, int]¶
- load(key: str | bytes, value: bytes, flags: int) Any¶
- class memcache.experiment.Set(key: 'Key', value: 'Any', ttl: 'int | None' = None, mode: 'str' = 'set', compare_cas: 'int | None' = None, version: 'int | None' = None, return_cas: 'bool' = False, vivify_ttl: 'int | None' = None)¶
- compare_cas: int | None = None¶
- key: str | bytes¶
- mode: str = 'set'¶
- return_cas: bool = False¶
- ttl: int | None = None¶
- value: Any¶
- version: int | None = None¶
- vivify_ttl: int | None = None¶
License¶
Memcache is distributed by a MIT license.