-
Notifications
You must be signed in to change notification settings - Fork 492
Add Sentinel support (PEP-661) #3575
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
Changes from 8 commits
20f0070
2553533
d1ce26f
4847feb
153d58a
b66942c
26aad50
f766c28
bcce9c0
8418fca
aad9898
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| /* | ||
| * Copyright (c) Meta Platforms, Inc. and affiliates. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE file in the root directory of this source tree. | ||
| */ | ||
|
|
||
| use std::fmt; | ||
| use std::fmt::Display; | ||
| use std::hash::Hash; | ||
|
|
||
| use dupe::Dupe; | ||
| use pyrefly_derive::TypeEq; | ||
| use pyrefly_python::module::Module; | ||
| use pyrefly_python::nesting_context::NestingContext; | ||
| use pyrefly_python::qname::QName; | ||
| use pyrefly_util::arc_id::ArcId; | ||
| use pyrefly_util::visit::Visit; | ||
| use pyrefly_util::visit::VisitMut; | ||
| use ruff_python_ast::Identifier; | ||
|
|
||
| use crate::equality::TypeEq; | ||
| use crate::equality::TypeEqCtx; | ||
| use crate::heap::TypeHeap; | ||
| use crate::types::Type; | ||
|
|
||
| #[derive(Clone, Copy, Dupe, Debug, PartialEq, Eq, Hash, Ord, PartialOrd, TypeEq)] | ||
| pub enum SentinelKind { | ||
| Builtins, | ||
| TypingExtensions, | ||
| } | ||
|
|
||
| impl SentinelKind { | ||
| pub fn name(&self) -> &str { | ||
| match self { | ||
| Self::Builtins => "sentinel", | ||
| Self::TypingExtensions => "Sentinel", | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// Used to represent Sentinel calls. Each Sentinel is unique, so use the ArcId to separate them. | ||
| #[derive(Clone, Dupe, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)] | ||
| pub struct Sentinel(ArcId<SentinelInner>); | ||
|
|
||
| // This is a lie, we do have types in the bound position | ||
| impl Visit<Type> for Sentinel { | ||
| const RECURSE_CONTAINS: bool = false; | ||
| fn recurse<'a>(&'a self, _: &mut dyn FnMut(&'a Type)) {} | ||
| } | ||
|
|
||
| impl VisitMut<Type> for Sentinel { | ||
| const RECURSE_CONTAINS: bool = false; | ||
| fn recurse_mut(&mut self, _: &mut dyn FnMut(&mut Type)) {} | ||
| } | ||
|
|
||
| impl Display for Sentinel { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| write!(f, "{}", self.0.qname.id()) | ||
| } | ||
| } | ||
|
|
||
| #[derive(Debug, PartialEq, TypeEq, Eq, Ord, PartialOrd)] | ||
| struct SentinelInner { | ||
| qname: QName, | ||
| kind: SentinelKind, | ||
| } | ||
|
|
||
| impl Sentinel { | ||
| pub fn new(name: Identifier, module: Module, kind: SentinelKind) -> Self { | ||
| Self(ArcId::new(SentinelInner { | ||
| kind, | ||
| // TODO: properly take parent from caller of new() | ||
| qname: QName::new(name, NestingContext::toplevel(), module), | ||
| })) | ||
| } | ||
|
|
||
| pub fn kind(&self) -> SentinelKind { | ||
| self.0.kind | ||
| } | ||
|
|
||
| pub fn qname(&self) -> &QName { | ||
| &self.0.qname | ||
| } | ||
|
|
||
| pub fn to_type(&self, heap: &TypeHeap) -> Type { | ||
| heap.mk_sentinel(self.dupe()) | ||
| } | ||
|
|
||
| pub fn type_eq_inner(&self, other: &Self, ctx: &mut TypeEqCtx) -> bool { | ||
| self.0.type_eq(&other.0, ctx) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -105,6 +105,12 @@ pub struct Stdlib { | |
| /// After 3.14, `typing_extensions` reexports from `typing`. | ||
| /// For 3.12 and 3.13 defined separately in both locations. | ||
| type_alias_type: StdlibResult<ClassType>, | ||
| /// Defined in `typing_extensions` as Sentinel. | ||
| /// Different attributes from `builtins` sentinel. | ||
| sentinel_typing_extensions: StdlibResult<ClassType>, | ||
| /// Defined in `builtins` as `sentinel` since 3.15. | ||
| /// Different attributes from `typing_extensions` Sentinel. | ||
| sentinel_builtin: StdlibResult<ClassType>, | ||
| traceback_type: StdlibResult<ClassType>, | ||
| builtins_type: StdlibResult<ClassType>, | ||
| /// Introduced in Python 3.10. | ||
|
|
@@ -257,6 +263,9 @@ impl Stdlib { | |
| param_spec_kwargs: lookup_concrete(standardised(3, 10), "ParamSpecKwargs"), | ||
| type_var_tuple: lookup_concrete(standardised(3, 11), "TypeVarTuple"), | ||
| type_alias_type: lookup_concrete(standardised(3, 12), "TypeAliasType"), | ||
| // sentinel: lookup_concrete(typing_extensions, "Sentinel"), | ||
| sentinel_builtin: lookup_concrete(builtins, "sentinel"), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How different are In general, we treat the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The main reason I differentiated between the two was the Here is how I confirmed this difference if you want to test it with the python 3.15 beta: uv run -p 3.15 --with 'typing_extensions' python -c 'from typing_extensions import Sentinel; MISSING = Sentinel("MISSING"); print(MISSING.__name__)'
> Traceback (most recent call last):
> File "<string>", line 1, in <module>
> from typing_extensions import Sentinel; MISSING = Sentinel("MISSING"); MISSING.__name__
> ^^^^^^^^^^^^^^^^
> AttributeError: 'Sentinel' object has no attribute '__name__'. Did you mean '.__ne__' instead of '.__name__'?
uv run -p 3.15 --with 'typing_extensions' python -c 'MISSING = sentinel("MISSING"); print(MISSING.__name__)'
> MISSINGAlso, if this ends up being kept, I should probably add a comment explaining the reasoning better, and remove that commented out line, not sure how that one got past me.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the explanation! If the presence/absence of the |
||
| sentinel_typing_extensions: lookup_concrete(typing_extensions, "Sentinel"), | ||
| traceback_type: lookup_concrete(types, "TracebackType"), | ||
| function_type: lookup_concrete(types, "FunctionType"), | ||
| method_type: lookup_concrete(types, "MethodType"), | ||
|
|
@@ -581,6 +590,13 @@ impl Stdlib { | |
| Self::primitive(&self.type_alias_type) | ||
| } | ||
|
|
||
| pub fn sentinel_builtin(&self) -> &ClassType { | ||
| Self::primitive(&self.sentinel_builtin) | ||
| } | ||
| pub fn sentinel_typing_extensions(&self) -> &ClassType { | ||
| Self::primitive(&self.sentinel_typing_extensions) | ||
| } | ||
|
|
||
| pub fn traceback_type(&self) -> &ClassType { | ||
| Self::primitive(&self.traceback_type) | ||
| } | ||
|
|
||
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.
You can grab the nesting context from the Scopes stack when constructing Binding::Sentinel - see comment in stmt.rs.
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, fixed