From: Quaylyn Rimer Date: Mon, 28 Jul 2025 02:45:49 +0000 (-0600) Subject: Fix ValueError in file_uploader.py by converting @gen.coroutine to async/await X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a6e7dc9f8ab95732f5327c9a3ea30af060100ee6;p=thirdparty%2Ftornado.git Fix ValueError in file_uploader.py by converting @gen.coroutine to async/await Fixes #3182 The file_uploader.py demo was failing with ValueError when trying to upload files because @gen.coroutine decorated functions return tornado.concurrent.Future objects, but asyncio.run() expects native coroutines. This change converts the @gen.coroutine decorated functions to native async/await syntax, which is the modern recommended approach and resolves the compatibility issue with asyncio.run(). Changes: - Remove tornado.gen import - Convert @gen.coroutine decorators to async def - Convert all yield statements to await - Maintains backward compatibility with existing Tornado versions --- diff --git a/demos/file_upload/file_uploader.py b/demos/file_upload/file_uploader.py index 67fce7ed0..6f2da0490 100755 --- a/demos/file_upload/file_uploader.py +++ b/demos/file_upload/file_uploader.py @@ -17,14 +17,13 @@ from functools import partial from urllib.parse import quote from uuid import uuid4 -from tornado import gen, httpclient +from tornado import httpclient from tornado.options import define, options # Using HTTP POST, upload one or more files in a single multipart-form-encoded # request. -@gen.coroutine -def multipart_producer(boundary, filenames, write): +async def multipart_producer(boundary, filenames, write): boundary_bytes = boundary.encode() for filename in filenames: @@ -39,29 +38,28 @@ def multipart_producer(boundary, filenames, write): + (b"Content-Type: %s\r\n" % mtype.encode()) + b"\r\n" ) - yield write(buf) + await write(buf) with open(filename, "rb") as f: while True: # 16k at a time. chunk = f.read(16 * 1024) if not chunk: break - yield write(chunk) + await write(chunk) - yield write(b"\r\n") + await write(b"\r\n") - yield write(b"--%s--\r\n" % (boundary_bytes,)) + await write(b"--%s--\r\n" % (boundary_bytes,)) # Using HTTP PUT, upload one raw file. This is preferred for large files since # the server can stream the data instead of buffering it entirely in memory. -@gen.coroutine -def post(filenames): +async def post(filenames): client = httpclient.AsyncHTTPClient() boundary = uuid4().hex headers = {"Content-Type": "multipart/form-data; boundary=%s" % boundary} producer = partial(multipart_producer, boundary, filenames) - response = yield client.fetch( + response = await client.fetch( "http://localhost:8888/post", method="POST", headers=headers, @@ -71,8 +69,7 @@ def post(filenames): print(response) -@gen.coroutine -def raw_producer(filename, write): +async def raw_producer(filename, write): with open(filename, "rb") as f: while True: # 16K at a time. @@ -81,18 +78,17 @@ def raw_producer(filename, write): # Complete. break - yield write(chunk) + await write(chunk) -@gen.coroutine -def put(filenames): +async def put(filenames): client = httpclient.AsyncHTTPClient() for filename in filenames: mtype = mimetypes.guess_type(filename)[0] or "application/octet-stream" headers = {"Content-Type": mtype} producer = partial(raw_producer, filename) url_path = quote(os.path.basename(filename)) - response = yield client.fetch( + response = await client.fetch( "http://localhost:8888/%s" % url_path, method="PUT", headers=headers,