adamchainz / django-perf-rec

Keep detailed records of the performance of your Django code.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Handle cache keys that are python3 bytes

twidi opened this issue · comments

Actually in CacheOp.__init__, bytes are managed via the isinstance(key_or_keys, (Mapping, Sequence)) code branch, which will not work.

I actually made it work by patching your code adding

        if isinstance(key_or_keys, bytes):
            key_or_keys = key_or_keys.decode('utf-8')

at the top, but there may be a better way.

bytes aren't documented as officially supported by Django's cache framework, and searching cache\.get.*b['"] in its test suite shows me no usage.

I think you should really be using strs and you're just lucky that whichever cache backend you're using is lenient, probably because of the Python 2 + 3 compatibility being maintained. On Python 3 bytes iterate in a different way and have other quirks that don't make them the same as 'ascii strings'.

Ok I understand, I'll use patchy (as you do, thanks for letting me discover this awfully beautiful library) to patch your code :)

PS: btw, I don't create this kind of keys myself, there are created by django-waffle ;) cf https://github.com/jsocol/django-waffle/blob/master/waffle/utils.py#L19-L25

I think you should take this up with django-waffle then 😄

If I have to choose, I prefer to keep a heavily used library in the django world that works very fine and patch your code manually (no offense, I totally understand your point of view!!) ;)

Keep the great job !

Waiting for the upgrade of django-waffle (thanks for the issue), and in case someone else encounter the same "issue", here is my code (works for me, but provided without any guarantee):

from django_perf_rec.cache import CacheOp
import patchy

patchy.patch(CacheOp.__init__, '''
@@ -1,6 +1,8 @@
 def __init__(self, alias, operation, key_or_keys):
     self.alias = alias
     self.operation = operation
+    if isinstance(key_or_keys, bytes):
+        key_or_keys = key_or_keys.decode('utf-8')
     if isinstance(key_or_keys, six.string_types):
         self.key_or_keys = self.clean_key(key_or_keys)
     elif isinstance(key_or_keys, (Mapping, Sequence)):
''')

patchy.patch(CacheOp.clean_key, '''
@@ -3,6 +3,8 @@
     """
     Replace things that look like variables with a '#' so tests aren't affected by random variables
     """
+    if isinstance(key, bytes):
+        key = key.decode('utf-8')
     for var_re in cls.VARIABLE_RES:
         key = var_re.sub('#', key)
     return key
''')