Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UplaodFile is already closed when received by post function #146

Open
guyernest opened this issue Jan 6, 2024 · 1 comment
Open

UplaodFile is already closed when received by post function #146

guyernest opened this issue Jan 6, 2024 · 1 comment

Comments

@guyernest
Copy link

I'm trying to upload a file from a form with FastUI and I'm consistently getting I/O operation on closed file. error.

The code is an adaptation of the demo:

class UploadForm(BaseModel):
    text_file: Annotated[UploadFile, FormFile(accept='text/plain')] = Field(
        description='Upload a text file'
    )

@router.get('/content/{kind}', response_model=FastUI, response_model_exclude_none=True)
def form_content(kind: FormKind):
    match kind:
        ...
        case 'text_file':
            return [
                c.Heading(text='Text File', level=2),
                c.Paragraph(text='Upload the text file.'),
                c.ModelForm(model=UploadForm, submit_url='/api/text_file_upload'),
            ]
        case _:
            raise ValueError(f'Invalid kind {kind!r}')

@router.post('/text_file_upload', response_model=FastUI, response_model_exclude_none=True)
async def text_file_form_post(form: Annotated[UploadForm, fastui_form(UploadForm)]):
    # print(form)
    file: UploadFile = form.text_file
    print(file.filename)
    print(file.file.closed)
    contents = await file.file.read()

The print message at the end prints the name of the file that I choose correctly and then True for the file to be closed.
Everything I try to read the contents of the file gives I/O operation on closed file. error.

@gradient-ascent-ai-lab
Copy link

I ran into this as well, I found that yielding inside the request.form() context instead of returning after it in the fastui_form function fixes it:

def fastui_form(model: _t.Type[FormModel]) -> fastapi_params.Depends:
    async def run_fastui_form(request: fastapi.Request):
        async with request.form() as form_data:
            model_data = unflatten(form_data)

            try:
                yield model.model_validate(model_data)
            except pydantic.ValidationError as e:
                raise fastapi.HTTPException(
                    status_code=422,
                    detail={'form': e.errors(include_input=False, include_url=False, include_context=False)},
                )

    return fastapi.Depends(run_fastui_form)

I've made a PR for it, until it's merged or otherwise fixed one could use a monkeypatch like this as a workaround:

import fastapi
import fastui.forms
import pydantic
from fastapi import params as fastapi_params


def patched_fastui_form(model: type[fastui.forms.FormModel]) -> fastapi_params.Depends:
    async def run_fastui_form(request: fastapi.Request):
        async with request.form() as form_data:
            model_data = fastui.forms.unflatten(form_data)

            try:
                yield model.model_validate(model_data)
            except pydantic.ValidationError as e:
                raise fastapi.HTTPException(
                    status_code=422,
                    detail={'form': e.errors(include_input=False, include_url=False, include_context=False)},
                )

    return fastapi.Depends(run_fastui_form)


fastui.forms.fastui_form = patched_fastui_form

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants