Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

to wide source positions for exceptions during pattern matching (3.11.0rc2) #96999

Open
15r10nk opened this issue Sep 21, 2022 · 2 comments
Open
Assignees
Labels
type-bug An unexpected behavior, bug, or error

Comments

@15r10nk
Copy link

15r10nk commented Sep 21, 2022

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.

@15r10nk 15r10nk added the type-bug An unexpected behavior, bug, or error label Sep 21, 2022
@15r10nk
Copy link
Author

15r10nk commented Sep 21, 2022

I just saw that the arguments in the MatchClass ast-node have no node for the argument name with the value.

case A(a=5):
       ^^^ # there is no ast-node

This would make it really difficult for cpython and libraries to work with such ranges.

I don't know what the best solution might be.

@brandtbucher
Copy link
Member

brandtbucher commented Sep 21, 2022

Yeah, this could certainly be tightened up a bit. It definitely falls under the general theme of #93691, so not a super high priority at the moment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

2 participants