Skip to content

Use the PEP 661 sentinel type where appropriate - #1190

Merged
agronholm merged 4 commits into
masterfrom
sentinels
Aug 17, 2026
Merged

Use the PEP 661 sentinel type where appropriate#1190
agronholm merged 4 commits into
masterfrom
sentinels

Conversation

@agronholm

Copy link
Copy Markdown
Owner

NOTE Erasing or replacing the contents of this template will result in your pull
request being summarily closed without consideration!

Changes

This changes our current sentinels to use the PEP 661 sentinel type instead of an anonymous object.

The only place where this could not be done is the typed_attribute() function, as it is typed to return Any.

This PR needs a newer mypy for type checks to pass, as it doesn't yet recognize typing_extensions.sentinel.

Checklist

If this is a user-facing code change, like a bugfix or a new feature, please ensure that
you've fulfilled the following conditions (where applicable):

  • You've added tests (in tests/) which would fail without your patch
  • You've updated the documentation (in docs/), in case of behavior changes or new
    features
  • You've added a new changelog entry (in docs/versionhistory.rst).

If this is a trivial change, like a typo fix or a code reformatting, then you can ignore
these instructions.

Updating the changelog

If there are no entries after the last release, use **UNRELEASED** as the version.
If, say, your patch fixes issue #123, the entry should look like this:

- Fix big bad boo-boo in task groups
  (`#123 <https://github.com/agronholm/anyio/issues/123>`_; PR by @yourgithubaccount)

If there's no issue linked, just link to your pull request instead by updating the
changelog after you've created the PR.

@agronholm agronholm added this to the 4.15 milestone Jul 22, 2026
@agronholm
agronholm marked this pull request as ready for review July 22, 2026 20:20
@tapetersen

Copy link
Copy Markdown
Collaborator

I know I saw at least one place a 1 member enum was used instead just for the debuggability reason. I'll see if I can find it again.

@tapetersen

Copy link
Copy Markdown
Collaborator

Found it (and it seems like the only one).

anyio/src/anyio/lowlevel.py

Lines 102 to 103 in b1f6e40

class _NoValueSet(enum.Enum):
NO_VALUE_SET = enum.auto()

@tapetersen

Copy link
Copy Markdown
Collaborator

Ok it uses it to have typing support in the code as well and until that is implemented it's probably not worth trying to change it.

I tried replacing it but mypy hasn't implemented it and with the flag to enable it in pyright they don't support the final semantics it seems yet (Sentinel vs., sentinel)

@agronholm

Copy link
Copy Markdown
Owner Author

Ok it uses it to have typing support in the code as well and until that is implemented it's probably not worth trying to change it.

I tried replacing it but mypy hasn't implemented it and with the flag to enable it in pyright they don't support the final semantics it seems yet (Sentinel vs., sentinel)

Tracking issue here.

@agronholm

Copy link
Copy Markdown
Owner Author

I think we could merge this as-is, and follow up with more when the tooling improves.

@EmmanuelNiyonshuti EmmanuelNiyonshuti left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Far as i can tell, this is good aside from waiting on mypy to ship the sentinel support. we can get the actual sentinel type in too (instead of object), once it does!

@agronholm

Copy link
Copy Markdown
Owner Author

Far as i can tell, this is good aside from waiting on mypy to ship the sentinel support. we can get the actual sentinel type in too (instead of object), once it does!

I expect us to switch to Zuban soon which already supports sentinels properly. As for the second part of your comment, I'm not sure what you're referring to.

@EmmanuelNiyonshuti

Copy link
Copy Markdown
Collaborator

Far as i can tell, this is good aside from waiting on mypy to ship the sentinel support. we can get the actual sentinel type in too (instead of object), once it does!

I expect us to switch to Zuban soon which already supports sentinels properly. As for the second part of your comment, I'm not sure what you're referring to.

I was trying to mean we'd follow up with the actual sentinel type:

diff --git a/src/anyio/_backends/_trio.py b/src/anyio/_backends/_trio.py
index 2e67786..b063d09 100644
--- a/src/anyio/_backends/_trio.py
+++ b/src/anyio/_backends/_trio.py
@@ -203,7 +203,7 @@ empty_start_value = sentinel("empty_start_value")
 
 
 class _TrioTaskStatus(abc.TaskStatus[T_contra], Generic[T_contra]):
-    early_start_value: T_contra | object = empty_start_value
+    early_start_value: T_contra | empty_start_value = empty_start_value
     real_task_status: trio.TaskStatus[T_contra | None] | None = None
 
     def started(self, value: T_contra | None = None) -> None:

Basically the follow-up to what I think you've already addressed that this PR needs a newer mypy for type checks to pass.

@agronholm
agronholm merged commit 3cc0f18 into master Aug 17, 2026
20 checks passed
@agronholm
agronholm deleted the sentinels branch August 17, 2026 17:19
@agronholm

Copy link
Copy Markdown
Owner Author

Thanks for the review!

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