Import python venv for stability
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
r"""
|
||||
Provides frozendict, a simple immutable dictionary.
|
||||
"""
|
||||
|
||||
try: # pragma: no cover
|
||||
from ._frozendict import *
|
||||
c_ext = True
|
||||
# noinspection PyUnresolvedReferences
|
||||
del _frozendict
|
||||
except ImportError:
|
||||
from ._frozendict_py import *
|
||||
c_ext = False
|
||||
|
||||
from . import cool
|
||||
from . import monkeypatch
|
||||
from .cool import *
|
||||
from .version import version as __version__
|
||||
|
||||
|
||||
def _getFrozendictJsonEncoder(BaseJsonEncoder = None):
|
||||
if BaseJsonEncoder is None: # pragma: no cover
|
||||
from json.encoder import JSONEncoder
|
||||
|
||||
BaseJsonEncoder = JSONEncoder
|
||||
|
||||
class FrozendictJsonEncoderInternal(BaseJsonEncoder):
|
||||
def default(self, obj):
|
||||
if isinstance(obj, frozendict): # pragma: no cover
|
||||
# TODO create a C serializer
|
||||
return dict(obj)
|
||||
|
||||
return BaseJsonEncoder.default(
|
||||
self,
|
||||
obj
|
||||
) # pragma: no cover
|
||||
|
||||
return FrozendictJsonEncoderInternal
|
||||
|
||||
|
||||
FrozendictJsonEncoder = _getFrozendictJsonEncoder()
|
||||
monkeypatch.patchOrUnpatchAll(patch = True, warn = False)
|
||||
|
||||
|
||||
from collections.abc import Mapping
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
Mapping.register(frozendict)
|
||||
del Mapping
|
||||
|
||||
|
||||
if c_ext: # pragma: no cover
|
||||
__all__ = (frozendict.__name__, )
|
||||
else:
|
||||
__all__ = _frozendict_py.__all__
|
||||
del _frozendict_py
|
||||
|
||||
# TODO deprecated, to remove in future versions
|
||||
FrozenOrderedDict = frozendict
|
||||
|
||||
__all__ += cool.__all__
|
||||
__all__ += (FrozendictJsonEncoder.__name__, "FrozenOrderedDict")
|
||||
@@ -0,0 +1,123 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import (
|
||||
TypeVar,
|
||||
overload,
|
||||
Optional,
|
||||
Union,
|
||||
Any,
|
||||
Dict,
|
||||
Callable,
|
||||
)
|
||||
|
||||
from collections.abc import Hashable
|
||||
|
||||
try:
|
||||
from typing import Mapping, Iterable, Iterator, Tuple, Type
|
||||
except ImportError:
|
||||
from collections.abc import Mapping, Iterable, Iterator
|
||||
Tuple = tuple
|
||||
Type = type
|
||||
|
||||
K = TypeVar("K")
|
||||
V = TypeVar("V", covariant=True)
|
||||
K2 = TypeVar("K2")
|
||||
V2 = TypeVar("V2", covariant=True)
|
||||
SelfT = TypeVar("SelfT", bound=frozendict[K, V])
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
class frozendict(Mapping[K, V]):
|
||||
@overload
|
||||
def __new__(cls: Type[SelfT]) -> SelfT: ...
|
||||
@overload
|
||||
def __new__(cls: Type[SelfT], **kwargs: V) -> frozendict[str, V]: ...
|
||||
@overload
|
||||
def __new__(cls: Type[SelfT], mapping: Mapping[K, V]) -> SelfT: ...
|
||||
@overload
|
||||
def __new__(cls: Type[SelfT], iterable: Iterable[Tuple[K, V]]) -> SelfT: ...
|
||||
|
||||
def __getitem__(self: SelfT, key: K) -> V: ...
|
||||
def __len__(self: SelfT) -> int: ...
|
||||
def __iter__(self: SelfT) -> Iterator[K]: ...
|
||||
def __hash__(self: SelfT) -> int: ...
|
||||
def __reversed__(self: SelfT) -> Iterator[K]: ...
|
||||
def copy(self: SelfT) -> SelfT: ...
|
||||
def __copy__(self: SelfT) -> SelfT: ...
|
||||
def __deepcopy__(self: SelfT) -> SelfT: ...
|
||||
def delete(self: SelfT, key: K) -> SelfT: ...
|
||||
@overload
|
||||
def key(self: SelfT, index: int) -> K: ...
|
||||
@overload
|
||||
def key(self: SelfT) -> K: ...
|
||||
@overload
|
||||
def value(self: SelfT, index: int) -> V: ...
|
||||
@overload
|
||||
def value(self: SelfT) -> V: ...
|
||||
@overload
|
||||
def item(self: SelfT, index: int) -> Tuple[K, V]: ...
|
||||
@overload
|
||||
def item(self: SelfT) -> Tuple[K, V]: ...
|
||||
@overload
|
||||
def __or__(self: SelfT, other: Mapping[K, V]) -> SelfT: ...
|
||||
@overload
|
||||
def __or__(self: SelfT, other: Mapping[K2, V]) -> frozendict[Union[K, K2], V]: ...
|
||||
@overload
|
||||
def __or__(self: SelfT, other: Mapping[K, V2]) -> frozendict[K, Union[V, V2]]: ...
|
||||
@overload
|
||||
def __or__(self: SelfT, other: Mapping[K2, V2]) -> frozendict[Union[K, K2], Union[V, V2]]: ...
|
||||
@overload
|
||||
def set(self: SelfT, key: K, value: V) -> SelfT: ...
|
||||
@overload
|
||||
def set(self: SelfT, key: K2, value: V) -> frozendict[Union[K, K2], V]: ...
|
||||
@overload
|
||||
def set(self: SelfT, key: K, value: V2) -> frozendict[K, Union[V, V2]]: ...
|
||||
@overload
|
||||
def set(self: SelfT, key: K2, value: V2) -> frozendict[Union[K, K2], Union[V, V2]]: ...
|
||||
@overload
|
||||
def setdefault(self: SelfT, key: K) -> SelfT: ...
|
||||
@overload
|
||||
def setdefault(self: SelfT, key: K2) -> SelfT: ...
|
||||
@overload
|
||||
def setdefault(self: SelfT, key: K, default: V) -> SelfT: ...
|
||||
@overload
|
||||
def setdefault(self: SelfT, key: K2, default: V) -> frozendict[Union[K, K2], V]: ...
|
||||
@overload
|
||||
def setdefault(self: SelfT, key: K, default: V2) -> frozendict[K, Union[V, V2]]: ...
|
||||
@overload
|
||||
def setdefault(self: SelfT, key: K2, default: V2) -> frozendict[Union[K, K2], Union[V, V2]]: ...
|
||||
|
||||
@classmethod
|
||||
def fromkeys(
|
||||
cls: Type[SelfT],
|
||||
seq: Iterable[K],
|
||||
value: Optional[V] = None
|
||||
) -> SelfT: ...
|
||||
|
||||
|
||||
FrozenOrderedDict = frozendict
|
||||
c_ext: bool
|
||||
|
||||
class FreezeError(Exception): pass
|
||||
|
||||
|
||||
class FreezeWarning(UserWarning): pass
|
||||
|
||||
# PyCharm complains about returning Hashable, because
|
||||
# it's not subscriptable
|
||||
def deepfreeze(
|
||||
o: Any,
|
||||
custom_converters: Optional[Dict[Any, Callable[[Any], Hashable]]] = None,
|
||||
custom_inverse_converters: Optional[Dict[Any, Callable[[Any], Any]]] = None
|
||||
) -> Any: ...
|
||||
|
||||
def register(
|
||||
to_convert: Any,
|
||||
converter: Callable[[Any], Any],
|
||||
*,
|
||||
inverse: bool = False
|
||||
) -> None: ...
|
||||
|
||||
def unregister(
|
||||
type: Any,
|
||||
inverse: bool = False
|
||||
) -> None: ...
|
||||
@@ -0,0 +1,254 @@
|
||||
from copy import deepcopy
|
||||
|
||||
|
||||
def immutable(self, *_args, **_kwargs):
|
||||
r"""
|
||||
Function for not implemented method since the object is immutable
|
||||
"""
|
||||
|
||||
raise AttributeError(
|
||||
f"'{self.__class__.__name__}' object is read-only"
|
||||
)
|
||||
|
||||
|
||||
_empty_frozendict = None
|
||||
_module_name = "frozendict"
|
||||
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
class frozendict(dict):
|
||||
r"""
|
||||
A simple immutable dictionary.
|
||||
|
||||
The API is the same as `dict`, without methods that can change the
|
||||
immutability. In addition, it supports __hash__().
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
"_hash",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def fromkeys(cls, *args, **kwargs):
|
||||
r"""
|
||||
Identical to dict.fromkeys().
|
||||
"""
|
||||
|
||||
return cls(dict.fromkeys(*args, **kwargs))
|
||||
|
||||
# noinspection PyMethodParameters
|
||||
def __new__(e4b37cdf_d78a_4632_bade_6f0579d8efac, *args, **kwargs):
|
||||
cls = e4b37cdf_d78a_4632_bade_6f0579d8efac
|
||||
|
||||
has_kwargs = bool(kwargs)
|
||||
continue_creation = True
|
||||
self = None
|
||||
|
||||
# check if there's only an argument and it's of the same class
|
||||
if len(args) == 1 and not has_kwargs:
|
||||
it = args[0]
|
||||
|
||||
# no isinstance, to avoid subclassing problems
|
||||
if it.__class__ == frozendict and cls == frozendict:
|
||||
self = it
|
||||
continue_creation = False
|
||||
|
||||
if continue_creation:
|
||||
self = dict.__new__(cls, *args, **kwargs)
|
||||
|
||||
dict.__init__(self, *args, **kwargs)
|
||||
|
||||
# empty singleton - start
|
||||
|
||||
if self.__class__ == frozendict and not len(self):
|
||||
global _empty_frozendict
|
||||
|
||||
if _empty_frozendict is None:
|
||||
_empty_frozendict = self
|
||||
else:
|
||||
self = _empty_frozendict
|
||||
continue_creation = False
|
||||
|
||||
# empty singleton - end
|
||||
|
||||
if continue_creation:
|
||||
object.__setattr__(self, "_hash", -1)
|
||||
|
||||
return self
|
||||
|
||||
# noinspection PyMissingConstructor
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def __hash__(self, *args, **kwargs):
|
||||
r"""
|
||||
Calculates the hash if all values are hashable, otherwise
|
||||
raises a TypeError.
|
||||
"""
|
||||
|
||||
if self._hash != -1:
|
||||
_hash = self._hash
|
||||
else:
|
||||
fs = frozenset(self.items())
|
||||
_hash = hash(fs)
|
||||
|
||||
object.__setattr__(self, "_hash", _hash)
|
||||
|
||||
return _hash
|
||||
|
||||
def __repr__(self, *args, **kwargs):
|
||||
r"""
|
||||
Identical to dict.__repr__().
|
||||
"""
|
||||
|
||||
body = super().__repr__(*args, **kwargs)
|
||||
klass = self.__class__
|
||||
|
||||
if klass == frozendict:
|
||||
name = f"{_module_name}.{klass.__name__}"
|
||||
else:
|
||||
name = klass.__name__
|
||||
|
||||
return f"{name}({body})"
|
||||
|
||||
def copy(self):
|
||||
r"""
|
||||
Return the object itself, as it's an immutable.
|
||||
"""
|
||||
|
||||
klass = self.__class__
|
||||
|
||||
if klass == frozendict:
|
||||
return self
|
||||
|
||||
return klass(self)
|
||||
|
||||
def __copy__(self, *args, **kwargs):
|
||||
r"""
|
||||
See copy().
|
||||
"""
|
||||
|
||||
return self.copy()
|
||||
|
||||
def __deepcopy__(self, memo, *args, **kwargs):
|
||||
r"""
|
||||
As for tuples, if hashable, see copy(); otherwise, it returns a
|
||||
deepcopy.
|
||||
"""
|
||||
|
||||
klass = self.__class__
|
||||
return_copy = klass == frozendict
|
||||
|
||||
if return_copy:
|
||||
try:
|
||||
hash(self)
|
||||
except TypeError:
|
||||
return_copy = False
|
||||
|
||||
if return_copy:
|
||||
return self.copy()
|
||||
|
||||
tmp = deepcopy(dict(self))
|
||||
|
||||
return klass(tmp)
|
||||
|
||||
def __reduce__(self, *args, **kwargs):
|
||||
r"""
|
||||
Support for `pickle`.
|
||||
"""
|
||||
|
||||
return (self.__class__, (dict(self),))
|
||||
|
||||
def set(self, key, val):
|
||||
new_self = dict(self)
|
||||
new_self[key] = val
|
||||
|
||||
return self.__class__(new_self)
|
||||
|
||||
def setdefault(self, key, default=None):
|
||||
if key in self:
|
||||
return self
|
||||
|
||||
new_self = dict(self)
|
||||
|
||||
new_self[key] = default
|
||||
|
||||
return self.__class__(new_self)
|
||||
|
||||
def delete(self, key):
|
||||
new_self = dict(self)
|
||||
del new_self[key]
|
||||
|
||||
if new_self:
|
||||
return self.__class__(new_self)
|
||||
|
||||
return self.__class__()
|
||||
|
||||
def _get_by_index(self, collection, index):
|
||||
try:
|
||||
return collection[index]
|
||||
except IndexError:
|
||||
maxindex = len(collection) - 1
|
||||
name = self.__class__.__name__
|
||||
raise IndexError(
|
||||
f"{name} index {index} out of range {maxindex}"
|
||||
) from None
|
||||
|
||||
def key(self, index=0):
|
||||
collection = tuple(self.keys())
|
||||
|
||||
return self._get_by_index(collection, index)
|
||||
|
||||
def value(self, index=0):
|
||||
collection = tuple(self.values())
|
||||
|
||||
return self._get_by_index(collection, index)
|
||||
|
||||
def item(self, index=0):
|
||||
collection = tuple(self.items())
|
||||
|
||||
return self._get_by_index(collection, index)
|
||||
|
||||
def __setitem__(self, key, val, *args, **kwargs):
|
||||
raise TypeError(
|
||||
f"'{self.__class__.__name__}' object doesn't support item "
|
||||
"assignment"
|
||||
)
|
||||
|
||||
def __delitem__(self, key, *args, **kwargs):
|
||||
raise TypeError(
|
||||
f"'{self.__class__.__name__}' object doesn't support item "
|
||||
"deletion"
|
||||
)
|
||||
|
||||
|
||||
def frozendict_or(self, other, *_args, **_kwargs):
|
||||
res = {}
|
||||
res.update(self)
|
||||
res.update(other)
|
||||
|
||||
return self.__class__(res)
|
||||
|
||||
|
||||
frozendict.__or__ = frozendict_or
|
||||
frozendict.__ior__ = frozendict_or
|
||||
|
||||
try:
|
||||
# noinspection PyStatementEffect
|
||||
frozendict.__reversed__
|
||||
except AttributeError: # pragma: no cover
|
||||
def frozendict_reversed(self, *_args, **_kwargs):
|
||||
return reversed(tuple(self))
|
||||
|
||||
|
||||
frozendict.__reversed__ = frozendict_reversed
|
||||
|
||||
frozendict.clear = immutable
|
||||
frozendict.pop = immutable
|
||||
frozendict.popitem = immutable
|
||||
frozendict.update = immutable
|
||||
frozendict.__delattr__ = immutable
|
||||
frozendict.__setattr__ = immutable
|
||||
frozendict.__module__ = _module_name
|
||||
|
||||
__all__ = (frozendict.__name__,)
|
||||
@@ -0,0 +1,328 @@
|
||||
from collections.abc import MutableMapping, MutableSequence, MutableSet
|
||||
from enum import Enum
|
||||
from types import MappingProxyType
|
||||
|
||||
from array import array
|
||||
from frozendict import frozendict
|
||||
|
||||
# fix for python 3.9-
|
||||
|
||||
# coverage does not work here!
|
||||
if not issubclass(array, MutableSequence): # pragma: no cover
|
||||
# noinspection PyUnresolvedReferences
|
||||
MutableSequence.register(array)
|
||||
|
||||
|
||||
def isIterableNotString(o):
|
||||
from collections import abc
|
||||
|
||||
return (
|
||||
isinstance(o, abc.Iterable) and
|
||||
not isinstance(o, memoryview) and
|
||||
not hasattr(o, "isalpha")
|
||||
)
|
||||
|
||||
|
||||
def getItems(o):
|
||||
from collections import abc
|
||||
|
||||
if not isinstance(o, abc.Iterable):
|
||||
raise TypeError("object must be an iterable")
|
||||
|
||||
if isinstance(o, abc.Mapping):
|
||||
return dict.items
|
||||
|
||||
return enumerate
|
||||
|
||||
|
||||
def nil(x):
|
||||
return x
|
||||
|
||||
|
||||
_freeze_conversion_map = frozendict({
|
||||
MutableMapping: frozendict,
|
||||
bytearray: bytes,
|
||||
MutableSequence: tuple,
|
||||
MutableSet: frozenset,
|
||||
Enum: nil,
|
||||
})
|
||||
|
||||
_freeze_conversion_map_custom = {}
|
||||
|
||||
|
||||
class FreezeError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class FreezeWarning(UserWarning):
|
||||
pass
|
||||
|
||||
|
||||
def register(to_convert, converter, *, inverse = False):
|
||||
r"""
|
||||
Adds a `converter` for a type `to_convert`. `converter`
|
||||
must be callable. The new converter will be used by `deepfreeze()`
|
||||
and has precedence over any previous converter.
|
||||
|
||||
If `to_covert` has already a converter, a FreezeWarning is raised.
|
||||
|
||||
If `inverse` is True, the conversion is considered from an immutable
|
||||
type to a mutable one. This make it possible to convert mutable
|
||||
objects nested in the registered immutable one.
|
||||
"""
|
||||
|
||||
if not issubclass(type(to_convert), type):
|
||||
raise ValueError(
|
||||
f"`to_convert` parameter must be a type, {to_convert} found"
|
||||
)
|
||||
|
||||
try:
|
||||
converter.__call__
|
||||
except AttributeError:
|
||||
raise ValueError(
|
||||
f"`converter` parameter must be a callable, {converter}" +
|
||||
"found"
|
||||
)
|
||||
|
||||
if inverse:
|
||||
freeze_conversion_map = getFreezeConversionInverseMap()
|
||||
else:
|
||||
freeze_conversion_map = getFreezeConversionMap()
|
||||
|
||||
if to_convert in freeze_conversion_map:
|
||||
import warnings
|
||||
|
||||
warnings.warn(
|
||||
f"{to_convert.__name__} is already in the conversion map",
|
||||
FreezeWarning
|
||||
)
|
||||
|
||||
if inverse:
|
||||
freeze_conversion_map = _freeze_conversion_inverse_map_custom
|
||||
else:
|
||||
freeze_conversion_map = _freeze_conversion_map_custom
|
||||
|
||||
freeze_conversion_map[to_convert] = converter
|
||||
|
||||
|
||||
def unregister(type, inverse = False):
|
||||
r"""
|
||||
Unregister a type from custom conversion. If `inverse` is `True`,
|
||||
the unregistered conversion is an inverse conversion
|
||||
(see `register()`).
|
||||
"""
|
||||
|
||||
if inverse:
|
||||
freeze_conversion_map = _freeze_conversion_inverse_map_custom
|
||||
else:
|
||||
freeze_conversion_map = _freeze_conversion_map_custom
|
||||
|
||||
try:
|
||||
del freeze_conversion_map[type]
|
||||
except KeyError:
|
||||
raise FreezeError(f"{type.__name__} is not registered")
|
||||
|
||||
|
||||
def getFreezeConversionMap():
|
||||
return _freeze_conversion_map | _freeze_conversion_map_custom
|
||||
|
||||
|
||||
_freeze_conversion_inverse_map = frozendict({
|
||||
frozendict: dict,
|
||||
MappingProxyType: dict,
|
||||
tuple: list,
|
||||
})
|
||||
|
||||
_freeze_conversion_inverse_map_custom = {}
|
||||
|
||||
|
||||
def getFreezeConversionInverseMap():
|
||||
return (
|
||||
_freeze_conversion_inverse_map |
|
||||
_freeze_conversion_inverse_map_custom
|
||||
)
|
||||
|
||||
|
||||
_freeze_types = (
|
||||
[x for x in _freeze_conversion_map] +
|
||||
[x for x in _freeze_conversion_inverse_map]
|
||||
)
|
||||
|
||||
|
||||
def getFreezeTypes():
|
||||
return (tuple(
|
||||
_freeze_types +
|
||||
[x for x in _freeze_conversion_map_custom] +
|
||||
[x for x in _freeze_conversion_inverse_map_custom]
|
||||
))
|
||||
|
||||
|
||||
_freeze_types_plain = (MutableSet, bytearray, array)
|
||||
|
||||
|
||||
def deepfreeze(
|
||||
o,
|
||||
custom_converters = None,
|
||||
custom_inverse_converters = None
|
||||
):
|
||||
r"""
|
||||
Converts the object and all the objects nested in it in its
|
||||
immutable counterparts.
|
||||
|
||||
The conversion map is in getFreezeConversionMap().
|
||||
|
||||
You can register a new conversion using `register()` You can also
|
||||
pass a map of custom converters with `custom_converters` and a map
|
||||
of custom inverse converters with `custom_inverse_converters`,
|
||||
without using `register()`.
|
||||
|
||||
By default, if the type is not registered and has a `__dict__`
|
||||
attribute, it's converted to the `frozendict` of that `__dict__`.
|
||||
|
||||
This function assumes that hashable == immutable (that is not
|
||||
always true).
|
||||
|
||||
This function uses recursion, with all the limits of recursions in
|
||||
Python.
|
||||
|
||||
Where is a good old tail call when you need it?
|
||||
"""
|
||||
|
||||
from frozendict import frozendict
|
||||
|
||||
if custom_converters is None:
|
||||
custom_converters = frozendict()
|
||||
|
||||
if custom_inverse_converters is None:
|
||||
custom_inverse_converters = frozendict()
|
||||
|
||||
for type_i, converter in custom_converters.items():
|
||||
if not issubclass(type(type_i), type):
|
||||
raise ValueError(
|
||||
f"{type_i} in `custom_converters` parameter is not a " +
|
||||
"type"
|
||||
)
|
||||
|
||||
try:
|
||||
converter.__call__
|
||||
except AttributeError:
|
||||
raise ValueError(
|
||||
f"converter for {type_i} in `custom_converters` " +
|
||||
"parameter is not a callable"
|
||||
)
|
||||
|
||||
for type_i, converter in custom_inverse_converters.items():
|
||||
if not issubclass(type(type_i), type):
|
||||
raise ValueError(
|
||||
f"{type_i} in `custom_inverse_converters` parameter " +
|
||||
"is not a type"
|
||||
)
|
||||
|
||||
try:
|
||||
converter.__call__
|
||||
except AttributeError:
|
||||
raise ValueError(
|
||||
f"converter for {type_i} in " +
|
||||
"`custom_inverse_converters`parameter is not a callable"
|
||||
)
|
||||
|
||||
type_o = type(o)
|
||||
|
||||
freeze_types = tuple(custom_converters.keys()) + getFreezeTypes()
|
||||
|
||||
base_type_o = None
|
||||
|
||||
for freeze_type in freeze_types:
|
||||
if isinstance(o, freeze_type):
|
||||
base_type_o = freeze_type
|
||||
break
|
||||
|
||||
if base_type_o is None:
|
||||
# this is before hash check because all object in Python are
|
||||
# hashable by default, if not explicitly suppressed
|
||||
try:
|
||||
o.__dict__
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
return frozendict(o.__dict__)
|
||||
|
||||
try:
|
||||
hash(o)
|
||||
except TypeError:
|
||||
pass
|
||||
else:
|
||||
# without a converter, we can only hope that
|
||||
# hashable == immutable
|
||||
return o
|
||||
|
||||
supported_types = ", ".join((x.__name__ for x in freeze_types))
|
||||
|
||||
err = (
|
||||
f"type {type_o} is not hashable or is not equal or a " +
|
||||
f"subclass of the supported types: {supported_types}"
|
||||
)
|
||||
|
||||
raise TypeError(err)
|
||||
|
||||
freeze_conversion_map = getFreezeConversionMap()
|
||||
|
||||
freeze_conversion_map = freeze_conversion_map | custom_converters
|
||||
|
||||
if base_type_o in _freeze_types_plain:
|
||||
return freeze_conversion_map[base_type_o](o)
|
||||
|
||||
if not isIterableNotString(o):
|
||||
return freeze_conversion_map[base_type_o](o)
|
||||
|
||||
freeze_conversion_inverse_map = getFreezeConversionInverseMap()
|
||||
|
||||
freeze_conversion_inverse_map = (
|
||||
freeze_conversion_inverse_map |
|
||||
custom_inverse_converters
|
||||
)
|
||||
|
||||
frozen_type = base_type_o in freeze_conversion_inverse_map
|
||||
|
||||
if frozen_type:
|
||||
o = freeze_conversion_inverse_map[base_type_o](o)
|
||||
|
||||
from copy import copy
|
||||
|
||||
o_copy = copy(o)
|
||||
|
||||
for k, v in getItems(o_copy)(o_copy):
|
||||
o_copy[k] = deepfreeze(
|
||||
v,
|
||||
custom_converters = custom_converters,
|
||||
custom_inverse_converters = custom_inverse_converters
|
||||
)
|
||||
|
||||
try:
|
||||
freeze = freeze_conversion_map[base_type_o]
|
||||
except KeyError:
|
||||
if frozen_type:
|
||||
freeze = type_o
|
||||
else: # pragma: no cover
|
||||
raise
|
||||
|
||||
return freeze(o_copy)
|
||||
|
||||
|
||||
__all__ = (
|
||||
deepfreeze.__name__,
|
||||
register.__name__,
|
||||
unregister.__name__,
|
||||
getFreezeConversionMap.__name__,
|
||||
getFreezeConversionInverseMap.__name__,
|
||||
FreezeError.__name__,
|
||||
FreezeWarning.__name__,
|
||||
)
|
||||
|
||||
del MappingProxyType
|
||||
del array
|
||||
del frozendict
|
||||
del MutableMapping
|
||||
del MutableSequence
|
||||
del MutableSet
|
||||
del Enum
|
||||
@@ -0,0 +1,6 @@
|
||||
# this provides compatibility for older pickles
|
||||
# created on a python-only implementation that
|
||||
# specifically mention frozendict.core.frozendict
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
from frozendict import frozendict
|
||||
@@ -0,0 +1,208 @@
|
||||
_OldJsonEncoder = None
|
||||
_oldOrjsonDumps = None
|
||||
_oldMutableMappingSubclasshook = None
|
||||
|
||||
|
||||
class MonkeypatchWarning(UserWarning):
|
||||
pass
|
||||
|
||||
|
||||
def checkCExtension(*, warn, warn_c = False):
|
||||
import frozendict as cool
|
||||
|
||||
res = cool.c_ext
|
||||
|
||||
if warn and res == warn_c:
|
||||
if warn_c: # pragma: no cover
|
||||
msg = "C Extension version, monkeypatch will be not applied"
|
||||
else:
|
||||
msg = "Pure Python version, monkeypatch will be not applied"
|
||||
|
||||
import warnings
|
||||
|
||||
warnings.warn(msg, MonkeypatchWarning)
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def patchOrUnpatchJson(*, patch, warn = True): # pragma: no cover
|
||||
if not checkCExtension(warn = warn):
|
||||
return
|
||||
|
||||
from importlib import import_module
|
||||
self = import_module(__name__)
|
||||
import frozendict as cool
|
||||
import json
|
||||
|
||||
OldJsonEncoder = self._OldJsonEncoder
|
||||
|
||||
# noinspection PyUnresolvedReferences, PyProtectedMember
|
||||
FrozendictJsonEncoder = cool._getFrozendictJsonEncoder(
|
||||
OldJsonEncoder
|
||||
)
|
||||
|
||||
if patch:
|
||||
DefaultJsonEncoder = FrozendictJsonEncoder
|
||||
else:
|
||||
DefaultJsonEncoder = OldJsonEncoder
|
||||
|
||||
if DefaultJsonEncoder is None:
|
||||
default_json_encoder = None
|
||||
else:
|
||||
default_json_encoder = DefaultJsonEncoder(
|
||||
skipkeys = False,
|
||||
ensure_ascii = True,
|
||||
check_circular = True,
|
||||
allow_nan = True,
|
||||
indent = None,
|
||||
separators = None,
|
||||
default = None,
|
||||
)
|
||||
|
||||
if patch:
|
||||
if OldJsonEncoder is None:
|
||||
self._OldJsonEncoder = json.encoder.JSONEncoder
|
||||
else:
|
||||
if OldJsonEncoder is None:
|
||||
raise ValueError(
|
||||
"Old json encoder is None " +
|
||||
"(maybe you already unpatched json?)"
|
||||
)
|
||||
|
||||
self._OldJsonEncoder = None
|
||||
|
||||
cool.FrozendictJsonEncoder = FrozendictJsonEncoder
|
||||
|
||||
json.JSONEncoder = DefaultJsonEncoder
|
||||
json.encoder.JSONEncoder = DefaultJsonEncoder
|
||||
json._default_encoder = default_json_encoder
|
||||
|
||||
|
||||
def patchOrUnpatchOrjson(*, patch, warn = True): # pragma: no cover
|
||||
if not checkCExtension(warn = warn):
|
||||
return
|
||||
|
||||
from importlib import import_module
|
||||
self = import_module(__name__)
|
||||
# noinspection PyUnresolvedReferences
|
||||
import orjson
|
||||
|
||||
if self._oldOrjsonDumps is None:
|
||||
if not patch:
|
||||
raise ValueError(
|
||||
"Old orjson encoder is None " +
|
||||
"(maybe you already unpatched orjson?)"
|
||||
)
|
||||
|
||||
oldOrjsonDumps = orjson.dumps
|
||||
else:
|
||||
oldOrjsonDumps = self._oldOrjsonDumps
|
||||
|
||||
if patch:
|
||||
from frozendict import frozendict
|
||||
|
||||
def frozendictOrjsonDumps(obj, *args, **kwargs):
|
||||
if isinstance(obj, frozendict):
|
||||
obj = dict(obj)
|
||||
|
||||
return oldOrjsonDumps(obj, *args, **kwargs)
|
||||
|
||||
defaultOrjsonDumps = frozendictOrjsonDumps
|
||||
newOldOrjsonDumps = oldOrjsonDumps
|
||||
else:
|
||||
defaultOrjsonDumps = oldOrjsonDumps
|
||||
newOldOrjsonDumps = None
|
||||
|
||||
self._oldOrjsonDumps = newOldOrjsonDumps
|
||||
orjson.dumps = defaultOrjsonDumps
|
||||
orjson.orjson.dumps = defaultOrjsonDumps
|
||||
|
||||
|
||||
def patchOrUnpatchMutableMappingSubclasshook(
|
||||
*,
|
||||
patch,
|
||||
warn = True
|
||||
): # pragma: no cover
|
||||
warn_c = True
|
||||
|
||||
if checkCExtension(warn = warn, warn_c = warn_c):
|
||||
return
|
||||
|
||||
from importlib import import_module
|
||||
self = import_module(__name__)
|
||||
from collections.abc import MutableMapping
|
||||
from frozendict import frozendict
|
||||
|
||||
if self._oldMutableMappingSubclasshook is None:
|
||||
if not patch:
|
||||
raise ValueError(
|
||||
"Old MutableMapping subclasshook is None " +
|
||||
"(maybe you already unpatched MutableMapping?)"
|
||||
)
|
||||
|
||||
oldMutableMappingHook = MutableMapping.__subclasshook__
|
||||
else:
|
||||
oldMutableMappingHook = self._oldMutableMappingSubclasshook
|
||||
|
||||
if patch:
|
||||
# noinspection PyDecorator
|
||||
@classmethod
|
||||
def frozendictMutableMappingSubclasshook(
|
||||
klass,
|
||||
subclass,
|
||||
*args,
|
||||
**kwargs
|
||||
):
|
||||
if klass == MutableMapping:
|
||||
if issubclass(subclass, frozendict):
|
||||
return False
|
||||
|
||||
# noinspection PyArgumentList
|
||||
return oldMutableMappingHook(
|
||||
subclass,
|
||||
*args,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
return NotImplemented
|
||||
|
||||
defaultMutableMappingHook = frozendictMutableMappingSubclasshook
|
||||
newOldMutableMappingHook = oldMutableMappingHook
|
||||
else:
|
||||
defaultMutableMappingHook = oldMutableMappingHook
|
||||
newOldMutableMappingHook = None
|
||||
|
||||
self._oldMutableMappingSubclasshook = newOldMutableMappingHook
|
||||
MutableMapping.__subclasshook__ = defaultMutableMappingHook
|
||||
|
||||
try:
|
||||
# noinspection PyUnresolvedReferences, PyProtectedMember
|
||||
MutableMapping._abc_caches_clear()
|
||||
except AttributeError:
|
||||
# noinspection PyUnresolvedReferences, PyProtectedMember
|
||||
MutableMapping._abc_cache.discard(frozendict)
|
||||
# noinspection PyUnresolvedReferences, PyProtectedMember
|
||||
MutableMapping._abc_negative_cache.discard(frozendict)
|
||||
|
||||
|
||||
def patchOrUnpatchAll(*, patch, warn = True, raise_orjson = False):
|
||||
patchOrUnpatchJson(patch = patch, warn = warn)
|
||||
|
||||
try:
|
||||
import orjson
|
||||
except ImportError: # pragma: no cover
|
||||
if raise_orjson:
|
||||
raise
|
||||
else: # pragma: no cover
|
||||
patchOrUnpatchOrjson(patch = patch, warn = warn)
|
||||
|
||||
patchOrUnpatchMutableMappingSubclasshook(patch = patch, warn = warn)
|
||||
|
||||
|
||||
__all__ = (
|
||||
patchOrUnpatchJson.__name__,
|
||||
patchOrUnpatchOrjson.__name__,
|
||||
patchOrUnpatchMutableMappingSubclasshook.__name__,
|
||||
patchOrUnpatchAll.__name__,
|
||||
MonkeypatchWarning.__name__,
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
version = "2.4.7"
|
||||
Reference in New Issue
Block a user