Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python 2.7 and 3.7.2 compatible django-redis serializer

I'm trying to write a py2.7 - py3.7 compatible django-redis serializer. I'm using django-redis==4.8.0 with django==1.11.22 and the PickleSerializer. I saw this issue https://github.com/niwinz/django-redis/pull/279 on django-redis and wrote a serializer similar to what's said in the thread. However my object seems a little bit more complex? Not sure.

My goal is to have 2 applications running at the same time, one with py2.7 and the other with py3.7. They have to be 100% compatible, and I'm not being able to get past this.

Here is the code for the serializer:

# -*- coding: utf-8 -*-
import six
from django.utils.encoding import force_bytes
from django_redis.serializers.pickle import PickleSerializer

try:
    import cPickle as pickle
except ImportError:
    import pickle


class CompatPickleSerializer(PickleSerializer):

    def loads(self, value):
        if six.PY3:
            return self._loads_py3(value)
        return super(CompatPickleSerializer, self).loads(force_bytes(value))

    def _loads_py3(self, value):
        return pickle.loads(
            force_bytes(value),
            fix_imports=True,
            encoding='bytes'
        )

Example of object I'm trying to serialize:

{
    'created_at': datetime.datetime(2019, 7, 30, 20, 0, 29, 244916, tzinfo = < UTC > ),
    'items': [{
        'unit_price': Decimal('3.00'),
        'name': 'my item',
        'id': '12312312',
    }]
    'id': 'b5c6210d-561f-4e4e-a025-e55b39d95418',
    'name': 'cart',
    'customer': None,
}

The object is a lot bigger then that, but I assume that if I can do the flow with this object, I can do with a bigger one.

After trying to load the object on python 3.7.2, I get this error:

Traceback:  

File "my-project/lib/python3.7/site-packages/django_redis/client/default.py" in decode
  313.             value = int(value)


      During handling of the above exception (invalid literal for int() with base 10: b'\x80\x02}q\x01(U\tdiscountsq\x02NU\x10display_order_idq\x03NU\x12shipping_method_idq\x04NU\x0creservationsq\x05}U\ncreated_atq\x06U 2019-07-30T20:00:14.022071+00:00q\x07U\tpromocodeq\x08NU\x11shippi), another exception occurred:



File "my-project/lib/python3.7/site-packages/django/core/handlers/exception.py" in inner
  41.             response = get_response(request)

File "my-project/lib/python3.7/site-packages/django/core/handlers/base.py" in _get_response
  187.                 response = self.process_exception_by_middleware(e, request)

File "my-project/lib/python3.7/site-packages/django/core/handlers/base.py" in _get_response
  185.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "my-project/lib/python3.7/site-packages/django/views/decorators/csrf.py" in wrapped_view
  58.         return view_func(*args, **kwargs)

File "my-project/lib/python3.7/site-packages/django/views/generic/base.py" in view
  68.             return self.dispatch(request, *args, **kwargs)

File "my-project/lib/python3.7/site-packages/rest_framework/views.py" in dispatch
  489.             response = self.handle_exception(exc)

File "my-project/lib/python3.7/site-packages/rest_framework/views.py" in handle_exception
  449.             self.raise_uncaught_exception(exc)

File "my-project/lib/python3.7/site-packages/rest_framework/views.py" in dispatch
  486.             response = handler(request, *args, **kwargs)

File "django/cart/api/cart.py" in get
  98.         cart = Cart.get(cart_id)

File "django/cart/models/cart.py" in get
  1.190.             raise e

File "django/cart/models/cart.py" in get
  1.186.             data = cart_cache.get(id)

File "my-project/lib/python3.7/site-packages/django_redis/cache.py" in _decorator
  33.             return method(self, *args, **kwargs)

File "my-project/lib/python3.7/site-packages/django_redis/cache.py" in get
  82.                                    client=client)

File "my-project/lib/python3.7/site-packages/django_redis/client/default.py" in get
  208.         return self.decode(value)

File "my-project/lib/python3.7/site-packages/django_redis/client/default.py" in decode
  320.             value = self._serializer.loads(value)

File "django/backports/django_redis/serializers.py" in loads
  28.             return self._loads_py3(value)

File "django/backports/django_redis/serializers.py" in _loads_py3
  35.             encoding='bytes'

Exception Type: TypeError at /my-url/
Exception Value: conversion from bytes to Decimal is not supported

Any ideas on what I could do?

like image 328
jarussi Avatar asked Mar 08 '26 00:03

jarussi


1 Answers

If anyone is having this issue, I came to this solution

# -*- coding: utf-8 -*-
import six
from django_redis.serializers.pickle import PickleSerializer, pickle


class CompatPickleSerializer(PickleSerializer):

    def loads(self, value):
        if six.PY3:
            return self._loads_py3(value)
        return super(CompatPickleSerializer, self).loads(value)

    def _loads_py3(self, value):
        return pickle.loads(
            value,
            fix_imports=True,
            encoding='latin1'
        )

I changed from encoding='bytes' to latin1 and it worked. No need to force_bytes.

Another important thing is that you need to force the PICKLE_VERSION to 2, otherwise you won't be able to pickle the data on python2.7 if it was serialized on python3.

Hope it helps anyone.

like image 78
jarussi Avatar answered Mar 09 '26 22:03

jarussi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!