Skip to content

Adapt Sentinel implementation to accepted version of PEP 661 #742

Closed

Description

@JelleZijlstra

PEP-661 was accepted, but it is implemented a little differently than our typing_extensions.Sentinel. Let's see how we can make the typing-extensions version better match the version that will be in Python 3.15, while keeping backward compatibility for users who may already be using typing_extensions.Sentinel.

Here are the relevant differences:

  • We have typing_extensions.Sentinel; 3.15 will have builtins.sentinel (lowercase). Let's not worry about the name. On Python 3.15 we'll re-export builtins.sentinel as typing_extensions.Sentinel.
  • Our Sentinel is subclassable; builtins.sentinel will not be. Let's add an __init_subclass__ that emits a DeprecationWarning.
  • We added an extra repr argument to control the repr. Let's emit a DeprecationWarning if it is passed.
  • The default repr is different: <name> for us, just name for the builtin. Let's just change this.
  • We made our Sentinel not picklable; the builtin is picklable by name. So let's add a __module__ attribute inferred from the caller (can reuse _set_module) and a __reduce__ that just returns self.__name__.
  • The builtin doesn't support weakrefs; our version does because it's in Python. I don't know of a way to deprecate our support, so let's just document this difference.
  • The builtin exposes a __name__ attribute; we have _name. Again let's just change this.
  • There are some observable changes from the builtin being in C (e.g., the type is immutable), but let's not worry about that.

There are three open PRs about changing Sentinel. #715 and #732 make changes that introduce further incompatibilities, so I'm going to close those. #617 goes in the right direction but also introduces some further incompatible changes.

Activity

  1. JelleZijlstra commented on Apr 24, 2026

    @JelleZijlstra
    MemberAuthor

    Some more things:

    • builtins.sentinel will raise TypeError on a non-string name; we accept it. Let's also raise DeprecationWarning for this.
    • The name argument to builtins.sentinel will be positional only, but we currently accept it as a keyword arg. I think we can also DeprecationWarning this, though we'll have to complicate the signature a bit.
    • builtins.sentinel has __copy__ returning itself, we don't.
    • The current implementation also allows setting arbitrary instance attributes. See if we can DeprecationWarning this (in __setattr__ maybe?).
  2. HexDecimal commented on Apr 24, 2026

    @HexDecimal
    Contributor

    Renaming and deprecating a class name is difficult. I've made an attempt but I'm unsure if a perfect solution is possible.

    builtins.sentinel has __copy__ returning itself, we don't.

    When __reduce__ is used for singletons then copies will automatically reuse the objects identity. I've included tests for this.

    builtins.sentinel will raise TypeError on a non-string name; we accept it.

    The parameter is name: str which means we already don't accept non-strings even if they might pass at runtime. The purpose of type checking is to resolve these issues before they occur at runtime.

  3. JelleZijlstra commented on Apr 25, 2026

    @JelleZijlstra
    MemberAuthor

    We don't need to deprecate the class itself, just some particular behaviors. In the future we can indefinitely keep re-exporting builtins.sentinel as typing_extensions.Sentinel.

    Thanks for working on this!

  4. JelleZijlstra commented on Apr 25, 2026

    @JelleZijlstra
    MemberAuthor

    @HexDecimal argues on the PR (https://github.com/python/typing_extensions/pull/617/changes#r3141102069) that we should rename to typing_extensions.sentinel:

    Using the correct name is important. Switching from typing_extensions to native Python and backporting to older versions should be a simple addition or removal of from typing_extensions import sentinel. Using Sentinel with the wrong case needs to be actively discouraged or else things will get worse for code migration.

    Curious what others think.

  5. HexDecimal commented on Apr 25, 2026

    @HexDecimal
    Contributor

    I can understand keeping Sentinel available because people would've used it by this point and thus it has to stick around forever. Though I'd prefer if typeshed deprecated it in favor of sentinel, linters will probably start doing that by themselves.

    What I really wonder is if anybody actually made weakrefs to it or added custom attributes. Otherwise I could've added a simple __slots__ to the implementation.

  6. JelleZijlstra commented on Apr 25, 2026

    @JelleZijlstra
    MemberAuthor

    typing-extensions is very widely used and backwards compatibility is important, so we generally have to assume that people have done all of those things.

    For what it's worth, when I was implementing PEP-695 in Python, I initially made it so you could no longer assign custom attributes to TypeVar instances, and it broke some things. Similarly, we got bug reports when I accidentally made TypeVars (and I think also unions?) not weakrefable.

  7. tmke8 commented on Apr 30, 2026

    @tmke8

    What about keeping the old Sentinel as it is (and deprecating it) and adding a new sentinel that's as close as possible to the one in Python 3.15?

  8. JelleZijlstra commented on May 1, 2026

    @JelleZijlstra
    MemberAuthor

    I think that would be confusing. Note that the existing class will continue to behave the same for pretty much any behavior, except some operations may raise DeprecationWarning.

  9. zedzhen commented on May 19, 2026

    @zedzhen
    Contributor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions