PEP 661: Sentinel Values

Proposed solution fits needs for pure-Python code. How about CPython parts, written in C? Or C extension modules? I’m pretty sure there is a zoo of poor-man replacements for this. E.g. the reduce() function uses NULL as default for the initial argument. perm() uses k=None.

Maybe it was discussed in the looong thread somewhere, but I would appreciate if that use case will be mentioned in the PEP text.

It wasn’t really discussed. I think it makes sense to provide a PySentinel_New(name, module) C API function. I’m not sure it’s needed for the cases you mention (C functions where NULL or None is acceptable), but it would make sense for replacing e.g. typing.NoDefault.

What should the interface look like? I’m leaning towards PySentinel_New(const char *name, const char *module).

1 Like

Sure, acceptable. But in mentioned cases its a replacement for missing values. Note also, that NULL breaks function signature.

Why not PyObject *module?

Why not a raw string? I am very open to being convinced about this, I don’t write much C.

My thinking was that for the typing.NoDefault marker, I’d want to be able to write something like:

no_default = PySentinel_New("NoDefault", "typing");
PyModule_AddObjectRef(m, "NoDefault", no_default):

I imagine that’s what most use cases would approximately look like.

I think this would happen in module initialization code where it’s natural to have a reference to the module object.

In the typing case, the module object is for the _typing module, but we want the object to pretend it is in typing. The same would go for functools.Placeholder, which is actually created in the _functools module.

I think the same thing will happen commonly in third-party code. For example, many numpy classes say they are in the numpy module, but in fact are presumably defined in some internal helper module.

1 Like

Thanks @taleinat for accepting @Jelle’s offer for help. I have done my (admittedly very small) part with a review of Jelle’s PR.

While reviewing I started to wonder whether the PEP should provide guidance for type checkers on how to handle:

MISSING = sentinel('MISSING')
MISSING = sentinel('MISSING')

It is very contrived but wouldn’t be surprised if this pattern could emerge in very long modules. Is this a redefinition of MISSING that is allowed? Or should this raise an error? Or should each type checker be allowed to choose the behaviour/strictness they want?

I don’t remember this being discussed in this thread, but I think we can all agree that at this point it is quite hard to remember all the various discussions we have had in it :sweat_smile:

1 Like

This particular issue wasn’t discussed. We haven’t clearly defined this in the typing spec either for similar use cases. However, we just amended the conformance suite (remove type statement restrictions unsupported by spec or PEP by carljm · Pull Request #2249 · python/typing · GitHub) to no longer require an error when redefining a type alias; this feels similar.

In any case, this is something that can be changed later with a change to the typing spec.

1 Like