Source code for numcodecs.pickles

import os
import pickle

import numpy as np

from .abc import Codec
from .compat import ensure_contiguous_ndarray
from .errors import UnsafeDecodeError

_ENV_VAR = "NUMCODECS_ALLOW_UNSAFE_PICKLE_DECODE"


def _read_opt_in_from_env() -> bool:
    """Return whether the ``NUMCODECS_ALLOW_UNSAFE_PICKLE_DECODE`` environment variable
    is set to ``1``, ``true`` or ``yes`` (case-insensitive, surrounding whitespace ignored)."""
    return os.environ.get(_ENV_VAR, "").strip().lower() in {"1", "true", "yes"}


#: Whether :meth:`Pickle.decode` may unpickle data. Unpickling can execute arbitrary
#: code, so this is ``False`` unless the ``NUMCODECS_ALLOW_UNSAFE_PICKLE_DECODE``
#: environment variable is set to ``1``, ``true`` or ``yes`` when ``numcodecs`` is
#: imported. This is deliberately a process-wide setting rather than a codec parameter,
#: because codec parameters are read from array metadata, which may be untrusted.
ALLOW_UNSAFE_PICKLE_DECODE = _read_opt_in_from_env()


[docs] class Pickle(Codec): """Codec to encode data as as pickled bytes. Useful for encoding an array of Python string objects. .. warning:: Decoding pickled data can execute arbitrary code. :meth:`decode` refuses to run unless the process opts in by setting ``numcodecs.pickles.ALLOW_UNSAFE_PICKLE_DECODE = True`` (or the ``NUMCODECS_ALLOW_UNSAFE_PICKLE_DECODE=1`` environment variable). Only opt in if you trust the source of the data. :class:`numcodecs.vlen.VLenUTF8`, :class:`numcodecs.vlen.VLenBytes`, :class:`numcodecs.json.JSON` and :class:`numcodecs.msgpacks.MsgPack` encode arrays of Python objects without this risk. Parameters ---------- protocol : int, defaults to pickle.HIGHEST_PROTOCOL The protocol used to pickle data. Examples -------- >>> import numcodecs as codecs >>> import numpy as np >>> x = np.array(['foo', 'bar', 'baz'], dtype='object') >>> f = codecs.Pickle() >>> codecs.pickles.ALLOW_UNSAFE_PICKLE_DECODE = True # only for trusted data >>> f.decode(f.encode(x)) array(['foo', 'bar', 'baz'], dtype=object) >>> codecs.pickles.ALLOW_UNSAFE_PICKLE_DECODE = False See Also -------- numcodecs.msgpacks.MsgPack """ codec_id = 'pickle' def __init__(self, protocol=pickle.HIGHEST_PROTOCOL): self.protocol = protocol
[docs] def encode(self, buf): return pickle.dumps(buf, protocol=self.protocol)
[docs] def decode(self, buf, out=None): if not ALLOW_UNSAFE_PICKLE_DECODE: raise UnsafeDecodeError( "Pickle.decode is disabled because unpickling data can execute arbitrary " "code. If you trust the source of this data, enable it by setting " "numcodecs.pickles.ALLOW_UNSAFE_PICKLE_DECODE = True, or by setting the " f"environment variable {_ENV_VAR}=1 before importing numcodecs." ) buf = ensure_contiguous_ndarray(buf) dec = pickle.loads(buf) if out is not None: np.copyto(out, dec) return out else: return dec
[docs] def get_config(self): return {'id': self.codec_id, 'protocol': self.protocol}
def __repr__(self): return f'Pickle(protocol={self.protocol})'