Mailing List Archive

The repr of a sentinel
Following a recent change, we now have in traceback.py:

_sentinel = object()
def print_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None,
file=None, chain=True):

So now:

>>> import traceback
>>> help(traceback.print_exception)
Help on function print_exception in module traceback:

print_exception(exc, /, value=<object object at
0x000002825DF09650>, tb=<object object at 0x000002825DF09650>,
limit=None, file=None, chain=True)


Is there a convention on how such default sentinel values should appear in
docs?

https://bugs.python.org/issue43024
Re: The repr of a sentinel [ In reply to ]
On Thu, 13 May 2021 10:15:03 +0100
Irit Katriel via Python-Dev <python-dev@python.org> wrote:

> Following a recent change, we now have in traceback.py:
>
> _sentinel = object()
> def print_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None,
> file=None, chain=True):
>
> So now:
>
> >>> import traceback
> >>> help(traceback.print_exception)
> Help on function print_exception in module traceback:
>
> print_exception(exc, /, value=<object object at
> 0x000002825DF09650>, tb=<object object at 0x000002825DF09650>,
> limit=None, file=None, chain=True)
>
>
> Is there a convention on how such default sentinel values should appear in
> docs?

If this were a positional-only argument, you could use square brackets,
e.g.:

print_exception(exc[, value[, ...]])

Other than that, I can't think of any existing convention. I agree
that <optional> is a reasonable spelling.

Regards

Antoine.


_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-leave@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/EJXKQJM7COQFIPPSQGH5O3IAFPUKYWGL/
Code of Conduct: http://python.org/psf/codeofconduct/
Re: The repr of a sentinel [ In reply to ]
On Thu, May 13, 2021 at 10:28 AM Antoine Pitrou <antoine@python.org> wrote:

>
> I agree that <optional> is a reasonable spelling.
>
>
I initially suggested <optional>, but now I'm not sure because it doesn't
indicate what happens when you don't provide it (as in, what is the default
value). So now I'm with <derived> or <implicit>.

The arg is only there for backwards compatibility now.
Re: The repr of a sentinel [ In reply to ]
Le 13/05/2021 à 11:40, Irit Katriel a écrit :
>
>
> On Thu, May 13, 2021 at 10:28 AM Antoine Pitrou <antoine@python.org
> <mailto:antoine@python.org>> wrote:
>
>
>  I agree that <optional> is a reasonable spelling.
>
>
> I initially suggested <optional>, but now I'm not sure because it
> doesn't indicate what happens when you don't provide it (as in, what is
> the default value).  So now I'm with <derived> or <implicit>.

"<derived>" makes think of a derived class, and leaves me confused.
"<implicit>" is a bit better, but doesn't clearly say what the default
value is, either. So in all cases I have to read the docstring in
addition to the function signature.

Regards

Antoine.
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-leave@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/EVACPXULASJ2GUTN7ZICKJ2LOT224MP4/
Code of Conduct: http://python.org/psf/codeofconduct/
Re: The repr of a sentinel [ In reply to ]
On 13. 05. 21 11:45, Antoine Pitrou wrote:
>
> Le 13/05/2021 à 11:40, Irit Katriel a écrit :
>>
>>
>> On Thu, May 13, 2021 at 10:28 AM Antoine Pitrou <antoine@python.org
>> <mailto:antoine@python.org>> wrote:
>>
>>
>>       I agree that <optional> is a reasonable spelling.
>>
>>
>> I initially suggested <optional>, but now I'm not sure because it
>> doesn't indicate what happens when you don't provide it (as in, what
>> is the default value).  So now I'm with <derived> or <implicit>.
>
> "<derived>" makes think of a derived class, and leaves me confused.
> "<implicit>" is a bit better, but doesn't clearly say what the default
> value is, either.  So in all cases I have to read the docstring in
> addition to the function signature.
>

Is <default> the term you're looking for?
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-leave@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/POF7BUF5EGU37DB5F34DOVT7E6LVERX4/
Code of Conduct: http://python.org/psf/codeofconduct/
Re: The repr of a sentinel [ In reply to ]
On 5/13/2021 7:48 AM, Petr Viktorin wrote:
> On 13. 05. 21 11:45, Antoine Pitrou wrote:
>>
>> Le 13/05/2021 à 11:40, Irit Katriel a écrit :
>>>
>>>
>>> On Thu, May 13, 2021 at 10:28 AM Antoine Pitrou <antoine@python.org
>>> <mailto:antoine@python.org>> wrote:
>>>
>>>
>>>       I agree that <optional> is a reasonable spelling.
>>>
>>>
>>> I initially suggested <optional>, but now I'm not sure because it
>>> doesn't indicate what happens when you don't provide it (as in, what
>>> is the default value).  So now I'm with <derived> or <implicit>.
>>
>> "<derived>" makes think of a derived class, and leaves me confused.
>> "<implicit>" is a bit better, but doesn't clearly say what the
>> default value is, either.  So in all cases I have to read the
>> docstring in addition to the function signature.
>>
>
> Is <default> the term you're looking for?

In the dataclasses docs
https://docs.python.org/3/library/dataclasses.html I document the
module-level sentinel MISSING, then I document the function as:

dataclasses.field(*, default=MISSING, default_factory=MISSING,
repr=True, hash=None, init=True, compare=True, metadata=None)

And I explain what MISSING means.

The help looks like:

field(*, default=<dataclasses._MISSING_TYPE object at 0x6fffffe46610>,
default_factory=<dataclasses._MISSING_TYPE object at 0x6fffffe46610>,
init=True, repr=True, hash=None, compare=True, metadata=None)

None of this is particularly awesome, but no one has complained about it
yet.

I think it's important to state an actual value for the default value,
instead of just using something like "<default>". Unless you know the
actual default value, you can't write a wrapper.

Say I wanted to write something that calls dataclasses.field, but
doesn't allow the user to specify repr, hash, init, compare, and
metadata. I could write it as:

def myfield_func(*, default=MISSING, default_factory=MISSING):
    ...

If the real default values for "default" and "default_factory" weren't
documented, there wouldn't be any easy way to write this.

I do think a python-wide standard for this would be helpful, but I don't
see how to change existing code given backward compatibility constraints.

Eric

_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-leave@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/ICKR43MC35QNBEWZLJ6NW2RG2M4O27FU/
Code of Conduct: http://python.org/psf/codeofconduct/
Re: The repr of a sentinel [ In reply to ]
On 13May2021 1248, Petr Viktorin wrote:
> On 13. 05. 21 11:45, Antoine Pitrou wrote:
>>
>> Le 13/05/2021 à 11:40, Irit Katriel a écrit :
>>>
>>>
>>> On Thu, May 13, 2021 at 10:28 AM Antoine Pitrou <antoine@python.org
>>> <mailto:antoine@python.org>> wrote:
>>>
>>>
>>>       I agree that <optional> is a reasonable spelling.
>>>
>>>
>>> I initially suggested <optional>, but now I'm not sure because it
>>> doesn't indicate what happens when you don't provide it (as in, what
>>> is the default value).  So now I'm with <derived> or <implicit>.
>>
>> "<derived>" makes think of a derived class, and leaves me confused.
>> "<implicit>" is a bit better, but doesn't clearly say what the default
>> value is, either.  So in all cases I have to read the docstring in
>> addition to the function signature.
>>
>
> Is <default> the term you're looking for?

Perhaps <unspecified> or <missing>?

Cheers,
Steve
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-leave@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/RSRBWH2UK2MKZN7O3PHSNVZFZEE7JIVJ/
Code of Conduct: http://python.org/psf/codeofconduct/
Re: The repr of a sentinel [ In reply to ]
On Thu, May 13, 2021 at 4:31 PM Eric V. Smith <eric@trueblade.com> wrote:
>
> I do think a python-wide standard for this would be helpful, but I don't
> see how to change existing code given backward compatibility constraints.

While we're on the subject, these sentinels also don't compare
properly using `is` after pickling and unpickling.

I think it's worth considering making the sentinels in the stdlib all
have good reprs and support pickling+unpickling.

What would be the potential backwards-compatibility issues with
changing the implementation of these existing sentinel values?

- Tal
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-leave@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/KKDZ5TW6WXSQDE2YPOU6X5JXEU264HBS/
Code of Conduct: http://python.org/psf/codeofconduct/
Re: The repr of a sentinel [ In reply to ]
On 5/13/2021 10:02 AM, Tal Einat wrote:
> On Thu, May 13, 2021 at 4:31 PM Eric V. Smith <eric@trueblade.com> wrote:
>> I do think a python-wide standard for this would be helpful, but I don't
>> see how to change existing code given backward compatibility constraints.
> While we're on the subject, these sentinels also don't compare
> properly using `is` after pickling and unpickling.
>
> I think it's worth considering making the sentinels in the stdlib all
> have good reprs and support pickling+unpickling.
>
> What would be the potential backwards-compatibility issues with
> changing the implementation of these existing sentinel values?

I don't think there would be a problem changing the implementation. I
was commenting on changing the name of the sentinel objects so that we
could document the functions with the sentinel's real name. We couldn't
change them to all be some appropriate module-level value named
"MISSING", for example.

Eric

_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-leave@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/SWGCTZFNGLFEQ5SL5GBVQFQAD2YAAZNI/
Code of Conduct: http://python.org/psf/codeofconduct/
Re: The repr of a sentinel [ In reply to ]
On Thu, 13 May 2021 13:44:54 +0100
Steve Dower <steve.dower@python.org> wrote:
> On 13May2021 1248, Petr Viktorin wrote:
> > On 13. 05. 21 11:45, Antoine Pitrou wrote:
> >>
> >> Le 13/05/2021 à 11:40, Irit Katriel a écrit :
> >>>
> >>>
> >>> On Thu, May 13, 2021 at 10:28 AM Antoine Pitrou <antoine@python.org
> >>> <mailto:antoine@python.org>> wrote:
> >>>
> >>>
> >>>       I agree that <optional> is a reasonable spelling.
> >>>
> >>>
> >>> I initially suggested <optional>, but now I'm not sure because it
> >>> doesn't indicate what happens when you don't provide it (as in, what
> >>> is the default value).  So now I'm with <derived> or <implicit>.
> >>
> >> "<derived>" makes think of a derived class, and leaves me confused.
> >> "<implicit>" is a bit better, but doesn't clearly say what the default
> >> value is, either.  So in all cases I have to read the docstring in
> >> addition to the function signature.
> >>
> >
> > Is <default> the term you're looking for?
>
> Perhaps <unspecified> or <missing>?

Now that I read more about the specific use case, though, I think
"<implicit>" really describes it accurately. It's not that the
information is missing, it's that it's already implied in another
argument.

Quoting the documentation:

"""Since Python 3.10, instead of passing value and tb, an exception object
can be passed as the first argument."""

(meaning the traceback is implicitly gotten from the exception object
which is passed as first argument)

Regards

Antoine.


_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-leave@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/BROL74WPPOLTJLSRER6NLWUJRU3JK4SE/
Code of Conduct: http://python.org/psf/codeofconduct/
Re: The repr of a sentinel [ In reply to ]
>>>>
On 5/13/21 2:15 AM, Irit Katriel via Python-Dev wrote:
>
> >>> help(traceback.print_exception)
> Help on function print_exception in module traceback:
>
> print_exception(exc, /, value=<object object at 0x000002825DF09650>, tb=<object object
> at 0x000002825DF09650>, limit=None, file=None, chain=True)


On 5/13/21 5:37 AM, Eric V. Smith wrote:
>
> The help looks like:
>
> field(*, default=<dataclasses._MISSING_TYPE object at 0x6fffffe46610>,
> default_factory=<dataclasses._MISSING_TYPE object at 0x6fffffe46610>,
> init=True, repr=True, hash=None, compare=True, metadata=None)
>
> None of this is particularly awesome, but no one has complained about it yet.

Consider me complaining. ;-)

Looks to me like the default repr for the sentinels is making those helps much less helpful by showing totally
irrelevant information and cluttering up the screen making it harder to see the actually useful bits.

An actual Sentinel class would be helpful:

>>> class Sentinel:
... def __init__(self, repr):
... self.repr = repr
... def __repr__(self):
... return self.repr
...

>>> MISSING = Sentinel('MISSING')
>>> MISSING
MISSING

>>> implicit = Sentinel('<implicit>')
>>> implicit
<implicit>

Naturally, since sentinels are symbolic names, I think it should go into the enum module. ;-) Although I will concede
that we could just put those five lines into the modules that need it.

--
~Ethan~
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-leave@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/7S5PU6334ZZXCA3EFV244YZWY27KA3FD/
Code of Conduct: http://python.org/psf/codeofconduct/
Re: The repr of a sentinel [ In reply to ]
On Thu, May 13, 2021 at 7:44 PM Ethan Furman <ethan@stoneleaf.us> wrote:
>
> Consider me complaining. ;-)

+1

> An actual Sentinel class would be helpful:
>
> >>> class Sentinel:
> ... def __init__(self, repr):
> ... self.repr = repr
> ... def __repr__(self):
> ... return self.repr
> ...
>
> >>> MISSING = Sentinel('MISSING')
> >>> MISSING
> MISSING
>
> >>> implicit = Sentinel('<implicit>')
> >>> implicit
> <implicit>

Here is my suggestion (also posted on the related bpo-44123), which is
also simple, ensures a single instance is used, even considering
multi-threading and pickling, and has a better repr:

class Sentinel:
def __new__(cls, *args, **kwargs):
raise TypeError(f'{cls.__qualname__} cannot be instantiated')

class MISSING(Sentinel):
pass

- Tal
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-leave@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/URFRF634732GRICGLRPGJEJON2BYQZM4/
Code of Conduct: http://python.org/psf/codeofconduct/
Re: The repr of a sentinel [ In reply to ]
On 5/13/2021 12:41 PM, Ethan Furman wrote:
>>>>>
> On 5/13/21 2:15 AM, Irit Katriel via Python-Dev wrote:
>>
>>      >>> help(traceback.print_exception)
>>      Help on function print_exception in module traceback:
>>
>>      print_exception(exc, /, value=<object object at
>> 0x000002825DF09650>, tb=<object object
>>                      at 0x000002825DF09650>, limit=None, file=None,
>> chain=True)
>
>
> On 5/13/21 5:37 AM, Eric V. Smith wrote:
>>
>> The help looks like:
>>
>>      field(*, default=<dataclasses._MISSING_TYPE object at
>> 0x6fffffe46610>,
>>            default_factory=<dataclasses._MISSING_TYPE object at
>> 0x6fffffe46610>,
>>            init=True, repr=True, hash=None, compare=True, metadata=None)
>>
>> None of this is particularly awesome, but no one has complained about
>> it yet.
>
> Consider me complaining.  ;-)
>
Your complaint is hereby noted!

> Looks to me like the default repr for the sentinels is making those
> helps much less helpful by showing totally irrelevant information and
> cluttering up the screen making it harder to see the actually useful
> bits.
>
> An actual Sentinel class would be helpful:
>
>    >>> class Sentinel:
>    ...     def __init__(self, repr):
>    ...         self.repr = repr
>    ...     def __repr__(self):
>    ...         return self.repr
>    ...
>
>    >>> MISSING = Sentinel('MISSING')
>    >>> MISSING
>    MISSING
>
dataclasses.py actually has similar code, but for some reason I guess it
got missed for MISSING (ha!).
>
> Naturally, since sentinels are symbolic names, I think it should go
> into the enum module.  ;-)  Although I will concede that we could just
> put those five lines into the modules that need it.

Yeah, it's probably not worth dataclasses importing enum just to get
that functionality.

Eric

_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-leave@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/45RGCWZJSS7WDWCYUEDL72NNHSYGGLCU/
Code of Conduct: http://python.org/psf/codeofconduct/
Re: The repr of a sentinel [ In reply to ]
On 5/13/2021 1:39 PM, Tal Einat wrote:
> On Thu, May 13, 2021 at 7:44 PM Ethan Furman <ethan@stoneleaf.us> wrote:
>> Consider me complaining. ;-)
> +1
>
>> An actual Sentinel class would be helpful:
>>
>> >>> class Sentinel:
>> ... def __init__(self, repr):
>> ... self.repr = repr
>> ... def __repr__(self):
>> ... return self.repr
>> ...
>>
>> >>> MISSING = Sentinel('MISSING')
>> >>> MISSING
>> MISSING
>>
>> >>> implicit = Sentinel('<implicit>')
>> >>> implicit
>> <implicit>
> Here is my suggestion (also posted on the related bpo-44123), which is
> also simple, ensures a single instance is used, even considering
> multi-threading and pickling, and has a better repr:
>
> class Sentinel:
> def __new__(cls, *args, **kwargs):
> raise TypeError(f'{cls.__qualname__} cannot be instantiated')
>
> class MISSING(Sentinel):
> pass

>>> MISSING
<class '__main__.MISSING'>

I think a repr of just "MISSING", or maybe "dataclasses.MISSING" would
be better.

Eric

_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-leave@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/EQZV3KZZQNBN6NQRIERUK5HOEAUW2DUJ/
Code of Conduct: http://python.org/psf/codeofconduct/
Re: The repr of a sentinel [ In reply to ]
On 5/13/21 10:46 AM, Eric V. Smith wrote:
> >>> MISSING
> <class '__main__.MISSING'>
>
> I think a repr of just "MISSING", or maybe "dataclasses.MISSING" would
> be better.


I literally just went down this road--for a while there was a special
sentinel value for the eval_str parameter to inspect.get_annotations(). 
The repr I went with was "<id>", e.g "<MISSING>".  It depends on how
seriously you take the idea that eval(repr(x)) == x.  Certainly most
objects don't actually support that, e.g., uh, object(), a type which I
understand is available in most Python implementations.


Cheers,


//arry/
Re: The repr of a sentinel [ In reply to ]
On Thu, May 13, 2021 at 8:46 PM Eric V. Smith <eric@trueblade.com> wrote:
>
>
> On 5/13/2021 1:39 PM, Tal Einat wrote:
> > Here is my suggestion (also posted on the related bpo-44123), which is
> > also simple, ensures a single instance is used, even considering
> > multi-threading and pickling, and has a better repr:
> >
> > class Sentinel:
> > def __new__(cls, *args, **kwargs):
> > raise TypeError(f'{cls.__qualname__} cannot be instantiated')
> >
> > class MISSING(Sentinel):
> > pass
>
> >>> MISSING
> <class '__main__.MISSING'>
>
> I think a repr of just "MISSING", or maybe "dataclasses.MISSING" would
> be better.

The repr uses whatever module the class is defined in, so we'd get:

>>> from dataclasses import MISSING
>>> MISSING
<class 'dataclasses.MISSING'>

We could override that to something even cleaner with a meta-class. For example:

class Sentinel(type):
@classmethod
def __prepare__(cls, name, bases, **kwds):
d = super().__prepare__(name, bases, **kwds)
def __new__(cls_, *args, **kwargs):
raise TypeError(f'{cls_!r} is a sentinel and cannot be
instantiated')
d.update(__new__=__new__)
return d

def __repr__(cls):
return f'{cls.__module__}.{cls.__qualname__}'

Which results in:

>>> from dataclasses import MISSING
>>> MISSING
dataclasses.MISSING
>>> type(MISSING)
<class 'sentinels.Sentinel'>
>>> MISSING()
Traceback (most recent call last): ...
TypeError: dataclasses.MISSING is a sentinel and cannot be instantiated

- Tal


- Tal
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-leave@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/ECYFQWPBQPRN4ZKDU6WNPPAG3Y5XZ2BD/
Code of Conduct: http://python.org/psf/codeofconduct/
Re: The repr of a sentinel [ In reply to ]
There was a discussion a while back ( a year or so?? ) on Python-ideas that
introduced the idea of having more "sentinel-like" singletons in Python --
right now, we only have None.

I can't remember the context, but the consensus seemed to be that it
was easy to create a custom sentinel object, and it was not worth adding
more "official" ones to the language. But this conversation reminded me
about that, and while I do agree that we don't need more that are elevated
to the status of None, maybe it would be good to have a couple (or only
MISSING) in the standard library somewhere "central" for everyone to use.
"central" rather than in, say, dataclasses.

I'm not sure where that should be, the operator module, maybe??

Ayway, if someone were to put one of the nifty implementations being
discussed here in the stdlib --I'd use it :-)

-CHB




On Thu, May 13, 2021 at 1:14 PM Tal Einat <taleinat@gmail.com> wrote:

> On Thu, May 13, 2021 at 8:46 PM Eric V. Smith <eric@trueblade.com> wrote:
> >
> >
> > On 5/13/2021 1:39 PM, Tal Einat wrote:
> > > Here is my suggestion (also posted on the related bpo-44123), which is
> > > also simple, ensures a single instance is used, even considering
> > > multi-threading and pickling, and has a better repr:
> > >
> > > class Sentinel:
> > > def __new__(cls, *args, **kwargs):
> > > raise TypeError(f'{cls.__qualname__} cannot be instantiated')
> > >
> > > class MISSING(Sentinel):
> > > pass
> >
> > >>> MISSING
> > <class '__main__.MISSING'>
> >
> > I think a repr of just "MISSING", or maybe "dataclasses.MISSING" would
> > be better.
>
> The repr uses whatever module the class is defined in, so we'd get:
>
> >>> from dataclasses import MISSING
> >>> MISSING
> <class 'dataclasses.MISSING'>
>
> We could override that to something even cleaner with a meta-class. For
> example:
>
> class Sentinel(type):
> @classmethod
> def __prepare__(cls, name, bases, **kwds):
> d = super().__prepare__(name, bases, **kwds)
> def __new__(cls_, *args, **kwargs):
> raise TypeError(f'{cls_!r} is a sentinel and cannot be
> instantiated')
> d.update(__new__=__new__)
> return d
>
> def __repr__(cls):
> return f'{cls.__module__}.{cls.__qualname__}'
>
> Which results in:
>
> >>> from dataclasses import MISSING
> >>> MISSING
> dataclasses.MISSING
> >>> type(MISSING)
> <class 'sentinels.Sentinel'>
> >>> MISSING()
> Traceback (most recent call last): ...
> TypeError: dataclasses.MISSING is a sentinel and cannot be instantiated
>
> - Tal
>
>
> - Tal
> _______________________________________________
> Python-Dev mailing list -- python-dev@python.org
> To unsubscribe send an email to python-dev-leave@python.org
> https://mail.python.org/mailman3/lists/python-dev.python.org/
> Message archived at
> https://mail.python.org/archives/list/python-dev@python.org/message/ECYFQWPBQPRN4ZKDU6WNPPAG3Y5XZ2BD/
> Code of Conduct: http://python.org/psf/codeofconduct/
>


--
Christopher Barker, PhD (Chris)

Python Language Consulting
- Teaching
- Scientific Software Development
- Desktop GUI and Web Development
- wxPython, numpy, scipy, Cython
Re: The repr of a sentinel [ In reply to ]
On Thu, May 13, 2021 at 10:31 PM Christopher Barker <pythonchb@gmail.com>
wrote:

> There was a discussion a while back ( a year or so?? ) on Python-ideas
> that introduced the idea of having more "sentinel-like" singletons in
> Python -- right now, we only have None.
>

As I remember, the year-ago conversation was basically wanting more ways of
saying "argument not specified" in function signatures. The thought was
that None is used pretty often to mean something somewhat different. The
rough consensus seemed to be that `my_sentinel = object()` was a low burden
per project.

But in what I recall, there was no talk there of custom behavior like a
nicer repr(). My own feeling is that ONLY a repr() isn't quite enough to
motivate an addition to stdlib. But if there were a few other useful
behaviors, maybe it would be (e.g. maybe default or specifiable inequality
operations). However, I wouldn't really want another two or three or four
singletons, but rather a slightly more templated factory than just
`object()`.

--
The dead increasingly dominate and strangle both the living and the
not-yet born. Vampiric capital and undead corporate persons abuse
the lives and control the thoughts of homo faber. Ideas, once born,
become abortifacients against new conceptions.
Re: The repr of a sentinel [ In reply to ]
> There was a discussion a while back ( a year or so?? ) on Python-ideas
> that introduced the idea of having more "sentinel-like" singletons in
> Python -- right now, we only have None.
>

Not quite true, we also have Ellipsis, which already has a nice repr that
both reads easily and still follows the convention of eval(repr(x)) == x.
It also is already safe from instantiation, survives pickle round-trip and
is multi-thread safe.
So long as you are not dealing with scientific projects, it seems a
quick (if dirty) solution to having a sentinel that is not None. There is
also some symmetrical wholeness when considered with the other builtin
sentinels: bool(None) is False; bool(Ellipsis) is True.
Re: The repr of a sentinel [ In reply to ]
Hi Tal,

Would it make sense to have an unique singleton for such sentinel, a
built-in singleton like None or Ellipsis? I propose the name
"Sentinel".

Sentinel would be similar to None, but the main property would be that
"Sentinel is None" is false :-)

The stdlib contains tons of sentinels:

* _collections_abc: __marker__
* cgitb.__UNDEF__
* configparser: _UNSET
* dataclasses: _HAS_DEFAULT_FACTORY, MISSING, KW_ONLY
* datetime.timezone._Omitted
* fnmatch.translate() STAR
* functools.lru_cache.sentinel (each @lru_cache creates its own sentinel object)
* functools._NOT_FOUND
* heapq: temporary sentinel in nsmallest() and nlargest()
* inspect._sentinel
* inspect._signature_fromstr() invalid
* plistlib._undefined
* runpy._ModifiedArgv0._sentinel
* sched: _sentinel
* traceback: _sentinel

There are different but similar use cases:

* Optional parameter: distinguish between func() and func(arg=value),
a sentinel is useful to distinguish func() from func(arg=None)
* Look into a data structure for a value and store the result in a
value, distinguish if 'result' variable was set ("result is not None"
doesn't work since None is a value). Quick example: "missing =
object(); tmsg = self._catalog.get(message, missing); if tmsg is
missing: ..."

Special cases:

* dataclases._EMPTY_METADATA = types.MappingProxyType({})
* string._sentinel_dict = {}
* enum: _auto_null = object()

Victor

On Thu, May 13, 2021 at 7:40 PM Tal Einat <taleinat@gmail.com> wrote:
>
> On Thu, May 13, 2021 at 7:44 PM Ethan Furman <ethan@stoneleaf.us> wrote:
> >
> > Consider me complaining. ;-)
>
> +1
>
> > An actual Sentinel class would be helpful:
> >
> > >>> class Sentinel:
> > ... def __init__(self, repr):
> > ... self.repr = repr
> > ... def __repr__(self):
> > ... return self.repr
> > ...
> >
> > >>> MISSING = Sentinel('MISSING')
> > >>> MISSING
> > MISSING
> >
> > >>> implicit = Sentinel('<implicit>')
> > >>> implicit
> > <implicit>
>
> Here is my suggestion (also posted on the related bpo-44123), which is
> also simple, ensures a single instance is used, even considering
> multi-threading and pickling, and has a better repr:
>
> class Sentinel:
> def __new__(cls, *args, **kwargs):
> raise TypeError(f'{cls.__qualname__} cannot be instantiated')
>
> class MISSING(Sentinel):
> pass
>
> - Tal
> _______________________________________________
> Python-Dev mailing list -- python-dev@python.org
> To unsubscribe send an email to python-dev-leave@python.org
> https://mail.python.org/mailman3/lists/python-dev.python.org/
> Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/URFRF634732GRICGLRPGJEJON2BYQZM4/
> Code of Conduct: http://python.org/psf/codeofconduct/



--
Night gathers, and now my watch begins. It shall not end until my death.
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-leave@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/JBYXQH3NV3YBF7P2HLHB5CD6V3GVTY55/
Code of Conduct: http://python.org/psf/codeofconduct/
Re: The repr of a sentinel [ In reply to ]
On 14. 05. 21 10:55, Victor Stinner wrote:
> Hi Tal,
>
> Would it make sense to have an unique singleton for such sentinel, a
> built-in singleton like None or Ellipsis? I propose the name
> "Sentinel".
>
> Sentinel would be similar to None, but the main property would be that
> "Sentinel is None" is false :-)

If you need your Sentinel to be different from one particular sentinel
(None), you'll usually want it to be different from all the other ones
as well.

A sentinel for an optional parameter shouldn't really be used at all
outside of the function it's defined for. That's why it's usually
defined as a private module-level variable.

Perhaps it would be beneficial to provide a common base class or
factory, so we get a good repr. But I don't think another common value
like None and Ellipsis would do much good.


> The stdlib contains tons of sentinels:
>
> * _collections_abc: __marker__
> * cgitb.__UNDEF__
> * configparser: _UNSET
> * dataclasses: _HAS_DEFAULT_FACTORY, MISSING, KW_ONLY
> * datetime.timezone._Omitted
> * fnmatch.translate() STAR
> * functools.lru_cache.sentinel (each @lru_cache creates its own sentinel object)
> * functools._NOT_FOUND
> * heapq: temporary sentinel in nsmallest() and nlargest()
> * inspect._sentinel
> * inspect._signature_fromstr() invalid
> * plistlib._undefined
> * runpy._ModifiedArgv0._sentinel
> * sched: _sentinel
> * traceback: _sentinel
>
> There are different but similar use cases:
>
> * Optional parameter: distinguish between func() and func(arg=value),
> a sentinel is useful to distinguish func() from func(arg=None)
> * Look into a data structure for a value and store the result in a
> value, distinguish if 'result' variable was set ("result is not None"
> doesn't work since None is a value). Quick example: "missing =
> object(); tmsg = self._catalog.get(message, missing); if tmsg is
> missing: ..."
>
> Special cases:
>
> * dataclases._EMPTY_METADATA = types.MappingProxyType({})
> * string._sentinel_dict = {}
> * enum: _auto_null = object()
>
> Victor
>
> On Thu, May 13, 2021 at 7:40 PM Tal Einat <taleinat@gmail.com> wrote:
>>
>> On Thu, May 13, 2021 at 7:44 PM Ethan Furman <ethan@stoneleaf.us> wrote:
>>>
>>> Consider me complaining. ;-)
>>
>> +1
>>
>>> An actual Sentinel class would be helpful:
>>>
>>> >>> class Sentinel:
>>> ... def __init__(self, repr):
>>> ... self.repr = repr
>>> ... def __repr__(self):
>>> ... return self.repr
>>> ...
>>>
>>> >>> MISSING = Sentinel('MISSING')
>>> >>> MISSING
>>> MISSING
>>>
>>> >>> implicit = Sentinel('<implicit>')
>>> >>> implicit
>>> <implicit>
>>
>> Here is my suggestion (also posted on the related bpo-44123), which is
>> also simple, ensures a single instance is used, even considering
>> multi-threading and pickling, and has a better repr:
>>
>> class Sentinel:
>> def __new__(cls, *args, **kwargs):
>> raise TypeError(f'{cls.__qualname__} cannot be instantiated')
>>
>> class MISSING(Sentinel):
>> pass
>>
>> - Tal
>> _______________________________________________
>> Python-Dev mailing list -- python-dev@python.org
>> To unsubscribe send an email to python-dev-leave@python.org
>> https://mail.python.org/mailman3/lists/python-dev.python.org/
>> Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/URFRF634732GRICGLRPGJEJON2BYQZM4/
>> Code of Conduct: http://python.org/psf/codeofconduct/
>
>
>
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-leave@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/SJ45ED57TNPLFCWXAREUGRKSPTPPJYJI/
Code of Conduct: http://python.org/psf/codeofconduct/
Re: The repr of a sentinel [ In reply to ]
On Fri, May 14, 2021 at 7:31 PM Petr Viktorin <encukou@gmail.com> wrote:
> Perhaps it would be beneficial to provide a common base class or
> factory, so we get a good repr. But I don't think another common value
> like None and Ellipsis would do much good.
>

Agreed - I think Sentinel would make a great class, from which you
instantiate purpose-specific sentinels.

But maybe there really needs to be a way to NOT specify an argument,
and to find out that an argument wasn't specified? Defaulting is only
one way to handle it. Consider:

def truly_optional_arg(x, y, *z):
"""Two mandatory args, and then an optional one"""
if z: print("Got an extra arg")

pass_arg = input("Pass the arg? ") == "y"
truly_optional_arg(10, 20, *([30] if pass_arg else []))

It's horrifically ugly, but it really and truly does/doesn't pass that
argument, and it really and truly detects whether one was passed. To
make that sort of thing actually viable, there'd need to be some sort
of language support; maybe something where the local name would start
out unbound, but testing for a local's boundness is a clunky
try/except, so that might also need some support.

Do we ever really need the ability to pass a specific sentinel to a
function, or are we actually looking for a way to say "and don't pass
this argument"?

ChrisA
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-leave@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/VSKX4PV4URDMNAJ6AYW3H3XKI4XWPJNS/
Code of Conduct: http://python.org/psf/codeofconduct/
Re: The repr of a sentinel [ In reply to ]
On 14May2021 0622, micro codery wrote:
>
> There was a discussion a while back ( a year or so?? ) on
> Python-ideas that introduced the idea of having more "sentinel-like"
> singletons in Python -- right now, we only have None.
>
> Not quite true, we also have Ellipsis, which already has a nice repr
> that both reads easily and still follows the convention of eval(repr(x))
> == x. It also is already safe from instantiation, survives pickle
> round-trip and is multi-thread safe.
> So long as you are not dealing with scientific projects, it seems a
> quick (if dirty) solution to having a sentinel that is not None.

I don't think using "..." to indicate "some currently unknown or
unspecified value" is dirty at all, it seems perfectly consistent with
how we use it in English (and indexing in scientific projects, for that
matter, where it tends to imply "figure out the rest for me").

All that's really missing is some kind of endorsement (from python-dev,
presumably in the docs) that it's okay to use it as a default parameter
value. I can't think of any reason you'd need to accept Ellipsis as a
*specified* value that wouldn't also apply to any other kind of shared
sentinel.

Cheers,
Steve
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-leave@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/VCKTZF45OAFYNIOL5IVNI5HG34BGJBEA/
Code of Conduct: http://python.org/psf/codeofconduct/
Re: The repr of a sentinel [ In reply to ]
On May 14, 2021, at 02:38, Chris Angelico <rosuav@gmail.com> wrote:
>
> Do we ever really need the ability to pass a specific sentinel to a
> function, or are we actually looking for a way to say "and don't pass
> this argument”?

Very often, that’s the case. Such a “it’s okay to not pass this argument” construct would have to work with the Optional type too.

The other use case I have for a special case single use singleton is for dict.get(), i.e.

missing = object()
value = somedict.get(‘key’, missing)
if value is missing:
# It ain’t there.

-Barry
Re: The repr of a sentinel [ In reply to ]
On Sat, May 15, 2021 at 2:04 AM Barry Warsaw <barry@python.org> wrote:
>
> On May 14, 2021, at 02:38, Chris Angelico <rosuav@gmail.com> wrote:
> >
> > Do we ever really need the ability to pass a specific sentinel to a
> > function, or are we actually looking for a way to say "and don't pass
> > this argument”?
>
> Very often, that’s the case. Such a “it’s okay to not pass this argument” construct would have to work with the Optional type too.

What I mean is: how often do you actually need to pass the specific
sentinel, as opposed to the normal construct of asking if the argument
was or wasn't passed? Eg if you have a function like this:

_sentinel = object()
def func(x, y=_sentinel):
if y is _sentinel: ...
else: ...

Would you ever call it like this:

func(42, _sentinel)

? Because if there's no reason to ever pass the sentinel, then it's
nothing more than an implementation detail for the concept of "was
this argument passed?", and that's exactly what I'm asking about.

> The other use case I have for a special case single use singleton is for dict.get(), i.e.
>
> missing = object()
> value = somedict.get(‘key’, missing)
> if value is missing:
> # It ain’t there.
>

I'd write that one with a try/except instead. The whole point of get()
is to provide the missing value.

try:
value = somedict['key']
except KeyError:
# It ain't there.

No sentinel needed. Same number of lines.

ChrisA
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-leave@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/TMZWXDXJSVXTEKJNXYDMQNHTFC6ZXIOH/
Code of Conduct: http://python.org/psf/codeofconduct/

1 2 3  View All