nikkie-ftnextの日記

イベントレポートや読書メモを発信

2025年12月から2026年年始にかけて共有された Claude Code の知見の自分向けリンク集

はじめに

七尾百合子さん、お誕生日 295日目 おめでとうございます! nikkieです。

師走から Claude Code の情報が爆発しているように感じます。
一度に読みきれないので、自分向けリンク集という立ち返りポイントを作ります

目次

Borisさん(Claude Code の作者)

こちらから始まる一連のスレッド!

皆さんまとめたり翻訳したりしていますが、私も posfie にスクラップしておきました。
Claude Codeの作者BorisさんのClaude Code活用術 - posfie

Boris さんは『プログラミングTypeScript』の著者でもあります(後述の aimeetup で知った情報)

Advent Calendar

Twitter で日次で tips 紹介してくださった方がいました。

Adoさん(Anthropic)

スクラップ版です。
Advent of Claude by Ado-san 2025 - posfie

Oikonさん

Claude Code の変更にキャッチアップされている Oikon さん

Advent Calendar とは別ですが、こちらのスライドも素晴らしいです!

ぬこぬこさん主催 aimeetup

Claude Code Meetup Tokyo

実践者の知見が #aimeetup に集まりました

12月出版 Claude Code 本!

技術評論社さんから2冊出ました!

『Claude CodeによるAI駆動開発入門』

注目ポイントはAI駆動開発の速さの体験かなと思っています

5分でアプリケーションを作る(第2章)
30分でAIチャットボットを作る(第3章)
半日で社内システムを開発する(第4章)

著者の平川さんによる YouTube

『実践Claude Code入門』(ウニ本)

ジェネラティブエージェンツさんから

注目ポイント

第6章 Claude Codeの動作原理を理解する
第7章 Claude Codeを意図通りに動かす

Claude Code を例にコーディングエージェントを理解する本なのかなと思って読み進めています

出版イベントのアーカイブです

まだまだ続く新星ゾーン

終わりに

Claude Code、2025年12月には認知を獲得した感があります。
夏くらいの性能劣化によるユーザ離脱から盛り返した印象です。

書籍が出たことで、コーディングエージェントといえば Claude Code がまず候補に挙がるのではないでしょうか。
世の中には Codex CLIOpenHands CLIgoose などなど他の選択肢もあるのですが、そこまで前のめりに追いかけていない方は「とりあえず Claude Code」になった気がしますね

しかし コンナニイチドニヨミキレナイヨ-
色んな人の情報をつまみ食いする形で、自分のその日の Claude Code(やコーディングエージェント)の使い方をちょっとでも改善すべく読み進めていこうと思います。
たぶん自転車と同じってこと!

Agent Development Kit (Python) は output_schema で構造化した JSON を output_key で state に保存できる

はじめに

七尾百合子さん、お誕生日 294日目 おめでとうございます! nikkieです。

ADK のドキュメントに興味を引いた記述があり、手を動かして確認しました

目次

output_schemaoutput_keyを同時に指定できる

ドキュメント「LLM Agent」の「Structuring Data」

output_schemaoutput_keyLlmAgentの引数であり、いずれもオプショナルです。

output_schema (Optional):
Define a schema representing the desired output structure. If set, the agent's final response must be a JSON string conforming to this schema.

output_schemaPydanticBaseModelを継承したクラス1を指定することで、エージェントの最終応答をJSONに構造化できます

output_keyを設定すると

If set, the text content of the agent's final response will be automatically saved to the session's state dictionary under this key.
This is useful for passing results between agents or steps in a workflow.

エージェントの最終応答がセッションの state に自動で保存されます。
state で後続のエージェントにデータを渡せます。
session.stateのドキュメントより、後続エージェントは{key}として、instruction の中に state の値を埋め込めます。
https://google.github.io/adk-docs/sessions/state/#accessing-session-state-in-agent-instructions

そしてこの2つが併用できることを知ったのです。
エージェントの最終応答がスキーマを満たす JSON に構造化され、それが自動で state に保存されます。
https://github.com/google/adk-python/blob/v1.21.0/tests/unittests/agents/test_llm_agent_output_save.py#L134-L148

adk-python の samples でoutput_schemaoutput_keyを指定している例を動かす

adk-python リポジトリにある samples を動かしてプロンプトを覗きます
https://github.com/google/adk-python/tree/v1.21.0/contributing/samples

$ git clone https://github.com/google/adk-python.git
$ cd adk-python
$ uvx --from google-adk adk web contributing/samples -v 2> debug.log

環境変数GOOGLE_API_KEY(またはGEMINI_API_KEY)の設定が必要です。
今回は Python 3.14.0、adk 1.21.0 で動かしました。

上記コマンドにより、contributing/samples以下のエージェントを ADK の Web UI で動かせます。
プロンプトを見たいので-vを指定しましたが、ログの実装に伸びしろ2があってコンソールがノイジーになるので、ファイルにリダイレクトしています。

output_schemaoutput_keyを同時に指定している例は2つ見つかりました。

1. fields_output_schema

https://github.com/google/adk-python/tree/v1.21.0/contributing/samples/fields_output_schema

root_agent = Agent(
    name='root_agent',
    model='gemini-2.0-flash',
    instruction="""省略""",
    output_schema=WeatherData,
    output_key='weather_data',
)

エージェントはWeatherDataスキーマに沿った JSON を返し、それが自動で state に保存されます。

DEBUG ログから Gemini API へのリクエスト・レスポンスはこちらです。
https://gist.github.com/ftnext/f6e7abfa2b3ada5ae665eddfee35b710
レスポンスでは、WeatherDataスキーマに沿った JSON 文字列が返っています。

LLM Response:
-----------------------------------------------------------
Text:
{
  "temperature": "26 C",
  "humidity": "20%",
  "wind_speed": "29 mph"
}
-----------------------------------------------------------

2. json_passing_agent

https://github.com/google/adk-python/tree/v1.21.0/contributing/samples/json_passing_agent

root_agentSequentialAgentです。
ユーザの入力から order の JSON に構造化し、その後のエージェントで参照して料金計算します。

  1. order_intake_agent
    • output_key='pizza_order'
    • output_schema=PizzaOrder
    • tools=[get_available_sizes, get_available_crusts, get_available_toppings]
  2. order_confirmation_agent
    • instruction で{pizza_order}を参照します
    • calculate_price toolを持ちます

なお、ユーザは一度にすべての事項を注文しないと、実行中にエラーになります。
(Pydantic のバリデーションエラーだったので、後述するようにorder_intake_agentPizzaOrderJSON しか出力できない制限によるのかなと思います。深掘りは宿題事項です)

リクエスト・レスポンスを以下に保存しました3
https://gist.github.com/ftnext/1e2fc6853aa9f7cf51bf2f5d83f2d666

まず、2つ目のorder_confirmation_agentですが、state にある構造化データが入ってきていました。
JSON を dump した文字列とちょっと違いますね(Python の辞書ですかね)

Then, summarize the order details from {'size': 'large', 'crust': 'thin', 'toppings': ['pepperoni', 'mushrooms']} and include the price in your summary.

calculate_priceツールはtool_context.state.get('pizza_order')して、引数ではなく state からデータを取得して計算する実装になっています

1つ目のorder_intake_agentでは、tool use と構造化出力を両立させるために工夫されていました。
「System Instruction」に追加されてる4

IMPORTANT: You have access to other tools, but you must provide your final response using the set_model_response tool with the required structured format. After using any other tools needed to complete the task, always call set_model_response with your final answer in the specified schema format.

「Functions」にset_model_responseがあります!

set_model_response: {'size': {'type': <Type.STRING: 'STRING'>}, 'crust': {'type': <Type.STRING: 'STRING'>}, 'toppings': {'items': {'type': <Type.STRING: 'STRING'>}, 'type': <Type.ARRAY: 'ARRAY'>}}

Gemini API に tool use と構造化出力両方やらせようとして怒られた記憶があるので、すべて tool use とする workaround ということでしょうか。
プロンプトでお願いなので、1000回とか10000回やったらやらないこともあるんじゃないかという気がします(体感ですが Gemini はエージェントの能力がまだ低いので)

終わりに

ADK のドキュメントにLlmAgentoutput_schemaoutput_keyを同時に指定できるという記載を見つけ、adk-python リポジトリ内の samples を動かしてプロンプトを確認しました

  • 同時に指定するとoutput_schemaで構造化された JSON が state に保存される(Gemini の構造化出力によると理解)
  • output_schematoolsを同時に指定した場合、set_model_response tool を使った workaround で tool use と構造化出力を両立していた

なお、adk web(やadk run)がデフォルトで.adk/session.dbを作るという挙動の変更に今回気づきました。
v1.20.0 で(リリースノートにないですが)入ったようです。
https://github.com/google/adk-python/commit/ed9da3fa45cc0d2ee1a88446e8026519eda1134b

Previously, adk run and adk web used in-memory session storage by default, causing sessions to be lost on restart. Now sessions persist to .adk/session.db automatically. To use in-memory storage, pass --session-service-uri memory://


  1. model_validate_json()とバリバリ Pydantic の機能を使う実装になっていることを今回知りました
  2. ADK のコードの中でルートロガーを設定しているので、DEBUG以上のログが出力される結果ノイジーになってます
  3. order_intake_agent は調子がいいときは3つのツールを並列で呼び出していました
  4. 詳しく追えてませんが、LlmRequestを加工する機構があるようです。https://github.com/google/adk-python/blob/v1.21.0/src/google/adk/flows/llm_flows/_output_schema_processor.py#L52-L64

rustpython-ast の visitor feature を使って、Rust で Python プログラムの属性アクセス回数を数える

はじめに

七尾百合子さん、お誕生日 293日目 おめでとうございます! nikkieです。

Rust で Python 抽象構文木の Visitor パターン、素振りしました。

目次

できたもの

例えばこんな Python プログラムを渡すと

def greet(person):
    return person.name + " " + person.age

.name.ageといった属性アクセスの回数を数えます

% cargo run -q
Attributes count: 2

rustpython-ast の visitor feature

過去に rustpython-parser を使って、Python プログラムの抽象構文木を dump しました

rustpython-parser は rustpython-ast に依存しています。
https://github.com/RustPython/Parser/blob/0.4.0/parser/Cargo.toml#L27

そして、rustpython-ast に visitor feature があることを知りました1

rustpython-ast の visitor を素振りしようとしていて、こんな issue を発見2

その中にめちゃめちゃ参考になるコメントがあったので、こちらを元に手を動かしました。
https://github.com/RustPython/Parser/issues/108#issuecomment-2470479057

rustpython-ast の Visitor で属性アクセスの回数を数える

% rustc --version
rustc 1.92.0 (ded5c06cf 2025-12-08)
% cargo --version
cargo 1.92.0 (344c4567c 2025-10-21)

環境構築

% cargo add rustpython-parser
% cargo add rustpython-ast --features visitor

先の Python プログラム3

def greet(person):
    return person.name + " " + person.age

Rust でこんな抽象構文木になります

statement が配列に入っているので、回して処理していきます(forcounter.visit_stmt()に渡す4

抽象構文木の中で属性アクセスに対応するノードはExprAttribute
ちょうど2回あります。

そこで、Visitor が持っているvisit_expr_attribute()5をこちらで実装します。

struct AttributeCounter {
    attributes_count: usize,
}

impl Visitor for AttributeCounter {
    fn visit_expr_attribute(&mut self, node: ExprAttribute) {
        self.attributes_count += 1;
        self.generic_visit_expr_attribute(node);
    }
}

The easiest way to start counting attributes would be to override the method that's visiting attributes, this is visit_expr_attribute at the time of writing.

If we look at the source code of the visitor, it is calling to a generic implementation which drills down to the other nodes. So to ensure the whole AST is traversed, we should also keep the original implementation in our code.

counter.visit_stmt(stmt)で始めて、body の statement について再帰的に処理していく中で、今回実装したvisit_expr_attribute()の処理も呼び出されるという理解です
https://github.com/RustPython/Parser/blob/0.4.0/ast/src/gen/visitor.rs#L48-L50

終わりに

rustpython-ast の visitor で手を動かしました。
ドキュメントが見つからない中、issue コメントにただただ感謝です。

Ruff みたいな Rust 製の Python リンタを作ってみたい気持ちがあります。
道のりはまだまだ遠いのかもしれませんが、ゆっくりであっても向かっていきたいなと思います

P.S. Python の抽象構文木では

rustpython-ast と少し違いました。

% cat <<EOF | python3.14 -m ast
def greet(person):
    return person.name + " " + person.age
EOF

Attributeを数えることになりそうです。
https://docs.python.org/ja/3/library/ast.html#ast.Attribute

たとえば d.keys のような属性へのアクセスです。


  1. 2025年のベスト勉強会だったかもしれません
  2. ドキュメントには visitor feature は見つからなかったんですよ
  3. Python コード中のダブルクォートを Rust の文字列として扱うために、raw string(生文字列。例r#"And then I said: "There is no escape!""#)を知りました。ref: Strings - Rust By Example
  4. 今回参考にしている issue コメントでも「The Visitor is built to be a recursive walker/visitor, starting with the function visit_stmt.
  5. rustpython-ast で Visitor は生成されるコードみたいです。どんなメソッドを持っているか一覧できました。https://github.com/RustPython/Parser/blob/0.4.0/ast/src/gen/visitor.rs

Python 3.13 から対話型インタプリタは、2つある

はじめに

七尾百合子さん、お誕生日 292日目 おめでとうございます! nikkieです。

Python の対話モードに色がつくようになったな〜と感じていたのですが、思っていたより大きな変化でした。
Python 3.12 までの対話型インタプリタとは別に追加されていたのです。

目次

What's New In Python 3.13「改善された対話的インタープリタ

改善された対話的インタープリタ

PyPy project のコードをベースに作られた新しい Python interactive シェルがデフォルトで使われるようになりました。

  • helpexitquit といったREPL 固有のコマンドが直接サポートされるようになり、関数として呼び出す必要がなくなりました。
  • デフォルトでプロンプトやトレースバックが 色付きで表示 されるようになりました。
  • インタラクティブなヘルプが F1 で閲覧できるようになり、REPLとは別にコマンド履歴が記録されるようになりました。
  • F2 で履歴を閲覧できるようになりました。出力や プロンプト内の >>> と ... は表示されません。
  • F3 で「ペーストモード」に切り替え、長いコードブロックをより簡単に貼り付けることができるようになりました

新しいインタプリタ、めっちゃ機能がある...!1

Python チュートリアルの付録にも掲載されています。
16.1. 対話モード

Windows や、 curses をサポートする Unix 系システムでは、 Python 3.13 以降 新しい対話型シェルがデフォルトで使われます。

What’s new in Python 3.14「Default interactive shell」

3.14 でもさらに2点改善されています。

The default interactive shell now highlights Python syntax.

The default interactive shell now supports import auto-completion.

タブキーで補完!

Python 3.13 から、色づく標準ライブラリのコンソール出力!

対話モードの話からは脱線ですが、Python の出力がカラフルになってきています!

What’s new in Python 3.13」を color で検索

新しい 対話的インタープリタ や トレースバック 、 doctest の出力において色付き文字に対応しました。

「What’s new in Python 3.14」Improved modules を color で検索すると

argparse で実装したライブラリ、Python 3.14 で実行しただけで、uv(Rust製ツール)のヘルプメッセージみたいにカラフルになって驚きました。

まとめ:Python 3.13 から新しい対話型インタプリタがデフォルトです!

Python 3.13 から対話型インタプリタに2つ目が追加され、そちらがデフォルトに切り替わっていました。
Python 3.14 時点で

F1 で typing.TypedDict のヘルプを見る例です

また、対話モードとは別に標準ライブラリのコンソール出力もカラフルになっています

終わりに

普段使っている対話モード、「プロンプトが色づいたな」くらいの認識でしたが、対話型インタプリタとして全く別物になっていました。
人力での開発を捗らせてくれそうです!
特に F1, F2, F3 の運指は体得したいですね。

一方、コーディングエージェントのことを考えると、新しい対話型インタプリタが彼らにもたらしたものはなさそうですよね。
出力に色がついても彼らには見えないですし、色でのシンタックスハイライトよりも LSP ツールとかのほうが彼らには役に立つでしょう。
エラーメッセージの改善は人類にもコーディングエージェントにも嬉しいものだと思うのですが、対話型インタプリタの改善(特に色づけ)をどこまでやるかはちょっと気になりました(コーディングエージェントが到来していなければいくらでもやってほしいのですが)


  1. Python 3.13で更新された機能の紹介 | gihyo.jpでも取り上げられていました(F2・F3の動画付き)

SpeechRecognition に書いた TypeVar ふりかえり:TypedDict や Protocol も Generic にする

はじめに

七尾百合子さん、お誕生日 291日目 おめでとうございます! nikkieです。

直近書いたTypeVarを使った型ヒントについて、さらに理解を深めたく、アウトプットします1
以下の自戦解説です

目次

Before: 似ている2つの TypedDict

SpeechRecognition はローカルで Whisper をopenai-whisperfaster-whisperとで動かせます。
以下のように返り値の型TranscribeOutputを定義していました

speech_recognition/recognizers/whisper_local/whisper.py

class Segment(TypedDict):
    id: int
    seek: int
    start: float
    end: float
    text: str
    tokens: list[int]
    temperature: float
    avg_logprob: float
    compression_ratio: float
    no_speech_prob: float


class TranscribeOutput(TypedDict):
    text: str
    segments: list[Segment]
    language: str


class TranscribableAdapter:
    def transcribe(
        self, audio_array: np.ndarray, **kwargs
    ) -> TranscribeOutput:
        ...

speech_recognition/recognizers/whisper_local/faster_whisper.py

from faster_whisper.transcribe import Segment


class TranscribeOutput(TypedDict):
    text: str
    segments: list[Segment]
    language: str


class TranscribableAdapter:
    def transcribe(
        self, audio_array: np.ndarray, **kwargs
    ) -> TranscribeOutput:
        ...

2つのTranscribeOutputの違いはSegmentの定義です

  • openai-whisper の方は、私が定義しています
  • faster-whisper の方は、faster-whisper に含まれるSegmentを使っています

これらの型ヒントの共通化を図ることにしました

After: TypeVarを使ってこうなりました

speech_recognition/recognizers/whisper_local/base.py

from typing import Generic, TypeVar

SegmentT = TypeVar("SegmentT")


class TranscribeOutputBase(TypedDict, Generic[SegmentT]):
    text: str
    segments: list[SegmentT]
    language: str

speech_recognition/recognizers/whisper_local/whisper.py

class Segment(TypedDict):
    # 上で見たのと同じなので省略します


class TranscribableAdapter:
    def transcribe(
        self, audio_array: np.ndarray, **kwargs
    ) -> TranscribeOutputBase[Segment]:
        ...

speech_recognition/recognizers/whisper_local/faster_whisper.py

from faster_whisper.transcribe import Segment


class TranscribableAdapter:
    def transcribe(
        self, audio_array: np.ndarray, **kwargs
    ) -> TranscribeOutputBase[Segment]:
        ...

型変数を使った型TranscribeOutputBaseを定義し、whisper.py、faster_whisper.pyでは具体的な型を指定しました。
変数に代入した感覚です

ふりかえり:TypeVar関連のドキュメント確認

以前書いた記事の使い所と感じ、やってみました。

TypedDictは型変数も取れます!
(ただ Python 3.10 だと実行時にエラーになったので、typing-extensionsTypedDictを使っています)

SegmentT = TypeVar("SegmentT")


class TranscribeOutputBase(TypedDict, Generic[SegmentT]):
    segments: list[SegmentT]

https://docs.python.org/ja/3/library/typing.html#typing.TypedDict

To create a generic TypedDict that is compatible with Python 3.11 or lower, inherit from Generic explicitly:

ハマったのが、今回 Protocol を使っているのですが、Protocol も Generic にする必要がありました。

-class Transcribable(Protocol):
+class Transcribable(Protocol, Generic[SegmentT]):
    def transcribe(
        self, audio_array: np.ndarray, **kwargs
    ) -> TranscribeOutputBase[SegmentT]:
        pass

whisper_local/whisper.py で出ていたエラー

error: Argument 1 to "WhisperCompatibleRecognizer" has incompatible type "TranscribableAdapter"; expected "Transcribable"  [arg-type]
note: Following member(s) of "TranscribableAdapter" have conflicts:
note:     Expected:
note:         def [SegmentT] transcribe(self, audio_array: ndarray[Any, Any], **kwargs: Any) -> TranscribeOutputBase[SegmentT]
note:     Got:
note:         def transcribe(self, audio_array: ndarray[Any, Any], **kwargs: Any) -> TranscribeOutputBase[Segment]

Claude Code (Sonnet 4.5) にガイドしてもらったのですが、Transcribable Protocol のtranscribeメソッドでSegmentTという型変数を使っているのに、Generic になっていなかったので怒られました。
変数のスコープみたいな話なのかなと思っています

https://docs.python.org/ja/3/library/typing.html#typing.Protocol

プロトコルクラスはジェネリックにもできます。

おや、こんな短く書けるんですね(このブログを書いたことで Sonnet 4.5 を超えたぞ)

T = TypeVar("T")

class GenProto(Protocol[T]):
    def meth(self) -> T:
        ...

終わりに

直近書いたTypeVarを使った型ヒントをふりかえりました。

  • 似たTranscribeOutputクラスが2つあり、違いを観察したところ、型変数の使い所と思った
  • 型変数SegmentTに具体な型を代入するイメージで、目論見通り型ヒントをリファクタリングできた
  • Protocol でも型変数を使いたい場合はProtocol[SegmentT]を継承する必要があった

型変数は今の私にはあまり書く機会がないですが、ハマると型ヒントがとてもいい感じなので、もっと理解したいです。
上達したいのでお気づきの点があったら@ftnextまでお知らせください。

最後に、推しのアドベントカレンダーから読み返したい記事を置きます


  1. 2025年12月31日(水)のリリース - nikkie-ftnextの日記の脚注を回収します。「TypeVar を使って頑張ったのは別記事にできたらいいな

「What's new in Python 3.14」より finally 節での return(や continue・break)は SyntaxWarning で警告されるようになりました(PEP 765)

はじめに

七尾百合子さん、お誕生日 290日目 おめでとうございます! nikkieです。

明けましておめでとうございます1
新年早々、悪いことしちゃいます!

目次

What's new in Python 3.14 における PEP 765 への言及

PEP 765: Control flow in finally blocks

The compiler now emits a SyntaxWarning when a return, break, or continue statement have the effect of leaving a finally block.

SyntaxWarningは「曖昧な構文に対する警告の基底クラス

PyCon JP 2025 での t-string の発表や、11月の Python Meetup Fukuoka で耳にする機会はありました。
ただなぜこれをしたかが理解できていなかったので、この機に手を動かしました。
というか、私、(Python 歴8年の中で)tryfinallyreturnしたことない気がします

クイズ:この関数は何を返すでしょうか?

Python 言語リファレンスより「8.4.4. finally 節

def foo():
    try:
        return "try"
    finally:
        return "finally"


print(foo())

関数fooは、try節で"try"returnしています。
続くfinally節にもreturnがあります。
はて、foo()の返り値は?

  1. "try"
  2. "finally"
  3. 上記以外(自由回答)
% uv run --python 3.13 script.py  # 本記事では Python 3.13.8 です
finally

The return value of a function is determined by the last return statement executed.

最後に実行されるのはtry節のreturn文ではなくfinally節のreturnなので、foo()の返り値は"finally"です。

これは言語機能を知るための例で、「こんなの書かないよ」と聞こえてきたので、もう1問!
tryで例外送出する場合です。

def foo():
    try:
        1 / 0
    finally:
        return 42


print(foo())

気になる返り値は...

  1. 42
  2. ZeroDivisionError送出(返り値ではなく)
  3. 上記以外(自由回答)
% uv run --python 3.13 script.py
42

If an exception occurs in any of the clauses and is not handled, the exception is temporarily saved.

try節で発生したZeroDivisionErrorは一時的に保存されます。

The finally clause is executed.
If there is a saved exception it is re-raised at the end of the finally clause.

finally節の終わりに保存されていた例外が再送出されるとありますが、問題なのはreturn(などの制御フロー)が入っている場合

If the finally clause executes a return, break or continue statement, the saved exception is discarded.

finally節がreturn文を実行する時、一時保存されていた例外は捨てられます。
なので、42が返ったわけですね。

finally節のreturn文、初めて書きましたが、動きがだいぶトリッキーです

Python チュートリアル8.7. クリーンアップ動作を定義する」でも

もし finally 節がある場合、 try 文が終わる前の最後の処理を、 finally 節が実行します。 try 文が例外を発生させるか否かに関わらず、 finally 節は実行されます。

(※try文で例外が発生したケースで)If the finally clause executes a break, continue or return statement, exceptions are not re-raised. This can be confusing and is therefore discouraged.

finally節でreturn文を書いてはいけません

PEP 8 に記載されていたり、リンタに指摘するルールがあったりしました2

PEP 8 Programming Recommendations

Use of the flow control statements return/break/continue within the finally suite of a try...finally, where the flow control statement would jump outside the finally suite, is discouraged.
This is because such statements will implicitly cancel any active exception that is propagating through the finally suite

pylint「return-in-finally / W0134」

flake8-bugbear & Ruff「jump-statement-in-finally (B012)」

PEP 765 – Disallow return/break/continue that exit a finally block

この流れの先に、Python 3.14 で取り込まれた PEP 765 です。

This PEP proposes to withdraw support for return, break and continue statements that break out of a finally block.

The current PEP is based on empirical evidence regarding the cost/benefit of this change,

PEP に調査レポートが添えられていて、上で取り上げた PEP 8 やリンタの話も知りました。

Indeed, PEP8 now recommends not to used control flow statements in a finally block, and linters such as pylint, ruff and flake8-bugbear flag them as a problem.

SyntaxWarningは例外でなく警告なので実行はできますが、無言の 3.13 までと比べて「俺、何かやっちゃいました?!」感が増しています

% uv run --python 3.14 script.py  # 本記事では Python 3.14.0 です
/.../script.py:5: SyntaxWarning: 'return' in a 'finally' block
  return "finally"
finally
% uv run --python 3.14 script.py
/.../script.py:5: SyntaxWarning: 'return' in a 'finally' block
  return 42
42

終わりに

2025年10月リリースの Python 3.14 から、PEP 765 により finally節のreturn文(やbreakcontinue)に警告が出ます。
今回初めてfinally節でreturn文を書きましたが、例外を捨ててfinallyreturnで返してしまい、動きに邪悪さも感じました。

私は8年書かなかったので人類が書く機会はあんまりないんじゃないと思いますが、

📣 おい、そこの Claude Code3、絶対に書くんじゃありませんよ?

変更履歴


  1. pyright にも入っています。https://github.com/microsoft/pyright/releases/tag/1.1.400
  2. LLM の皆さんは前科しかありませんから〜(なのでリンタを渡しましょう)

2025年12月31日(水)のリリース

はじめに

七尾百合子さん、お誕生日 289日目 おめでとうございます! nikkieです。

毎週水曜のリリース報告エントリです1

目次

SpeechRecognition v3.14.5

前回:2025年11月19日(水)のリリース - nikkie-ftnextの日記

https://pypi.org/project/SpeechRecognition/3.14.5/

12月にいただいた型ヒント修正のプルリクエストを取り込んでリリースしました2

再発防止として、自分でも気づけるように、型チェックをGitHub Actionsで実行するようにしました。
Recall.aiさんにスポンサードいただいてることもあり、月1リリースが目標です。

recent-state-summarizer 0.0.4

https://pypi.org/project/recent-state-summarizer/0.0.4/

前回の 0.0.3 でコマンドを壊していたので直しました。

% uvx --from recent-state-summarizer==0.0.3 omae-douyo -h

ImportError: cannot import name 'fetch_titles_as_bullet_list' from 'recent_state_summarizer.fetch' (/.../Library/Caches/uv/archive-v0/JLCv8R1dEcs_TdbO8agkQ/lib/python3.14/site-packages/recent_state_summarizer/fetch.py)
% uvx --from recent-state-summarizer==0.0.4 omae-douyo -h
usage: omae-douyo [-h] url

再発防止として、気づけるようにomae-douyoコマンドのテストを Claude Code に書いてもらいました。
openai SDKが v0 ではなく v1 だったら得たい学びがあるので手を動かしたのですが、v0 なら Claude Code にお任せでいいやという判断です。
openai v0 は(HTTPX ではなく)requests を使っていたので、responses でモックするコードを書き上げてもらっています。

ためてた月のふりかえりを書こうとして、コマンドを壊していたことに気づいた次第です。

今週のリリースは以上です

終わりに

今回は Claude Code の力を多く借りました。
修正自体は小さいものですが、再発防止を実装するところを Claude Code で加速してもらった感覚です。

来週は紗代子です!
https://lantis.jp/imas/ssr/