Skip to content

Refactor Sentinel to conform to PEP 661 #617

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 5 commits into
base: main
Choose a base branch
from

Conversation

HexDecimal
Copy link
Contributor

My attempt at implementing PEP 661 since I was unhappy with #594. I'm hoping that this is a PR of decent quality and not just me desperately wanting pickle support.

Changes made to Sentinel:

  • repr parameter removed, explicit repr tests removed, repr was rejected by the PEP
  • __repr__ modified to match PEP implementation (removed angle brackets)
  • Added module_name parameter following PEP implementation and tweaking that to use the local _caller helper function.
  • name required support for qualified names to follow PEP and ensure forward compatibility, this is tested. Unless sentinels in class scopes are forbidden then qualified names are not optional.
  • Added __reduce__ to track Sentinel by name and module_name.
  • Added a Sentinel registry to preserve Sentinel identity across multiple calls to the class. Added tests for this.
  • Added an import step to allow forward compatibility with other sentinel libraries. Import step is tested.
    This is not from the PEP but it is required for typing_extensions to be forward compatible with any official sentinel type.
  • Added copy and pickle tests. There were several other use-cases for pickle from the PEP 661 discussion. The PEP requires that the sentinel identity is preserved during these operations.
  • Updated documentation for Sentinel.

For a while I still supported the repr parameter and after following the PEP 661 discussion I ended up implementing the truthiness tweaks mentioned there, but then I ended up scrapping all of that so that I could follow the PEP more closely, but they would be easy to reintroduce later if desired.

If you don't like the current __reduce__ function relying on the class directly then I can modify it to rely on a private classmethod instead which will ensure that any later changes to __new__ or __reduce__ won't cause issues. That said it seems the name, module_name parameters are one of the few things most have agreed on.

The _import_sentinel staticmethod can be moved if desired.

I'm unsure of how to mention version changes in the docs. Replacing repr with module_name is technically a breaking change.

_sentinel_registry.setdefault is potentially supposed to be protected with a lock but I'm unsure of if I can import the threading module without causing platform issues.

_sentinel_registry could be replaced with a weak values dictionary but I'm unsure if that's necessary due to how permanent most sentinels are.

`repr` parameter removed, explicit repr tests removed

`__repr__` modified to match PEP implementation (removed angle brackets)

Added `module_name` parameter following PEP implementation and tweaking
to use `_caller` helper function.

`name` required support for qualified names to follow PEP implementation.

Added `__reduce__` to track Sentinel by name and module_name.

Added a Sentinel registry to preserve Sentinel identity across multiple
calls to the class. Added tests for this.

Added an import step to allow forward compatibility with other sentinel libraries.
Import step is tested.
This was not required by the PEP but it is required for
typing_extensions to have a forward compatible type.

Added copy and pickle tests.

Updated documentation for Sentinel.
@python-cla-bot
Copy link

python-cla-bot bot commented Jun 4, 2025

All commit authors signed the Contributor License Agreement.

CLA signed

@Viicos
Copy link
Contributor

Viicos commented Jun 5, 2025

Replacing repr with module_name is technically a breaking change.

I think breaking changes are expected for draft PEPs, so imo no deprecation process should be used. Users should be aware that changes are expected. However, I think we can:

  • Improve the documentation about draft PEPs, stating that implementation can change.
  • Explicitly state that Sentinels are from a draft PEP in the Sentinel class documentation, and isn't stable yet.

@Viicos
Copy link
Contributor

Viicos commented Jun 5, 2025

cc @taleinat

@JelleZijlstra
Copy link
Member

I'm not willing to remove the repr parameter from typing_extensions.Sentinel without a deprecation. However, I'm OK to re-export the builtin Sentinel class in 3.15 (if it makes it in), even if it doesn't have this parameter.

@HexDecimal
Copy link
Contributor Author

I'm not willing to remove the repr parameter from typing_extensions.Sentinel without a deprecation. However, I'm OK to re-export the builtin Sentinel class in 3.15 (if it makes it in), even if it doesn't have this parameter.

Then I will change repr into a keyword parameter.

If compatibility is important enough then I can also have the code assume module_name is actually repr if it does not refer to a existing module. Would this be necessary?

Configuration like repr would be defined only with the initial Sentinel object. It wouldn't get stored in the reduce function. This allows changes in repr to be reflected in unpickled sentinels. This means that removing repr later will not break serialization.

Adds tests for both the keyword and old positional repr parameters
Changes in Sentinel such as swapping it with a theoretical
typing.Sentinel will affect private methods, so the callable used with
`__reduce__` must be at the top-level to be stable.
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.

3 participants