Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 5 additions & 9 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -4432,7 +4432,7 @@ def infer_variable_type(
# partial type which will be made more specific later. A partial type
# gets generated in assignment like 'x = []' where item type is not known.
if name.name != "_" and not self.infer_partial_type(name, lvalue, init_type):
self.msg.need_annotation_for_var(name, context, self.options.python_version)
self.msg.need_annotation_for_var(name, context, self.options)
self.set_inference_error_fallback_type(name, lvalue, init_type)
elif (
isinstance(lvalue, MemberExpr)
Expand All @@ -4442,7 +4442,7 @@ def infer_variable_type(
and not is_same_type(self.inferred_attribute_types[lvalue.def_var], init_type)
):
# Multiple, inconsistent types inferred for an attribute.
self.msg.need_annotation_for_var(name, context, self.options.python_version)
self.msg.need_annotation_for_var(name, context, self.options)
name.type = AnyType(TypeOfAny.from_error)
else:
# Infer type of the target.
Expand Down Expand Up @@ -4639,9 +4639,7 @@ def check_simple_assignment(
rvalue, type_context=lvalue_type, always_allow_any=always_allow_any
)
if not is_valid_inferred_type(rvalue_type, self.options) and inferred is not None:
self.msg.need_annotation_for_var(
inferred, context, self.options.python_version
)
self.msg.need_annotation_for_var(inferred, context, self.options)
rvalue_type = rvalue_type.accept(SetNothingToAny())

if (
Expand Down Expand Up @@ -7663,7 +7661,7 @@ def enter_partial_types(
var.type = NoneType()
else:
if var not in self.partial_reported and not permissive:
self.msg.need_annotation_for_var(var, context, self.options.python_version)
self.msg.need_annotation_for_var(var, context, self.options)
self.partial_reported.add(var)
if var.type:
fixed = fixup_partial_type(var.type)
Expand All @@ -7690,9 +7688,7 @@ def handle_partial_var_type(
if in_scope:
context = partial_types[node]
if is_local or not self.options.allow_untyped_globals:
self.msg.need_annotation_for_var(
node, context, self.options.python_version
)
self.msg.need_annotation_for_var(node, context, self.options)
self.partial_reported.add(node)
else:
# Defer the node -- we might get a better type in the outer scope
Expand Down
7 changes: 3 additions & 4 deletions mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -1803,18 +1803,17 @@ def unimported_type_becomes_any(self, prefix: str, typ: Type, ctx: Context) -> N
)

def need_annotation_for_var(
self, node: SymbolNode, context: Context, python_version: tuple[int, int] | None = None
self, node: SymbolNode, context: Context, options: Options | None = None
) -> None:
hint = ""
pep604_supported = not python_version or python_version >= (3, 10)
# type to recommend the user adds
recommended_type = None
# Only gives hint if it's a variable declaration and the partial type is a builtin type
if python_version and isinstance(node, Var) and isinstance(node.type, PartialType):
if options and isinstance(node, Var) and isinstance(node.type, PartialType):
type_dec = "<type>"
if not node.type.type:
# partial None
if pep604_supported:
if options.use_or_syntax():
recommended_type = f"{type_dec} | None"
else:
recommended_type = f"Optional[{type_dec}]"
Expand Down
3 changes: 2 additions & 1 deletion mypy/suggestions.py
Original file line number Diff line number Diff line change
Expand Up @@ -890,7 +890,8 @@ def visit_typeddict_type(self, t: TypedDictType) -> str:

def visit_union_type(self, t: UnionType) -> str:
if len(t.items) == 2 and is_overlapping_none(t):
return f"Optional[{remove_optional(t).accept(self)}]"
s = remove_optional(t).accept(self)
return f"{s} | None" if self.options.use_or_syntax() else f"Optional[{s}]"
else:
return super().visit_union_type(t)

Expand Down
2 changes: 1 addition & 1 deletion mypy/test/testcheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ def run_case_once(
options.hide_error_codes = False
if "abstract" not in testcase.file:
options.allow_empty_bodies = not testcase.name.endswith("_no_empty")
if "union-error" not in testcase.file:
if "union-error" not in testcase.file and "Pep604" not in testcase.name:
options.force_union_syntax = True
Comment on lines -143 to 144
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Special case for testSuggestPep604AnnotationForPartialNone. This will be removed once all tests are updated to just use the the PEP 604 syntax.


if incremental_step and options.incremental:
Expand Down
2 changes: 1 addition & 1 deletion test-data/unit/check-inference.test
Original file line number Diff line number Diff line change
Expand Up @@ -3548,7 +3548,7 @@ if x:
[builtins fixtures/dict.pyi]

[case testSuggestPep604AnnotationForPartialNone]
# flags: --local-partial-types --python-version 3.10
# flags: --local-partial-types --python-version 3.10 --no-force-union-syntax
x = None # E: Need type annotation for "x" (hint: "x: <type> | None = ...")

[case testTupleContextFromIterable]
Expand Down