collective.redismemoize.serializer#

The check that decides what is shareable, and the restricted pickle format.

Strict serialization of plain data.

A value goes to Redis only when it is plain data, and that is decided by construction: dumps() walks the value and accepts nothing but the types listed here, matched exactly. "pickle.dumps did not raise" is not the test. A persistent object without an acquisition wrapper pickles happily and comes back as a detached copy, and a utility comes back as a different object. Neither is an error pickle can report, and both break the caller.

loads() is the security boundary. Its unpickler resolves only the globals the accepted types need, so a payload someone else wrote into Redis cannot make this process call anything.

This module is pure Python and never imports Zope; DateTime is a plain library.

collective.redismemoize.serializer.FORMAT_VERSION = b'\x01'#

First byte of every payload. A future format gets a new byte, and a payload with a byte this code does not know is treated as a miss.

collective.redismemoize.serializer.PROTOCOL = 5#

Pickle protocol used for every payload.

collective.redismemoize.serializer.LEAVES: frozenset[type] = frozenset({<class 'DateTime.DateTime.DateTime'>, <class 'NoneType'>, <class 'bool'>, <class 'bytearray'>, <class 'bytes'>, <class 'complex'>, <class 'datetime.date'>, <class 'datetime.datetime'>, <class 'datetime.time'>, <class 'datetime.timedelta'>, <class 'datetime.timezone'>, <class 'decimal.Decimal'>, <class 'float'>, <class 'int'>, <class 'str'>, <class 'uuid.UUID'>})#

Types accepted as leaves, matched with type(value) in LEAVES.

collective.redismemoize.serializer.SEQUENCES: frozenset[type] = frozenset({<class 'frozenset'>, <class 'list'>, <class 'set'>, <class 'tuple'>})#

Containers accepted, walked recursively, matched exactly.

collective.redismemoize.serializer.SAFE_GLOBALS: frozenset[tuple[str, str]] = frozenset({('DateTime.DateTime', 'DateTime'), ('builtins', 'complex'), ('datetime', 'date'), ('datetime', 'datetime'), ('datetime', 'time'), ('datetime', 'timedelta'), ('datetime', 'timezone'), ('decimal', 'Decimal'), ('uuid', 'UUID')})#

The globals loads() may resolve, as (module, name). Exactly what pickling the types in LEAVES produces, and nothing else.

exception collective.redismemoize.serializer.NotShareable[source]#

Bases: TypeError

A value that is not plain data, and so stays in the RAM cache.

Parameters:

reason -- What was refused and where, for the log.

__init__(reason)[source]#
Parameters:

reason (str)

exception collective.redismemoize.serializer.UndecodableValue[source]#

Bases: ValueError

A payload loads() refuses to decode; callers treat it as a miss.

collective.redismemoize.serializer.check(value, path='value')[source]#

Refuse anything that is not plain data.

Parameters:
  • value (object) -- The value to check.

  • path (str) -- Where value sits in the structure, for the message.

Raises:

NotShareable -- Naming the offending type and its path.

Return type:

None

collective.redismemoize.serializer.dumps(value)[source]#

Serialize plain data.

Parameters:

value (object) -- The value to store.

Returns:

The payload, starting with FORMAT_VERSION.

Raises:

NotShareable -- When the value is not plain data.

Return type:

bytes

collective.redismemoize.serializer.loads(payload)[source]#

Decode a payload written by dumps().

Parameters:

payload (bytes) -- The bytes read from Redis.

Returns:

A new copy of the stored value.

Raises:

UndecodableValue -- For an unknown format, a truncated or corrupt payload, or one that asks for a global not in SAFE_GLOBALS.

Return type:

object