GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Unify and fix`Literal`/`Final` interaction by srittau · Pull Request #2353 · python/typing

Conversation

@srittau

@srittau srittau commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator
  • 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.

I've decided to move the interaction section to the "qualifiers" chapters,
since Literal seems much closer linked to Final than vice versa.

Closes: #2351

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
srittau added 6 commits August 31, 2026 17:08
@srittau
Add tests for Final/Literal interaction
d52de49
@srittau
Fix type assertions
4276314
@srittau
Reshuffle, fix, comment
b214de4
@srittau
Finagle tests more
c61cb1c
@srittau
Update results
d42d2fd
@srittau
Unify and fixLiteral/Final interaction
ff6cf96
- 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 srittau added the topic: typing spec For improving the typing spec label Aug 31, 2026
srittau
srittau commented Aug 31, 2026
View reviewed changes
Comment thread docs/spec/qualifiers.rst
Comment on lines -162 to -168
Type checkers should infer a final attribute that is initialized in a class
body as being a class variable, except in the case of :doc:`dataclasses`, where
``x: Final[int] = 3`` creates a dataclass field and instance-level final
attribute ``x`` with default value ``3``; ``x: ClassVar[Final[int]] = 3`` is
necessary to create a final class variable with value ``3``. In
non-dataclasses, combining ``ClassVar`` and ``Final`` is redundant, and type
checkers may choose to warn or error on the redundancy.

@srittau srittau Aug 31, 2026

Copy link
Copy Markdown
Collaborator Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The 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
srittau commented Aug 31, 2026
View reviewed changes
Comment thread docs/spec/qualifiers.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

srittau
srittau commented Aug 31, 2026
View reviewed changes
Comment thread docs/spec/qualifiers.rst Outdated
Comment on lines +198 to +205
In the example below, we know that ``foo`` will always be equal to
exactly ``3``. A type checker can use this information to deduce that ``foo``
is valid to use in any context that expects a ``Literal[3]``::

def expects_three(x: Literal[3]) -> None: ...

foo: Final = 3
expects_three(foo) # Type checks, since 'foo' is Final and equal to 3

@srittau srittau Aug 31, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Collaborator Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

The introductory part was reshuffled a bit from the original section to be a better fit here. The rest is identical.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
srittau added 3 commits August 31, 2026 19:05
@srittau
Update a link
e8ec2bf
@srittau
One case is optional per the spec, add another case
5b3a1bb
@srittau
Update results
ae58134
@carljm carljm mentioned this pull request Aug 31, 2026
Add tests for Final/Literal interaction #2352
Closed
@srittau

srittau commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator Author

I just noticed that the introductory sentence about "normal inference" is actually important. I will change the PR when I get home.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

srittau added 3 commits August 31, 2026 20:30
@srittau
Merge remote-tracking branch 'origin/conformance-final' into final-li…
f56623b
…teral
@srittau
Update spec link
0872a87
@srittau
Move tests from literals_interactions to qualifiers
9ae5733
@srittau

srittau commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator Author

The spec used to say:

The typechecker should apply its usual type inference mechanisms to determine the type of ID (here, likely, int)"

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:

Type checkers are not obligated to understand any other uses of Final. For
example, whether or not the following program type checks is left unspecified: [...]

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 X: Final = cls(), Y: Final = 3.2, and remove the ambiguity surrounding Z: Final[int] = 3.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@srittau
Re-run conformance tests
d764ec3
carljm
carljm reviewed Sep 1, 2026
View reviewed changes

@carljm carljm left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for 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.

Uh oh!

There was an error while loading. Please reload this page.

srittau reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
Comment thread docs/spec/qualifiers.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread conformance/tests/qualifiers_final_annotation.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread docs/spec/qualifiers.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread docs/spec/qualifiers.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

JelleZijlstra
JelleZijlstra reviewed Sep 1, 2026
View reviewed changes
Comment thread docs/spec/qualifiers.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread conformance/tests/qualifiers_final_annotation.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

@srittau
Rework Inference Rules section
eba639e
Update conformance tests accordingly

Restore the `NamedTuple` section
@srittau

srittau commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator Author

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 NamedTuple section in a dedicated "Interaction with NamedTuple" section and reworded it to apply to NamedTuples only for now, since the previous language was extremely vague. Please let me know if there are cases missing that should be added.

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 assert_type(X, float) and assert_type(X, float | int). I will extract that into a separate PR. The reworked conformance tests reveal a few instances of type checkers being more liberal in what type of literals they support (especially Literal[3.14], but pycroscope also accepts Literal[range(0, 3)]. I'm not sure what the best approach here is.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@srittau
Fix key names
7e6e1c3
@srittau srittau mentioned this pull request Sep 1, 2026
New conformance tag '# E[tag!]' to require at least one success #2354
Merged
@srittau
Remove unnecessary function
eb3b496
davidhalter
davidhalter reviewed Sep 1, 2026
View reviewed changes
Comment thread docs/spec/qualifiers.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

carljm
carljm reviewed Sep 1, 2026
View reviewed changes
Comment thread conformance/tests/qualifiers_final_annotation.py Outdated
# > 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

@carljm carljm Sep 1, 2026

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for 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.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@srittau srittau Sep 2, 2026

Copy link
Copy Markdown
Collaborator Author

There was a problem hiding this comment.

Choose a reason for 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.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread docs/spec/qualifiers.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread docs/spec/qualifiers.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread conformance/src/main.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread docs/spec/qualifiers.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread docs/spec/qualifiers.rst Outdated
* 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]

@carljm carljm Sep 1, 2026

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for 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.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@srittau srittau Sep 2, 2026

Copy link
Copy Markdown
Collaborator Author

There was a problem hiding this comment.

Choose a reason for 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.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread docs/spec/qualifiers.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread docs/spec/qualifiers.rst
Comment on lines +226 to +227
Type checkers should use the following inference rules for ``Final`` with an
explicit type:

@carljm carljm Sep 1, 2026

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Suggested change
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.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@srittau srittau Sep 2, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Collaborator Author

There was a problem hiding this comment.

Choose a reason for 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.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread docs/spec/qualifiers.rst Outdated
Comment on lines 187 to 188

@carljm carljm Sep 1, 2026

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for 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.

Suggested change
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.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@srittau srittau Sep 2, 2026

Copy link
Copy Markdown
Collaborator Author

There was a problem hiding this comment.

Choose a reason for 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.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread docs/spec/qualifiers.rst Outdated
Comment on lines +209 to +210
* If the value is a literal value which is a valid parameter for
``Literal[...]``, type checkers should infer that ``Literal``. For example::

@carljm carljm Sep 1, 2026

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for 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:

Suggested change
* 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.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@srittau srittau Sep 2, 2026

Copy link
Copy Markdown
Collaborator Author

There was a problem hiding this comment.

Choose a reason for 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.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@carljm carljm Sep 2, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for 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.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@srittau
Revert changes to conformance checker
7f5fad6
@srittau srittau mentioned this pull request Sep 2, 2026
Spec: Final section could use better definition for Final vs. Final[X] #2356
Closed
@srittau
Apply review suggestions
ab0e98a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@carljm carljm carljm left review comments
@davidhalter davidhalter davidhalter left review comments
@JelleZijlstra JelleZijlstra JelleZijlstra left review comments

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

topic: typing spec For improving the typing spec

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Finals and Literals: Spec contradiction

Uh oh!

There was an error while loading. Please reload this page.

4 participants

@srittau @carljm @davidhalter @JelleZijlstra
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.