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

Allow non-final __match_args__ and overriding #12415

Merged
merged 4 commits into from
Mar 22, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 9 additions & 8 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -1439,12 +1439,7 @@ def check_setattr_method(self, typ: Type, context: Context) -> None:
self.msg.invalid_signature_for_special_method(typ, context, '__setattr__')

def check_match_args(self, var: Var, typ: Type, context: Context) -> None:
"""Check that __match_args__ is final and contains literal strings"""

if not var.is_final:
self.note("__match_args__ must be final for checking of match statements to work",
context, code=codes.LITERAL_REQ)

"""Check that __match_args__ contains literal strings"""
typ = get_proper_type(typ)
if not isinstance(typ, TupleType) or \
not all([is_string_literal(item) for item in typ.items]):
Expand Down Expand Up @@ -2276,11 +2271,16 @@ def check_assignment(self, lvalue: Lvalue, rvalue: Expression, infer_lvalue_type

# Defer PartialType's super type checking.
if (isinstance(lvalue, RefExpr) and
not (isinstance(lvalue_type, PartialType) and lvalue_type.type is None)):
not (isinstance(lvalue_type, PartialType) and
lvalue_type.type is None) and
not (isinstance(lvalue, NameExpr) and lvalue.name == '__match_args__')):
if self.check_compatibility_all_supers(lvalue, lvalue_type, rvalue):
# We hit an error on this line; don't check for any others
return

if isinstance(lvalue, MemberExpr) and lvalue.name == '__match_args__':
self.fail(message_registry.CANNOT_MODIFY_MATCH_ARGS, lvalue)

if lvalue_type:
if isinstance(lvalue_type, PartialType) and lvalue_type.type is None:
# Try to infer a proper type for a variable with a partial None type.
Expand Down Expand Up @@ -2377,7 +2377,8 @@ def check_assignment(self, lvalue: Lvalue, rvalue: Expression, infer_lvalue_type

if inferred:
rvalue_type = self.expr_checker.accept(rvalue)
if not inferred.is_final:
if not (inferred.is_final or (isinstance(lvalue, NameExpr) and
lvalue.name == '__match_args__')):
rvalue_type = remove_instance_last_known_values(rvalue_type)
self.infer_variable_type(inferred, lvalue, rvalue_type, rvalue)
self.check_assignment_to_slots(lvalue)
Expand Down
1 change: 1 addition & 0 deletions mypy/message_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,3 +247,4 @@ def format(self, *args: object, **kwargs: object) -> "ErrorMessage":
CLASS_PATTERN_DUPLICATE_KEYWORD_PATTERN: Final = 'Duplicate keyword pattern "{}"'
CLASS_PATTERN_UNKNOWN_KEYWORD: Final = 'Class "{}" has no attribute "{}"'
MULTIPLE_ASSIGNMENTS_IN_PATTERN: Final = 'Multiple assignments to name "{}" in pattern'
CANNOT_MODIFY_MATCH_ARGS: Final = 'Cannot assign to "__match_args__"'
23 changes: 20 additions & 3 deletions test-data/unit/check-python310.test
Original file line number Diff line number Diff line change
Expand Up @@ -881,16 +881,16 @@ reveal_type(z) # N: Revealed type is "builtins.int*"

[case testMatchNonFinalMatchArgs]
class A:
__match_args__ = ("a", "b") # N: __match_args__ must be final for checking of match statements to work
__match_args__ = ("a", "b")
a: str
b: int

m: object

match m:
case A(i, j):
reveal_type(i) # N: Revealed type is "Any"
reveal_type(j) # N: Revealed type is "Any"
reveal_type(i) # N: Revealed type is "builtins.str"
reveal_type(j) # N: Revealed type is "builtins.int"
[builtins fixtures/tuple.pyi]

[case testMatchAnyTupleMatchArgs]
Expand Down Expand Up @@ -1528,3 +1528,20 @@ class A:
class B:
def __enter__(self) -> B: ...
def __exit__(self, x, y, z) -> None: ...

[case testOverrideMatchArgs]
class AST:
__match_args__ = ()

class stmt(AST): ...

class AnnAssign(stmt):
__match_args__ = ('target', 'annotation', 'value', 'simple')

reveal_type(AST.__match_args__) # N: Revealed type is "Tuple[]"
reveal_type(stmt.__match_args__) # N: Revealed type is "Tuple[]"
reveal_type(AnnAssign.__match_args__) # N: Revealed type is "Tuple[builtins.str, builtins.str, builtins.str, builtins.str]"

JelleZijlstra marked this conversation as resolved.
Show resolved Hide resolved
AnnAssign.__match_args__ = ('a', 'b', 'c', 'd') # E: Cannot assign to "__match_args__"
__match_args__ = 0
[builtins fixtures/tuple.pyi]