long2ice/fastapi-cache

TypeError: unhashable type: 'dict'

robert-nix opened this issue · 4 comments

When using @cache(coder=CustomJsonCoder), where CustomJsonCoder is a subclass of fastapi_cache.coder.JsonCoder, returning a dict from a handler causes TypeError: unhashable type: 'dict' to raise from #112's hash generation.

e.g.

from fastapi.encoders import jsonable_encoder
from fastapi_cache.coder import JsonCoder


class CustomJsonCoder(JsonCoder):
    @classmethod
    def encode(cls, value):
        return jsonable_encoder(value)

    @classmethod
    def decode(cls, value):
        return value

@router.get("/v1/example")
@cache(expire=60, coder=CustomJsonCoder)
async def get_example():
    return {'message': 'test'}

Also FWIW I'm not quite sure why we needed to use this custom coder, I'm just putting this issue in to document the unintended API breakage.

Thanks! Could you submit a PR?

This is entirely expected; jsonable_encoder() doesn't encode to JSON. It is instead meant to be used as the default hook in a JSON encoder. It doesn't return a string, but the Coder.encode() method must return a string, so the fact that things then break further down the line is not suprising.

This works correctly and is probably what you intended to do:

class CustomJsonCoder(JsonCoder):
    @classmethod
    def encode(cls, value):
        return json.dumps(value, default=jsonable_encoder)

    @classmethod
    def decode(cls, value):
        return json.loads(value)

The next release changes Coders to work with bytes instead of strings, so the above would also need to have str.encode() and bytes.decode() calls.

Thanks! Could you submit a PR?

The Coder interface is now firmly handling bytes, only, though, so I'm not sure how a PR could help here. ;-)

I'm closing this issue now; the coder API can only handle bytes. If there was a specific use-case for returning something else, do let us know!