0

My function:

@app.route("/media/<collection>/<unique_path>/<path:filename>")
def view_media_file(collection, unique_path, filename):
    document = db.find_one_or_404(collection, 'unique_path', unique_path)
    hex_id = filename.rsplit(".", 1)[0]
    if not hex_id in document.get("files", {}).keys():
        return "Error", 404
    response_obj = app.mongo.send_file(filename)
    return response_obj

when I try Flask-Caching:

@app.route("/media/<collection>/<unique_path>/<path:filename>")
@cache.cached()
def view_media_file(collection, unique_path, filename):
    ...

I get a 500 error with the following traceback:

Traceback (most recent call last):
  File "...flask/env/lib/python3.9/site-packages/flask/app.py", line 2548, in __call__
    return self.wsgi_app(environ, start_response)
  File "...flask/env/lib/python3.9/site-packages/flask/app.py", line 2528, in wsgi_app
    response = self.handle_exception(e)
  File "...flask/env/lib/python3.9/site-packages/flask/app.py", line 2525, in wsgi_app
    response = self.full_dispatch_request()
  File "...flask/env/lib/python3.9/site-packages/flask/app.py", line 1822, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "...flask/env/lib/python3.9/site-packages/flask/app.py", line 1820, in full_dispatch_request
    rv = self.dispatch_request()
  File "...flask/env/lib/python3.9/site-packages/flask/app.py", line 1796, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
  File "...flask/env/lib/python3.9/site-packages/flask_caching/__init__.py", line 413, in decorated_function
    self.cache.set(
  File "...flask/env/lib/python3.9/site-packages/cachelib/simple.py", line 80, in set
    self._cache[key] = (expires, self.serializer.dumps(value))
  File "...flask/env/lib/python3.9/site-packages/cachelib/serializers.py", line 43, in dumps
    serialized = pickle.dumps(value, protocol)
TypeError: cannot pickle '_thread.lock' object

How do I serialize (dumps/loads) a pymongo response object that is a file?

I've tried this with cached and memoized, and I've tried to serialize with json and pickle modules. I understand that thread locking is the issue in the traceback, but I'm not very familiar with it, and don't really understand why it applies to this type of file response delivery, but not with a standard response.

Python 3.10.1

Flask==2.2.2

Flask-Caching==2.0.1

full response_obj:

object: <Response streamed [200 OK]>
type(object): <class 'flask.wrappers.Response'>

response_obj attributes:

<Response streamed [200 OK]>.accept_ranges: None
<Response streamed [200 OK]>.access_control_allow_credentials: False
<Response streamed [200 OK]>.access_control_allow_headers: None
<Response streamed [200 OK]>.access_control_allow_methods: None
<Response streamed [200 OK]>.access_control_allow_origin: None
<Response streamed [200 OK]>.access_control_expose_headers: None
<Response streamed [200 OK]>.access_control_max_age: None
<Response streamed [200 OK]>.add_etag: <bound method Response.add_etag of <Response streamed [200 OK]>>
<Response streamed [200 OK]>.age: None
<Response streamed [200 OK]>.allow: 
<Response streamed [200 OK]>.autocorrect_location_header: False
<Response streamed [200 OK]>.automatically_set_content_length: True
<Response streamed [200 OK]>.cache_control: max-age=31536000, public
<Response streamed [200 OK]>.calculate_content_length: <bound method Response.calculate_content_length of <Response streamed [200 OK]>>
<Response streamed [200 OK]>.call_on_close: <bound method Response.call_on_close of <Response streamed [200 OK]>>
<Response streamed [200 OK]>.charset: utf-8
<Response streamed [200 OK]>.close: <bound method Response.close of <Response streamed [200 OK]>>
<Response streamed [200 OK]>.content_encoding: None
<Response streamed [200 OK]>.content_language: 
<Response streamed [200 OK]>.content_length: 2924675
<Response streamed [200 OK]>.content_location: None
<Response streamed [200 OK]>.content_md5: None
<Response streamed [200 OK]>.content_range: 
<Response streamed [200 OK]>.content_security_policy: 
<Response streamed [200 OK]>.content_security_policy_report_only: 
<Response streamed [200 OK]>.content_type: image/png
<Response streamed [200 OK]>.cross_origin_embedder_policy: COEP.UNSAFE_NONE
<Response streamed [200 OK]>.cross_origin_opener_policy: COOP.UNSAFE_NONE
scoofy
  • 103
  • 8
  • 1
    Are you putting `@cache.cached` before or after the route? – Nick ODell Nov 16 '22 at 21:18
  • No, I'm following the basic docs: route, then cached, then function. I've even tried to serialize the object *itself* and am running into problems. Not sure if i'm just missing something about response objects. – scoofy Nov 16 '22 at 21:36
  • What's the type of `response_obj`? – Nick ODell Nov 16 '22 at 21:59
  • type(response_obj): and I will update with the full object. – scoofy Nov 16 '22 at 22:14
  • 1
    Reading thru the source code of Flask and Flask-PyMongo, I think I have a theory about what's going on: Flask-PyMongo is returning an iterable which yields all of the bytes in the file. You can see this because it's a "Response streaming." It's doing that because it saves memory compared to buffering up the whole file in memory and returning it to the client. But the cache decorator expects the response to not be streaming, or else it can't cache it. Is there some way to avoid a streaming response from the mongodb read? – Nick ODell Nov 16 '22 at 22:58

0 Answers0