pallets / werkzeug

The comprehensive WSGI web application library.
https://werkzeug.palletsprojects.com
BSD 3-Clause "New" or "Revised" License
6.65k stars 1.73k forks source link

`FileStorage.name` should be the filename #2854

Closed Nama closed 8 months ago

Nama commented 8 months ago

Uploaded files on flask (request.files) have .name and .filename, but the standard python BytesIO sets the full path into .name and werkzeug breaks functions depending on the .name from file-like-objects to have the filename. Since .name from werkzeug file-objects has the form-input-name.

import deepl
from io import BytesIO
from flask import Flask
from flask import request
from flask import send_file
import conf

app = Flask(__name__)
translator = deepl.Translator(conf.token)

@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == 'POST':
        source_file = request.files['file']
        target_lang = 'EN-US'
        if source_file.filename:
            result = BytesIO()
            #source_file.name = source_file.filename  # This is the current workaround
            translator.translate_document(input_document=source_file, output_document=result, target_lang=target_lang, filename=source_file.filename)
            result.seek(0, 0)
            return send_file(result, download_name=source_file.filename, as_attachment=True)
    return '''
    <!doctype html>
    <title>Upload new File</title>
    <h1>Upload new File</h1>
    <form method=post enctype=multipart/form-data>
      <input type=file name=file>
      <input type=submit value=Upload>
    </form>
    '''
[2024-02-21 16:58:16,388] ERROR in app: Exception on / [POST]
Traceback (most recent call last):
  File "venv/lib/python3.11/site-packages/flask/app.py", line 1463, in wsgi_app
    response = self.full_dispatch_request()
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "venv/lib/python3.11/site-packages/flask/app.py", line 872, in full_dispatch_request
    rv = self.handle_user_exception(e)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "venv/lib/python3.11/site-packages/flask/app.py", line 870, in full_dispatch_request
    rv = self.dispatch_request()
         ^^^^^^^^^^^^^^^^^^^^^^^
  File "venv/lib/python3.11/site-packages/flask/app.py", line 855, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)  # type: ignore[no-any-return]
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "test.py", line 20, in index
    translator.translate_document(input_document=source_file, output_document=result, target_lang=target_lang, filename=source_file.filename)
  File "venv/lib/python3.11/site-packages/deepl/translator.py", line 604, in translate_document
    handle = self.translate_document_upload(
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "venv/lib/python3.11/site-packages/deepl/translator.py", line 680, in translate_document_upload
    self._raise_for_status(status, content, json)
  File "venv/lib/python3.11/site-packages/deepl/translator.py", line 203, in _raise_for_status
    raise DeepLException(
deepl.exceptions.DeepLException: Bad request, message: Failed to get the document_type from the file extension (file).

The workaround is a easy one, but still required me to debug and compare a python BytesIO with a werkzeug FileStorage.

Environment:

davidism commented 8 months ago

I see what you're saying, file.name is the "filename", but we set it to the form field name. However, I don't see a way to change this. filename is not a required field in form uploads. File names are "arbitrary", and the file could just have easily actually been called "data" without an extension already. It seems like this is better reported to the library you're using that expects the filename to be in a specific format. Either they should not be doing that (since names are arbitrary), or offer a way to override whatever it is that they're trying to do.

Nama commented 8 months ago

Yes, they should offer an override. But my point is, this is not true:

It basically behaves like a standard file object you know from Python, with the difference that it also has a save() function that can store the file on the filesystem.