Title: Add style guide for sentinel usage
Type: Stage: resolved
Components: Versions:
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: eric.smith, madman bob, serhiy.storchaka, steven.daprano, vstinner
Priority: normal Keywords:

Created on 2018-10-31 11:22 by madman bob, last changed 2018-11-07 11:59 by serhiy.storchaka. This issue is now closed.

Messages (8)
msg328984 - (view) Author: Robert Wright (madman bob) * Date: 2018-10-31 11:22
Sentinel values are used inconsistently in the Python code base.

It would be helpful to have a style guide (about eg. casing, placement, and sentinel reuse) to prevent arguments about their use in future code.
msg328987 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2018-10-31 11:33
Could you give some examples of problems caused by inconsistent usage of sentinels?

I do often wish modules exposed their sentinel values, since it makes writing wrappers easier. It's one of the reasons I ended up exposing dataclasses.MISSING.
msg328989 - (view) Author: Robert Wright (madman bob) * Date: 2018-10-31 12:00
Just the classic problems caused by inconsistency in code - confusion when reading, and disagreements when writing.

For reading: Where should I look in a file for the sentinels? Is this value a sentinel, or is it some other type of variable?

For writing, there will always be disagreements in PRs, so we should nip it in the bud.

You raise a good point about exposing sentinel values. I was previously on the fence about whether sentinels should (in general) be private, or public.
msg329007 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2018-10-31 19:35
msg329021 - (view) Author: Steven D'Aprano (steven.daprano) * (Python committer) Date: 2018-10-31 23:08
I was not aware that there currently were arguments about the use of sentinels in future code. I feel that you are being excessively prescriptive here:

"we should nip it in the bud"

is good advice for gardening, but for programming it just results in a lot of pre-emptive arguments about prescriptive rules that aren't needed.

What makes a sentinel a sentinel is *how it is used*, not what it is. As such, sentinels are just another type of (pseudo-)constant, and the same style guides should apply: in general, they should be in UPPER case, at the top of the module. (But remember the rule about foolish consistency.)

And each sentinel should be reused exactly as often as is appropriate. The same as any other constant.

As for exposing sentinels, the same applies to any part of the implementation. Should we make this a public part of the API or a private implementation detail? That has to be judged on a case-by-case basis. Making a rule or even a guideline that just because something is a sentinel is "should be" public (or private) is precisely the sort of thing I don't want to see. Sentinels should be public when it makes sense to make them public and private when it makes sense to keep them as private implementation details.

The bottom line is, I don't think we need sentinel-specific rules in the style guide. That would be like adding specific rules for "numeric constants", or specific rules for "constants used by decorators".
msg329024 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2018-10-31 23:38
I don't see any problems that need solving.

My only interest in this is that I tend to think sentinels that are used for missing parameters should be exposed, and this isn't always an obvious consideration when designing an API.

For example, say there's a library that has a function:
open(name, type, [mode])

That is, mode is optional. I realize that in pure Python, I could find out what sentinel value they're using, unless they're playing tricks with args and kwargs. But maybe this is written in C.

Now, say I want to write a wrapper around this, called myopen, and always pass in "foo" for type.

I'd need to use my own sentinel:
_MISSING= object()
def myopen(name, mode=_MISSING)

But, how do I call open? In the past, I've done things like:
if mode is _MISSING:
  return open(name, "foo")
  return open(name, "foo", mode)

That doesn't scale well if there are multiple optional parameters.

In any event, this just seems like a guideline we should be aware of when adding APIs.

Absent any concrete proposals for a "sentinel style guide", I don't think there's anything actionable here. I agree with Steven that it's a case-by-case thing. One size need not fit all.

As to the issue referenced by Victor: I'm not sure what a style guide would bring to the table. I think the issue of whether to use a second sentinel or not could be decided in this file alone, without needed a global decision.
msg329413 - (view) Author: Robert Wright (madman bob) * Date: 2018-11-07 11:48
Perhaps there's confusion over what I mean by "style guide". I'd happily take Steven's message *as* the style guide. Or, at the very least, the clarification that sentinels are to be thought of as constants.

It's one of those things that's obvious once mentioned, but might not be thought of when you're using them.

Though Eric does raise an interesting point about C sentinels. Something like:

kwargs = {}

if mode is not _MISSING:

return open(name, "foo", **kwargs)

would work a bit better with multiple arguments, but it's still not great.
msg329415 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2018-11-07 11:59
I agree that there are not any problems that need solving.
Date User Action Args
2018-11-07 11:59:34serhiy.storchakasetstatus: open -> closed

nosy: + serhiy.storchaka
messages: + msg329415

resolution: not a bug
stage: resolved
2018-11-07 11:48:33madman bobsetmessages: + msg329413
2018-10-31 23:38:53eric.smithsetmessages: + msg329024
2018-10-31 23:08:03steven.dapranosetnosy: + steven.daprano
messages: + msg329021
2018-10-31 19:35:29vstinnersetnosy: + vstinner
messages: + msg329007
2018-10-31 12:00:16madman bobsetmessages: + msg328989
2018-10-31 11:33:17eric.smithsetnosy: + eric.smith
messages: + msg328987
2018-10-31 11:22:05madman bobcreate