python / python/mypy

Type narrowing for Optional[T] fails inside an else branch inside a while cycle

オープン
#11,947 コメント 0 件 リアクション 1 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

bug topic-type-narrowing
主要言語
Python
スター
20.6k
フォーク
3.3k
平均マージ
1日 18時間
マージ済み PR(30日)
54

説明

Bug Report

from typing import Optional                                                      
                                                                                 
                                                                                 
class Node:                                                                      
    def __init__(self, value: int):                                      
        self.value = value                                                       
        self.next: Optional[Node] = None                                         
                                                                                 
                                                                                 
def foo(node: Node) -> None:                                                     
    while node.next is not None:                                                 
        if node.value != node.next.value:                                        
            node = node.next                                                     
        else:                                                                    
            node.next = node.next.next   # <-- HERE

Expected Behavior

No mypy errors. Mypy should know that node.next is not None in the else branch.

Actual Behavior

Mypy says error: Item "None" of "Optional[Node]" has no attribute "next" on the line marked by <-- HERE. (With column numbers turned on, the location is the beginning of node.next.next.)

Interestingly, everything is OK if the branches of if are reversed:

def foo(node: Node) -> None:                                                     
    while node.next is not None:                                                 
        if node.value == node.next.value:                                        
            node.next = node.next.next                                           
        else:                                                                    
            node = node.next                                                     

Writing assert node is not None before the problematic line also fixes the error (but seems quite redundant).

Your Environment

  • Mypy version used: mypy 0.930
  • Mypy command-line flags: none
  • Mypy configuration options from mypy.ini (and other config files): default, no config files
  • Python version used: Python 3.10.1
  • Operating system and version: Arch Linux

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

まず、issue の Optional[Node] の例に対して設定なしで mypy 0.930 を実行し、次に 2 つの分岐順序と assert 版を比較します。while 条件と代入の周辺での型の絞り込みを追跡します。元の例が冗長な assert なしでエラーを報告しなければ完了です。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
python
領域
compilers
issue の種類
バグ
難易度
3/5
見積もり時間
1〜2日
活発さ
停滞
明瞭さ
おおむね明確
初心者へのやさしさ
42/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。