@taleinat Thank you for this PEP, and the good news is that the 2025 SC has started discussing it. We haven’t gotten too far, but I wanted to give you my thoughts, mostly as a Python user and developer. My mind as an SC member is still open, so please do not take these suggestions as anything official or as a sign of which way the discussions will go.
I’ve certainly written my fair share of sentinels, mostly of the missing = object() variety, which generally does what I need it to do, in very localized use cases. I’d say primarily in “get” APIs where None is a possible legal value, e.g.:
missing = object()
if dict.get('key', missing) is missing:
# The key really isn't in the dictionary.
I usually only need it right there and then, but if it’s convenient to use in multiple places in a module, I might define it at module scope.
Reading the PEP, I find the complaint about object()'s repr resonates with me. I can’t remember the last time I needed its repr, but I can see it wouldn’t hurt to have something more recognizable. Likewise, I can understand the motivation for being more type-friendly, even though again, I haven’t needed that personally.
I don’t find the motivation about copying or pickling too persuasive, mostly because I’ve just never needed it. For missing value singletons, I can’t imagine ever needing to pickle or copy them, but I guess it might be useful for a generic singleton object. I lean toward YAGNI; do you have some real world examples where this functionality would be useful?
Likewise, I’m not so sure about the registry. It feels to me like a YAGNI, and it complicates the semantics and implementation. Like someone said above, I’d eliminate that and leave it to the user to ensure whether they wanted unique sentinels on each instantiation or not. I’m also not comfortable with the sys._getframe() call, which would be a performance hit, though it could be avoided by passing a module_name in the PEP’s proposed API.
If I had to boil down sentinels to the most useful, simplest functionality, I’d just want something like object() with a configurable repr, and maybe a settable bool value.
I’d also consider adding a sentinel() callable as a built-in to really make it just as convenient as using object(). The big advantage is that you wouldn’t need to import anything to use it. Having to import a module first would be a mild deterrent to changing my own uses of the object() sentinel in my existing code. This would also mean that you wouldn’t have to worry about what module it lives in, but you’d have to implement it in C. For a simpler API, that might be easy. If you still want a more expansive functionality, or you need it for type hinting, then the built-in sentinel() could just be a front-end into some Python code, returning a Sentinel instance written in Python. It would still be less important exactly where that Python code lives if you never need the underlying Sentinel class.