Python WebAcademy Blog

Starlette 1.9と1.10がリリース。FastAPIのテストで少しずつ届く応答を確かめられるようになったニュースを初心者向けに解説

|

FastAPIの土台であるStarletteの1.9.0が2026年10月18日に、1.10.0が10月24日に公開されました。テスト用のTestClientで、少しずつ届く応答を届いた順に読めるようになったほか、URLの組み立てやクエリパラメータの比べ方も変わっています。1.8.0と1.10.0で同じコードを動かした結果とともに、上げる前に確かめたい点を整理します。

PythonのWebフレームワークの土台として使われているStarletteの1.9.0が2026年10月18日に、1.10.0が10月24日に公開されました。どちらも、FastAPIを使っている人に関係する変更を含んでいます。

特に嬉しいのが、テスト用のTestClientの変更です。少しずつ届く応答を、届いた順にその場で読めるようになりました。

一方で、URLの組み立て方や、クエリパラメータを比べたときの結果も変わっています。今回は、1.8.0と1.10.0で同じコードを動かしながら、何が変わったのかを見ていきます。

まずはStarletteとはどんなものか

最初に、Starletteを知らない方のために、どんなライブラリなのかを簡単に紹介します。Starletteは、ASGIと呼ばれる非同期のWebアプリの決まりごとに沿って作られた、軽量なWebフレームワークです。

その名前を直接聞く機会は少ないかもしれません。けれども、FastAPIはこのStarletteの上に作られていて、リクエストの受け取りやテストの仕組みなど、多くの部分をStarletteに任せています。

FastAPIとの関係

たとえば、FastAPIのテストで使うfastapi.testclient.TestClientは、中身を見るとStarletteのTestClientをそのまま読み込んでいるだけです。手元のFastAPIでファイルを確かめると、1行だけの次のような中身でした。

from starlette.testclient import TestClient as TestClient  # noqa

PyPIの記録によると、FastAPI 0.162.0が求めるのはstarlette>=0.46.0です。つまり、FastAPIを入れ直すと、Starletteの最新版が一緒に入ることがあります。

FastAPIそのものについては、こちらで紹介しています。【関連記事】PythonのFastAPIとは?

1.9と1.10で何が変わったのかを整理しよう

続いて、2つの版の主な変更を表にまとめます。内容は、Starletteの公式リポジトリにある変更履歴release-notes.mdにもとづいています。

版 公開日 主な変更
1.9.0 10月18日 TestClientが、応答を少しずつ受け取れるように
1.9.0 10月18日 request.urlを組み立てるとき、パスの中の?や#を符号化するように
1.10.0 10月24日 QueryParamsやFormDataの比べ方を変更
1.10.0 10月24日 項目の多いフォームで、メモリと処理の負担を軽く
1.10.0 10月24日 送られてきたフォームの最後の項目を読み落とす問題などを修正

ひとつ前の1.8.0は、Python 3.10のサポートを終えた版でした。その内容は、こちらでまとめています。【関連記事】Starlette 1.8も3.10を卒業。主要ライブラリの対応状況

TestClientで、少しずつ届く応答を確かめられるように

1.9.0のいちばんの変更が、TestClientです。変更履歴によると、client.stream()が、アプリの処理が終わる前に戻ってくるようになりました。

少しずつ届く応答とは

Webアプリの応答は、全部できあがってから一度に返すものばかりではありません。生成AIのチャットのように、できた部分から順に送る応答もあります。

Starletteでは、こうした応答をStreamingResponseで作れます。中身は、yieldで少しずつ値を返すジェネレーターです。

yieldの基本については、こちらで紹介しています。【関連記事】Pythonのyieldとは?

1.8.0と1.10.0で動かしてみた

実際に試してみましょう。1秒おきに数字を1行ずつ送るアプリを作り、TestClientで届いた時間を測ります。

import asyncio
import time

from starlette.applications import Starlette
from starlette.responses import StreamingResponse
from starlette.routing import Route
from starlette.testclient import TestClient


async def numbers():
    for i in range(3):
        yield f"{i}\n"
        await asyncio.sleep(1)


async def endpoint(request):
    return StreamingResponse(numbers(), media_type="text/plain")


app = Starlette(routes=[Route("/numbers", endpoint)])
client = TestClient(app)

start = time.perf_counter()
with client.stream("GET", "/numbers") as res:
    for line in res.iter_lines():
        print(f"{time.perf_counter() - start:.1f}秒: {line}")

1.8.0と1.10.0を、それぞれ新しい仮想環境に入れ、手元のPython 3.11で実行した結果です。動作に関係のない警告の行は省いています。

Starlette 1.8.0
3.0秒: 0
3.0秒: 1
3.0秒: 2

Starlette 1.10.0
0.0秒: 0
1.0秒: 1
2.0秒: 2

1.8.0では、アプリがすべて送り終わる3秒後に、3行がまとめて届いていました。1.10.0では、0秒、1秒、2秒と、送られた順にその場で届いています。

何が嬉しいのか

これまでのTestClientでは、少しずつ届くことそのものを確かめられませんでした。最初の1行がすぐ届くか、という大事な性質を、テストで見られるようになったわけです。

変更履歴によると、読み終わる前に応答を閉じると、アプリの処理も取り消されるようになりました。無限に送り続けるような応答でも、テストが終わらなくなる心配が減ります。

テストの書き方そのものは、こちらで紹介しています。【関連記事】pytest入門|Pythonでテストを書く基本

request.urlの組み立て方が変わった

1.9.0では、URLの扱いも直されています。変更履歴によると、request.urlを組み立てるときに、リクエストのパスに含まれる?や#、バックスラッシュ、先頭の//を、パーセント符号化するようになりました。

パーセント符号化とは、URLの中で特別な意味を持つ文字を、%と2桁の数字で表す書き方です。たとえば?は、%3Fと書き表します。

実際に試してみた

/a%3Fbというパスにアクセスしたときに、request.urlがどうなるかを2つの版で比べました。パスの一部として?を含む、少し変わったURLです。

from starlette.applications import Starlette
from starlette.responses import PlainTextResponse
from starlette.routing import Route
from starlette.testclient import TestClient


async def home(request):
    return PlainTextResponse(str(request.url))


app = Starlette(routes=[Route("/{path:path}", home)])
client = TestClient(app, base_url="http://example.com")
print(client.get("/a%3Fb").text)

実行した結果です。

Starlette 1.8.0
http://example.com/a?b

Starlette 1.10.0
http://example.com/a%3Fb

1.8.0では、パスの中の?がそのまま戻され、?bがクエリパラメータのように見えるURLになっていました。1.10.0では%3Fのまま組み立てられ、元のURLと同じ意味を保っています。

request.urlの値を、ログに残したり、リダイレクト先に使ったりしている場合は、上げたあとに一度確かめておきましょう。

QueryParamsの比べ方が変わった

1.10.0では、クエリパラメータを表すQueryParamsや、フォームの中身を表すFormDataを、==で比べたときの結果が変わりました。変更履歴によると、キーごとに比べつつ、同じキーに複数の値がある場合は、その順番も区別するようになっています。

2つの版で比べてみた

まずは、クエリパラメータの比較を試してみます。

from starlette.datastructures import QueryParams

print(QueryParams("a=1&a=2") == QueryParams("a=2&a=1"))
print(QueryParams("a=1&b=2") == QueryParams("b=2&a=1"))

実行した結果です。

比べたもの 1.8.0 1.10.0
a=1&a=2とa=2&a=1 True False
a=1&b=2とb=2&a=1 True True

同じaというキーに、1と2が逆の順番で入っている場合は、1.10.0では別物と判断されました。違うキーの並び順だけが違う場合は、これまでどおり同じと判断されます。

ファイルを複数送るフォームでのエラーも直った

変更履歴によると、同じ名前で複数のファイルを送ったFormDataは、比べるとTypeErrorになっていました。手元でも、1.8.0では'<' not supported between instances of 'UploadFile' and 'UploadFile'というエラーが出ました。

1.10.0では、同じ比較がエラーにならず、Falseが返ってきました。ファイルどうしを大小で並べ替えようとして失敗していた処理が、なくなったわけです。

そのほかの修正

ほかにも、細かな修正がたくさん入っています。初心者にも関係しそうなものを紹介します。

1.10.0では、送られてきたフォームの最後の項目を、条件によって読み落とす問題が直されました。変更履歴によると、リクエストの本体が、空の区切りを送らずに終わった場合に起きていたそうです。

また、.envのような設定ファイルから値を読むConfigで、値を囲む引用符を、外側の1組だけ取り除くように直されています。1.9.0では、Hostによる振り分けやTrustedHostMiddlewareで、ホスト名の大文字と小文字を区別しないようにもなりました。

上げる前にやっておきたいこと

最後に、Starletteを1.10.0へ上げる前に確かめておきたいことを整理します。FastAPIを使っている方も同じです。

順番 確認すること
1 Python 3.11以上を使っているか
2 TestClientのstream()を使ったテストが、新しい動きで通るか
3 request.urlの値を、ログやリダイレクトに使っていないか
4 QueryParamsやFormDataを==で比べていないか
5 使うStarletteの版を、requirements.txtなどで固定しているか

私は10年ほどエンジニアとして開発に関わってきましたが、少しずつ応答を返すAPIのテストで、最初のデータがすぐ届くかを確かめられず、本番で初めて遅さに気づいたことがあります。テストで確かめられることが増えるのは、地味ですが大きな前進です。

FastAPIの最近の大きな変更については、こちらでまとめています。【関連記事】FastAPIの変更は0.156まで続く

まとめ

Starletteの1.9.0が2026年10月18日に、1.10.0が10月24日に公開されました。TestClientで少しずつ届く応答を確かめられるようになり、request.urlの組み立て方やQueryParamsの比べ方も変わっています。

FastAPIを使っている方にとっても、テストの書き方が広がる嬉しい変更です。上げたあとは、上の確認ポイントを一度見直してみてください。

ここまでお読みいただきありがとうございました。

参考情報

次のアクション

記事で学んだ内容を実際に動かしてみよう

Python WebAcademyでは、ブラウザ上でコードを書きながら基礎から実践まで体系的に学べます。

Python WebAcademyの学習画面

あわせて読む

関連記事

ブログ一覧へ

Python学習ロードマップ

まずはこの3講座から

記事で気になったテーマを、順番に手を動かしながら学べます。

ロードマップを見る