Sitelet https://web.archive.org/web/20250709023922/https://github.com/python/mypy/pull/11340
Skip to content

Infer literal types in analyze_var_ref #11340

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

Open
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

97littleleaf11
Copy link
Collaborator

@97littleleaf11 97littleleaf11 commented Oct 15, 2021 •

Description

Partially fixes #11336.

This PR adds inferring for final types when analyzing vars.

The last_known_value of annotation types are set with None currently, which would bring a lot of false positive when comparing Instance. I have to silence it temporally. We should check all the cases of last_known_value initialization and then remove the comments in the following PRs.

Test Plan

  • Adds testFormatCallMatchingFinal
  • Fixes testFormatCallFinalChar testLiteralFineGrainedChainedViaFinal and testLiteralFinalDirectInstanceTypesSupercedeInferredLiteral, which were wrong previously.

mypy/types.py Outdated
@@ -52,7 +52,7 @@
# Note: Although "Literal[None]" is a valid type, we internally always convert
# such a type directly into "None". So, "None" is not a valid parameter of
# LiteralType and is omitted from this list.
LiteralValue: _TypeAlias = Union[int, str, bool]
LiteralValue: _TypeAlias = Union[int, str, float, bool]
Copy link
Contributor

Choose a reason for hiding this comment

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

According to PEP 586, which describes Literal, floats are currently not allowed as Literals:

The following are provisionally disallowed for simplicity. We can consider allowing them in future extensions of this PEP.

Floats: e.g. Literal[3.14]. Representing Literals of infinity or NaN in a clean way is tricky; real-world APIs are unlikely to vary their behavior based on a float parameter.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks for pointing out!

if var.name in {'True', 'False'}:
return self.infer_literal_expr_type(var.name == 'True', 'builtins.bool')
if isinstance(var.final_value, int) or isinstance(var.final_value, str):
Copy link
Member

Choose a reason for hiding this comment

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

What about float and bytes?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Literal[float] is illegal. And we actually treat bytes as str in mypy for convenience.

and self.last_known_value == other.last_known_value)
and self.args == other.args)
# TODO: last_known_value is not set correctly at initializations of Literal type
# and self.last_known_value == other.last_known_value)
Copy link
Collaborator

Choose a reason for hiding this comment

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

This change is not safe, since now two Instance objects that compare equal can have different hash values.

Since these support hashing, it seems necessary to also use last_known_value when comparing for equality. Otherwise all sorts of weird (and hard-to-debug) things could happen.

I think that fixing the value of last_known_value could be the way to go. Alternatively, it may be feasible to replace == with comparing type and args attributes in selected contexts only (e.g. replace i1 == i2 with i1.type == i2.type and i1.args == i2.args), but I'm not sure about this.

@@ -2088,7 +2091,8 @@ def analyze_external_member_access(self, member: str, base_type: Type,
def is_literal_context(self) -> bool:
return is_literal_type_like(self.type_context[-1])

def infer_literal_expr_type(self, value: LiteralValue, fallback_name: str) -> Type:
def infer_literal_expr_type(self, value: LiteralValue, fallback_name: str,
fallback_typ: Optional[Instance] = None) -> Type:
Copy link
Collaborator

Choose a reason for hiding this comment

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

This signature is now a bit awkward. I think that it would be better to always only take an Instance, and some callers can be updated to use self.named_type.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Mypy does not detect redundant keyword arguments in 'str.format()'
4 participants