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 — gh-137840: Implement PEP 728 (closed and extra_items in typing.TypedDict) by angela-tarantula · Pull Request #137933 · python/cpython

Conversation

@angela-tarantula

@angela-tarantula angela-tarantula commented Aug 19, 2025 •
edited by bedevere-app Bot
Loading

Uh oh!

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

Copy link
Copy Markdown
Contributor

Features:

  1. New class parameters on TypedDict: closed and extra_items.
  2. Raise TypeError when both parameters are specified together.
  3. New introspection attributes __closed__ and __extra_items__.
  4. typing.NoExtraItems is now a C-level immortal singleton, co-located with NoDefault in typevarobject.c.
  5. Tests covering PEP 728 runtime behavior.
  6. Suggested new docstring for TypedDict.

Not included in this PR:

  • Full documentation update in Doc/library/typing.rst
  • Issue: Implement PEP 728: TypedDict with Typed Extra Items #137840

Sorry, something went wrong.

Uh oh!

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

edgarrmondragon and PIG208 reacted with rocket emoji
All reactions
  • 🚀 2 reactions
@bedevere-app bedevere-app Bot mentioned this pull request Aug 19, 2025
Implement PEP 728: TypedDict with Typed Extra Items #137840
Closed
@angela-tarantula
angela-tarantula force-pushed the fix-issue-137840 branch from cf0ac68 to c338766 Compare August 19, 2025 05:14
dimaqq
dimaqq reviewed Aug 19, 2025
View reviewed changes
Comment thread Lib/test/test_typing.py
TypeError,
"Cannot combine closed=True and extra_items"
):
class TD(TypedDict, closed=True, extra_items=range):

@dimaqq dimaqq Aug 19, 2025

Copy link
Copy Markdown
Contributor

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.

Is range the builtin here?
That's a bit strange.

Sorry, something went wrong.

Uh oh!

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

All reactions

@angela-tarantula angela-tarantula Aug 19, 2025 •
edited
Loading

Uh oh!

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

Copy link
Copy Markdown
Contributor 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 agree it looks a bit odd as an arbitrary extra_items value, but it's valid, since it just means extra keys must have values assignable to range. For context, this test is copied from typing_extensions, where it was used to cover the same case. We could edit it for readability, but I kind of like it as a reminder for maintainers that extra_items accepts any type, not just the obvious ones.

Sorry, something went wrong.

Uh oh!

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

All reactions

@dimaqq dimaqq Aug 19, 2025

Copy link
Copy Markdown
Contributor

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.

That could be valid for a regular test that validates stored values.

this however is a negative test, isn’t it?

Sorry, something went wrong.

Uh oh!

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

All reactions

@angela-tarantula angela-tarantula Aug 19, 2025

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

It's still valid/correct as a negative test. By "valid" do you mean "correct" or "readable?" Passing range into extra_items is valid, and passing closed=True at the same time should raise a runtime error.

Sorry, something went wrong.

Uh oh!

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

All reactions

@dimaqq dimaqq Aug 19, 2025 •
edited
Loading

Uh oh!

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

Copy link
Copy Markdown
Contributor

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.

Sorry I was being terse.

I'm trying to say that this test:

        class Child(Base, extra_items=int):
            a: str

        self.assertEqual(Child.__required_keys__, frozenset({'a'}))
        self.assertEqual(Child.__optional_keys__, frozenset({}))
        self.assertEqual(Child.__readonly_keys__, frozenset({}))
        self.assertEqual(Child.__mutable_keys__, frozenset({'a'}))
        self.assertEqual(Child.__annotations__, {"a": str})
        self.assertIs(Child.__extra_items__, int)
        self.assertIsNone(Child.__closed__)

Could have an extra_items=range counterpart.

That would make a solid test, both understandable and useful.

Meanwhile, the negative test, class TD(TypedDict, closed=True, extra_items=range): --> error would be better served with a simpler, more straightforward extra_items=int argument.

Sorry, something went wrong.

Uh oh!

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

All reactions

@dimaqq dimaqq Aug 19, 2025

Copy link
Copy Markdown
Contributor

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.

P.S. my comment overall is minor, please don't let me stop your work!

Sorry, something went wrong.

Uh oh!

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

All reactions

@angela-tarantula angela-tarantula Aug 20, 2025 •
edited
Loading

Uh oh!

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

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

Thanks for clarifying. I thought about it some more.

I don't think introducing an extra_items=range counterpart would actually widen the test coverage, since it wouldn't be exercising any new behavior. PEP 728 splits responsibilities between runtime and type checker behavior. While extra_items is only supposed to accept a valid type expression, validating that is the type checker's job (e.g. MyPy's valid-type error), not the runtime's. The runtime just stores whatever is passed in.

So the real subject under test is simply:

Child.__extra_items__ must correctly store the value passed into extra_items.

We don't need multiple values to prove that behavior.

And although range is a less obvious type, I think it makes sense in the negative test. That test is specifically asserting the error message “Cannot combine closed=True and extra_items”. Using range highlights that the failure comes from the combination, not from range itself being an invalid value of extra_items.

Sorry, something went wrong.

Uh oh!

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

All reactions

@JelleZijlstra JelleZijlstra Aug 20, 2025

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 tend to use range sometimes as it's a builtin type that isn't generic (like list) and doesn't participate in promotion weirdness (like float and historically bytes), so it's a good basic type to test with.

Plus, people who forget that range is a type get to learn something :)

Sorry, something went wrong.

Uh oh!

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

brianschubert, AlexWaygood, and angela-tarantula reacted with heart emoji
All reactions
  • ❤️ 3 reactions
angela-tarantula added 15 commits August 20, 2025 00:52
@angela-tarantula
Introduce NoExtraItems as a C-level singleton
cbea6f8
Implemented _Py_NoExtraItemsStruct and _Py_NoExtraItemsStruct
@angela-tarantula
expose NoExtraItems via _typing
4cde2f9
@angela-tarantula
Clearer comments in the C internals
06de7a9
@angela-tarantula
Import NoExtraItems into typing
407bf74
@angela-tarantula
Update _TypedDictMeta and TypedDict
f9755d3
They now both accept `closed` and `extra_items` as parameters.
For introspection, these arguments are also mapped to 2 new attributes:
`__closed__` and `__extra_items__`.
@angela-tarantula
fix typo
385812a
@angela-tarantula
Port over the relevant tests
bdbae49
@angela-tarantula
Updated docstring and error messages
e68c2ed
@angela-tarantula
NEWS entry
8203bc1
@angela-tarantula
New draft of docstring and error message
1b3ce52
@angela-tarantula
just use the original error message
2f5c42e
@angela-tarantula
fix typo
1bd3a6d
@angela-tarantula
undo capitalization
42a1aac
@angela-tarantula
fix typo x2
36ee4d2
@angela-tarantula
fix typo x3
503b825
@angela-tarantula
angela-tarantula force-pushed the fix-issue-137840 branch from c1395ca to e2297b6 Compare August 20, 2025 04:54
@angela-tarantula
Hush the c-analyzer: NoExtraItems is a singleton
1f23caa
@angela-tarantula
angela-tarantula force-pushed the fix-issue-137840 branch from e2297b6 to 1f23caa Compare August 20, 2025 05:05
@angela-tarantula
angela-tarantula marked this pull request as ready for review August 20, 2025 11:44
@angela-tarantula
angela-tarantula requested review from AlexWaygood, JelleZijlstra, ericsnowcurrently and markshannon as code owners August 20, 2025 11:44
@bedevere-app bedevere-app Bot added the awaiting review label Aug 20, 2025
@angela-tarantula

angela-tarantula commented Aug 20, 2025

Copy link
Copy Markdown
Contributor Author

Ready for review! @PIG208 @JelleZijlstra

All reactions

Sorry, something went wrong.

Uh oh!

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

JelleZijlstra
JelleZijlstra reviewed Aug 20, 2025
View reviewed changes
Comment thread Objects/typevarobject.c Outdated

PyObject _Py_NoDefaultStruct = _PyObject_HEAD_INIT(&_PyNoDefault_Type);

/* NoExtraItems: a marker object for TypedDict extra_items when it's unset. */

@JelleZijlstra JelleZijlstra Aug 20, 2025

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.

Is this implemented in C just for analogy with NoDefault? NoDefault is in C because TypeVar uses it and TypeVar has to be in C because it has native syntax, but TypedDict is all Python code, so I'd rather keep NoExtraItems also just Python.

Sorry, something went wrong.

Uh oh!

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

ZeroIntensity reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

@JelleZijlstra JelleZijlstra Aug 20, 2025

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.

Also if we get PEP 661 implemented in 3.15 (I hope we will), this can and should use it.

Sorry, something went wrong.

Uh oh!

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

AlexWaygood reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

@angela-tarantula angela-tarantula Aug 26, 2025

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

My bad, I thought we wanted it in C like NoDefault, but yeah since TypedDict is all Python we can keep it like that. I'll reverse course on this. PEP 661 looks interesting! Must being annoying to maintain so many ad-hoc sentinels.

Sorry, something went wrong.

Uh oh!

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

All reactions
ZeroIntensity
ZeroIntensity reviewed Aug 20, 2025
View reviewed changes
Comment thread Objects/typevarobject.c Resolved

Uh oh!

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

Comment thread Objects/typevarobject.c Resolved

Uh oh!

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

JelleZijlstra
JelleZijlstra reviewed Aug 20, 2025
View reviewed changes
Comment thread Lib/typing.py Resolved

Uh oh!

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

Comment thread Objects/typevarobject.c Outdated

PyObject _Py_NoDefaultStruct = _PyObject_HEAD_INIT(&_PyNoDefault_Type);

/* NoExtraItems: a marker object for TypedDict extra_items when it's unset. */

@JelleZijlstra JelleZijlstra Aug 20, 2025

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.

Also if we get PEP 661 implemented in 3.15 (I hope we will), this can and should use it.

Sorry, something went wrong.

Uh oh!

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

AlexWaygood reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
angela-tarantula and others added 3 commits August 25, 2025 21:21
@angela-tarantula
remove c-level NoExtraItems singleton
9a9c5c5
@angela-tarantula @JelleZijlstra
Update Lib/typing.py
18b929b
Co-authored-by: Jelle Zijlstra <[email protected]>
@angela-tarantula
define NoExtraItems singleton in Python
611b3e6
@angela-tarantula
angela-tarantula requested review from JelleZijlstra, ZeroIntensity and dimaqq August 26, 2025 03:03
@angela-tarantula
enforce singleton qualities on NoExtraItems
a6250f4
JelleZijlstra
JelleZijlstra approved these changes Aug 26, 2025
View reviewed changes
@bedevere-app bedevere-app Bot added awaiting merge and removed awaiting review labels Aug 26, 2025
@JelleZijlstra JelleZijlstra self-assigned this Aug 26, 2025
@JelleZijlstra
JelleZijlstra merged commit fd7dac0 into python:main Oct 3, 2025
@bedevere-app bedevere-app Bot removed the awaiting merge label Oct 3, 2025
@cdce8p cdce8p mentioned this pull request Oct 6, 2025
Fix PEP 728 TypedDict.__closed__ annotation in typing_extensions python/typeshed#14839
Merged
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@JelleZijlstra JelleZijlstra JelleZijlstra approved these changes
@AlexWaygood AlexWaygood Awaiting requested review from AlexWaygood AlexWaygood is a code owner
@ericsnowcurrently ericsnowcurrently Awaiting requested review from ericsnowcurrently
@markshannon markshannon Awaiting requested review from markshannon
@ZeroIntensity ZeroIntensity Awaiting requested review from ZeroIntensity
@dimaqq dimaqq Awaiting requested review from dimaqq

Uh oh!

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

Assignees

@JelleZijlstra JelleZijlstra

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

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

4 participants

@angela-tarantula @dimaqq @JelleZijlstra @ZeroIntensity
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.