GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — Unify and fix`Literal`/`Final` interaction by srittau · Pull Request #2353 · python/typing Conversation
Collaborator
I've decided to move the interaction section to the "qualifiers" chapters, Closes: #2351
Sorry, something went wrong. Uh oh!
There was an error while loading. Please reload this page. All reactions- Add an "Inference Rules" subsection to the `Final` section. - Move the inference rules for class vars there. - Remove contradictory paragraph about literal handling from `Final` section. - Merge the "Interactions with Final" section from literals into the new "Inference Rules" section, add a link, and trim slightly.
srittau
commented
Comment on lines
-162
to
-168
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This got moved to the "Inference Rules" section.
Sorry, something went wrong. Uh oh!
There was an error while loading. Please reload this page. All reactions
srittau
commented
Uh oh!
There was an error while loading. Please reload this page.
srittau
commented
Comment on lines
+198
to
+205
|
|
I just noticed that the introductory sentence about "normal inference" is actually important. I will change the PR when I get home. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
The spec used to say:
before I replaced that sentence with a link to the new inference rules section. The problem is that this contradicts the explicit guidance in the section I copied over from the literals spec:
My suggestion: We leave this original sentence out for now, reverting back to "left unspecified" for now. But I did plan to open a discuss thread anyway to define a few more supported cases, like |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I think combining these sections makes sense. Left some inline comments.
I think this is a clarification, not a substantive spec change, so I don't know that we need the full process including DPO post here. But it's a hefty enough rewording / rearrangement that I do think we should try to get at least most of the typing council to approve it.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Update conformance tests accordingly Restore the `NamedTuple` section
|
I've now completely rewritten the inference rules section (except the part finals in class bodies). I think I captured the intent of the various bits and pieces previously scattered around the specs. I'm using concrete language better suited for a spec than the previous "PEP" language that was more explanatory. Personally, I'd prefer the "explicit type" section to just read "Type checkers should use an explicit type if one is provided.", but that would be a direct contradiction to the previous example that type checkers may accept: bar1: Final[int] = 3 expects_three(bar1) # May or may not be accepted by type checkers I've gone with the old vague language for now, but I think there is very much room for improvement here in the future. I've also restored the Finally, I've reworked the conformance tests to follow the reworked section. I've introduced a new way to mark result groups as requiring at least one success (as pyrefly accepts both |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
| # > In all other cases, type checkers should use standard inference rules. | ||
|
|
||
| bare3: Final = 3.14 # infer bare3 as Final[float] or Final[float | int] | ||
| assert_type(bare3, float) # E[bare3!]: either float or float | int |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
These exact-type assertions reject checkers that are applying their normal inference, just as the spec requires.
We should just not use float as an example, because the float/int special case is not well specified, so we can't make very many assertions around it that are clearly supported by the spec.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Inferring Literal[3.14] is against the spec, which explicitly forbids float literals. That said, this is more of an issue that should be covered by the Literal test cases, so changing it to a list is the better option.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
| * In all other cases, type checkers should use standard inference rules. | ||
| For example:: | ||
|
|
||
| bare3: Final = 3.14 # infer bare3 as Final[float] or Final[float | int] |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I think we should stay away from floats here.
I don't actually think it's useful to even include examples for "type checkers should use standard inference rules", I would just leave them out entirely.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I think examples are still useful as a clarification, although two examples are not really necessary.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
| Type checkers should use the following inference rules for ``Final`` with an | ||
| explicit type: |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
| Type checkers should use the following inference rules for ``Final`` with an | |
| explicit type: | |
| Type checkers should use the following inference rules for ``Final[SomeType]`` annotations: |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
This also uses the exact wording of the syntax section, which I think should be used.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
As written, this conflicts with the requirements stated below, because x: Final[int] = 3 is still "a final name that was initialized with a literal", but we explicitly say below that type checkers aren't required to infer x as Literal[3] in that case. So I think we at the very least need an update to the text here to fix that inconsistency.
| Type checkers should treat uses of a name annotated with bare ``Final`` and initialized | |
| with a literal as if it was replaced by the literal. For example, the |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Maybe "... annotated with a Final where a Literal is inferred"? This would mean consistent handling between these cases, and we don't need to repeat the rules here directly.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| * If the value is a literal value which is a valid parameter for | ||
| ``Literal[...]``, type checkers should infer that ``Literal``. For example:: |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I think saying "is a literal value" here introduces some ambiguity that wasn't present in the previous wording. Are -4, +1, and Color.RED "literal values"? Unclear; none of them are represented in the AST as a literal value. But they are all valid Literal arguments.
Ideally those cases might also be covered in the tests, though arguably it's fine to leave these edge cases out of the Final tests if the Literal tests cover them, as long as the wording here is clear.
At the same time, we should take care not to imply that Alias = Literal[1]; x: Final = Alias invokes this rule, even though Literal[Alias] would be a valid type expression. The old wording was potentially subject to this mis-interpretation.
I think the clearest option here is probably just to outline the relevant cases explicitly:
| * If the value is a literal value which is a valid parameter for | |
| ``Literal[...]``, type checkers should infer that ``Literal``. For example:: | |
| * If ``value`` is an integer literal (optionally prefixed with unary ``+`` or ``-``), | |
| a string or bytes literal, ``True``, ``False``, ``None``, or an enum-member | |
| reference such as ``Color.RED``, type checkers should infer ``Literal[value]``. | |
| For example:: |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should repeat the literal rules here. If we just slightly reformulate this as "If the value would be a valid parameter for Literal[...], ..." it is unambiguous that -4, +1, and Color.RED are allowed.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I agree that would address the inclarity with -4, +1, and Color.RED. But as mentioned in my comment, Literal[1] is also a valid parameter for Literal[...], and so is Alias, where Alias = Literal[1]. But x: Final = Alias where Alias = Literal[1], and x: Final = Literal[1], do not trigger this rule. So "would be a valid parameter for Literal[...]" seems not sufficiently precise. That's why my suggestion here does not actually just repeat the literal rules, it specifies a subset of them which apply in this case.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
Finals and Literals: Spec contradiction
There was an error while loading. Please reload this page.