python / python/cpython

Narrow source positions for exceptions during pattern matching

オープン
#96,999 コメント 4 件 リアクション 0 件 担当者 2 名 GitHub で見る

@iritkatriel がすでに取り組んでいます。

2022年9月30日 から。

interpreter-core type-feature
主要言語
Python
スター
77.2k
フォーク
36k
PR マージ指標
PR 指標を取得中

説明

The source range in the traceback is to wide in some cases, if an exception is thrown during matching of a pattern.

The Ranges

example for attribute lookup:

class A:
    def __getattr__(self, name):
        assert name[0] == "a"
        return 5


match A():
    case A(apple=5, banana=7):
        print("hi")

output (Python 3.11.0rc2+):

Traceback (most recent call last):
  File "/home/frank/projects/cpython/match_test.py", line 8, in <module>
    case A(apple=5, banana=7):
         ^^^^^^^^^^^^^^^^^^^^
  File "/home/frank/projects/cpython/match_test.py", line 3, in __getattr__
    assert name[0] == "a"
AssertionError

Annotating only the source positions where the attribute check failed (banana=7) would make the traceback easier to read.

The same problem exists for positional matching:

class A:
    __match_args__ = ("apple", "banana")

    def __getattr__(self, name):
        assert name == "apple"


match A():
    case A(5, 7):
        print("hi")

output (Python 3.11.0rc2+):

Traceback (most recent call last):
  File "/home/frank/projects/cpython/match_test2.py", line 9, in <module>
    case A(5, 7):
         ^^^^^^^
  File "/home/frank/projects/cpython/match_test2.py", line 5, in __getattr__
    assert name == "apple"
AssertionError

Equality checks are handled better:

class NeverEqual:
    def __eq__(self, other):
        assert other==5


class A:
    def __init__(self):
        self.a = NeverEqual()
        self.b = 3


match A():
    case A(a=5|3, b=3):
        print("hi")

output (Python 3.11.0rc2+):

Traceback (most recent call last):
  File "/home/frank/projects/cpython/match_test3.py", line 13, in <module>
    case A(a=5|3, b=3):
               ^
  File "/home/frank/projects/cpython/match_test3.py", line 3, in __eq__
    assert other==5
AssertionError

Instance checks are represented in the same way:

class MetaB(type):
    def __instancecheck__(self, instance):
        assert False, "do not use me"


class B(metaclass=MetaB):
    pass


class A:
    def __init__(self):
        self.a = 5
        self.b = 3


match A():
    case A(a=5, b=2|B()):
        print("hi")

output (Python 3.11.0rc2+):

Traceback (most recent call last):
  File "/home/frank/projects/cpython/match_test4.py", line 17, in <module>
    case A(a=5, b=2|B()):
                    ^^^
  File "/home/frank/projects/cpython/match_test4.py", line 3, in __instancecheck__
    assert False, "do not use me"
AssertionError: do not use me

Problem for the User

Consistent source ranges would improve the readability of the tracebacks.

Is it possible to highlight always only the failing Pattern (with attribute name if given) for failing attribute access?

example:

case A(a=5):
       ^^^
case A(a=B()):
       ^^^^^
case A(5):
       ^
case A(5|B()):
       ^^^^^

Instance checks and equality tests are already handled well:

case A(a=5):
         ^
case A(a=B()):
         ^^^
case A(5):
       ^
case A(B()):
       ^^^
case A(5|B())
         ^^^

Problem for libraries

Libraries are using the source ranges for all sorts of functionality.
Executing for example is using this ranges (for the new 3.11 support) to map from instructions back to ast-nodes. Useful ranges are here required to map to useful ast-nodes.

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

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

はじめの一歩

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

評価

この issue はまだ評価されていません。

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

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