mirror of
https://github.com/python/cpython.git
synced 2026-05-04 09:31:02 +00:00
gh-148829: Implement PEP 661 (#148831)
Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com> Co-authored-by: Pieter Eendebak <pieter.eendebak@gmail.com> Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
This commit is contained in:
parent
0efd679a6c
commit
29a92abb60
24 changed files with 532 additions and 32 deletions
|
|
@ -112,6 +112,7 @@ Other Objects
|
|||
picklebuffer.rst
|
||||
weakref.rst
|
||||
capsule.rst
|
||||
sentinel.rst
|
||||
frame.rst
|
||||
gen.rst
|
||||
coro.rst
|
||||
|
|
|
|||
35
Doc/c-api/sentinel.rst
Normal file
35
Doc/c-api/sentinel.rst
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
.. highlight:: c
|
||||
|
||||
.. _sentinelobjects:
|
||||
|
||||
Sentinel objects
|
||||
----------------
|
||||
|
||||
.. c:var:: PyTypeObject PySentinel_Type
|
||||
|
||||
This instance of :c:type:`PyTypeObject` represents the Python
|
||||
:class:`sentinel` type. This is the same object as :class:`sentinel`.
|
||||
|
||||
.. versionadded:: next
|
||||
|
||||
.. c:function:: int PySentinel_Check(PyObject *o)
|
||||
|
||||
Return true if *o* is a :class:`sentinel` object. The :class:`sentinel` type
|
||||
does not allow subclasses, so this check is exact.
|
||||
|
||||
.. versionadded:: next
|
||||
|
||||
.. c:function:: PyObject* PySentinel_New(const char *name, const char *module_name)
|
||||
|
||||
Return a new :class:`sentinel` object with :attr:`~sentinel.__name__` set to
|
||||
*name* and :attr:`~sentinel.__module__` set to *module_name*.
|
||||
*name* must not be ``NULL``. If *module_name* is ``NULL``, :attr:`~sentinel.__module__`
|
||||
is set to ``None``.
|
||||
Return ``NULL`` with an exception set on failure.
|
||||
|
||||
For pickling to work, *module_name* must be the name of an importable
|
||||
module, and the sentinel must be accessible from that module under a
|
||||
path matching *name*. Pickle treats *name* as a global variable name
|
||||
in *module_name* (see :meth:`object.__reduce__`).
|
||||
|
||||
.. versionadded:: next
|
||||
|
|
@ -2037,6 +2037,10 @@ PySeqIter_Check:PyObject *:op:0:
|
|||
PySeqIter_New:PyObject*::+1:
|
||||
PySeqIter_New:PyObject*:seq:0:
|
||||
|
||||
PySentinel_New:PyObject*::+1:
|
||||
PySentinel_New:const char*:name::
|
||||
PySentinel_New:const char*:module_name::
|
||||
|
||||
PySequence_Check:int:::
|
||||
PySequence_Check:PyObject*:o:0:
|
||||
|
||||
|
|
|
|||
|
|
@ -19,13 +19,13 @@ are always available. They are listed here in alphabetical order.
|
|||
| | :func:`ascii` | | :func:`filter` | | :func:`map` | | **S** |
|
||||
| | | | :func:`float` | | :func:`max` | | |func-set|_ |
|
||||
| | **B** | | :func:`format` | | |func-memoryview|_ | | :func:`setattr` |
|
||||
| | :func:`bin` | | |func-frozenset|_ | | :func:`min` | | :func:`slice` |
|
||||
| | :func:`bool` | | | | | | :func:`sorted` |
|
||||
| | :func:`breakpoint` | | **G** | | **N** | | :func:`staticmethod` |
|
||||
| | |func-bytearray|_ | | :func:`getattr` | | :func:`next` | | |func-str|_ |
|
||||
| | |func-bytes|_ | | :func:`globals` | | | | :func:`sum` |
|
||||
| | | | | | **O** | | :func:`super` |
|
||||
| | **C** | | **H** | | :func:`object` | | |
|
||||
| | :func:`bin` | | |func-frozenset|_ | | :func:`min` | | :func:`sentinel` |
|
||||
| | :func:`bool` | | | | | | :func:`slice` |
|
||||
| | :func:`breakpoint` | | **G** | | **N** | | :func:`sorted` |
|
||||
| | |func-bytearray|_ | | :func:`getattr` | | :func:`next` | | :func:`staticmethod` |
|
||||
| | |func-bytes|_ | | :func:`globals` | | | | |func-str|_ |
|
||||
| | | | | | **O** | | :func:`sum` |
|
||||
| | **C** | | **H** | | :func:`object` | | :func:`super` |
|
||||
| | :func:`callable` | | :func:`hasattr` | | :func:`oct` | | **T** |
|
||||
| | :func:`chr` | | :func:`hash` | | :func:`open` | | |func-tuple|_ |
|
||||
| | :func:`classmethod` | | :func:`help` | | :func:`ord` | | :func:`type` |
|
||||
|
|
@ -1827,6 +1827,61 @@ are always available. They are listed here in alphabetical order.
|
|||
:func:`setattr`.
|
||||
|
||||
|
||||
.. class:: sentinel(name, /)
|
||||
|
||||
Return a new unique sentinel object. *name* must be a :class:`str`, and is
|
||||
used as the returned object's representation::
|
||||
|
||||
>>> MISSING = sentinel("MISSING")
|
||||
>>> MISSING
|
||||
MISSING
|
||||
|
||||
Sentinel objects are truthy and compare equal only to themselves. They are
|
||||
intended to be compared with the :keyword:`is` operator.
|
||||
|
||||
Shallow and deep copies of a sentinel object return the object itself.
|
||||
|
||||
Sentinels are conventionally assigned to a variable with a matching name.
|
||||
Sentinels defined in this way can be used in :term:`type hints <type hint>`::
|
||||
|
||||
MISSING = sentinel("MISSING")
|
||||
|
||||
def next_value(default: int | MISSING = MISSING):
|
||||
...
|
||||
|
||||
Sentinel objects support the :ref:`| <bitwise>` operator for use in type expressions.
|
||||
|
||||
:mod:`Pickling <pickle>` is supported for sentinel objects that are
|
||||
placed in the global scope of a module under a name matching the sentinel's
|
||||
name, and for sentinels placed in class scopes with a name matching the
|
||||
:term:`qualified name` of the sentinel. Other sentinels, such as those
|
||||
defined in a function scope, are not picklable. The identity of the sentinel is preserved
|
||||
after pickling::
|
||||
|
||||
import pickle
|
||||
|
||||
PICKLABLE = sentinel("PICKLABLE")
|
||||
|
||||
assert pickle.loads(pickle.dumps(PICKLABLE)) is PICKLABLE
|
||||
|
||||
class Cls:
|
||||
PICKLABLE = sentinel("Cls.PICKLABLE")
|
||||
|
||||
assert pickle.loads(pickle.dumps(Cls.PICKLABLE)) is Cls.PICKLABLE
|
||||
|
||||
Sentinel objects have the following attributes:
|
||||
|
||||
.. attribute:: __name__
|
||||
|
||||
The sentinel's name.
|
||||
|
||||
.. attribute:: __module__
|
||||
|
||||
The name of the module where the sentinel was created.
|
||||
|
||||
.. versionadded:: next
|
||||
|
||||
|
||||
.. class:: slice(stop, /)
|
||||
slice(start, stop, step=None, /)
|
||||
|
||||
|
|
|
|||
|
|
@ -69,6 +69,8 @@ Summary -- Release highlights
|
|||
<whatsnew315-lazy-imports>`
|
||||
* :pep:`814`: :ref:`Add frozendict built-in type
|
||||
<whatsnew315-frozendict>`
|
||||
* :pep:`661`: :ref:`Add sentinel built-in type
|
||||
<whatsnew315-sentinel>`
|
||||
* :pep:`799`: :ref:`A dedicated profiling package for organizing Python
|
||||
profiling tools <whatsnew315-profiling-package>`
|
||||
* :pep:`799`: :ref:`Tachyon: High frequency statistical sampling profiler
|
||||
|
|
@ -247,6 +249,20 @@ to accept also other mapping types such as :class:`~types.MappingProxyType`.
|
|||
(Contributed by Victor Stinner and Donghee Na in :gh:`141510`.)
|
||||
|
||||
|
||||
.. _whatsnew315-sentinel:
|
||||
|
||||
:pep:`661`: Add sentinel built-in type
|
||||
--------------------------------------
|
||||
|
||||
A new :class:`sentinel` type is added to the :mod:`builtins` module for
|
||||
creating unique sentinel values with a concise representation. Sentinel
|
||||
objects preserve identity when copied, support use in type expressions with
|
||||
the ``|`` operator, and can be pickled when they are importable by module and
|
||||
name.
|
||||
|
||||
(PEP by Tal Einat; contributed by Jelle Zijlstra in :gh:`148829`.)
|
||||
|
||||
|
||||
.. _whatsnew315-profiling-package:
|
||||
|
||||
:pep:`799`: A dedicated profiling package
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ __pragma(warning(disable: 4201))
|
|||
#include "cpython/genobject.h"
|
||||
#include "descrobject.h"
|
||||
#include "genericaliasobject.h"
|
||||
#include "sentinelobject.h"
|
||||
#include "warnings.h"
|
||||
#include "weakrefobject.h"
|
||||
#include "structseq.h"
|
||||
|
|
|
|||
22
Include/sentinelobject.h
Normal file
22
Include/sentinelobject.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/* Sentinel object interface */
|
||||
|
||||
#ifndef Py_SENTINELOBJECT_H
|
||||
#define Py_SENTINELOBJECT_H
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef Py_LIMITED_API
|
||||
PyAPI_DATA(PyTypeObject) PySentinel_Type;
|
||||
|
||||
#define PySentinel_Check(op) Py_IS_TYPE((op), &PySentinel_Type)
|
||||
|
||||
PyAPI_FUNC(PyObject *) PySentinel_New(
|
||||
const char *name,
|
||||
const char *module_name);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* !Py_SENTINELOBJECT_H */
|
||||
|
|
@ -3244,6 +3244,7 @@ def test_builtin_types(self):
|
|||
'BuiltinImporter': (3, 3),
|
||||
'str': (3, 4), # not interoperable with Python < 3.4
|
||||
'frozendict': (3, 15),
|
||||
'sentinel': (3, 15),
|
||||
}
|
||||
for t in builtins.__dict__.values():
|
||||
if isinstance(t, type) and not issubclass(t, BaseException):
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
import builtins
|
||||
import collections
|
||||
import contextlib
|
||||
import copy
|
||||
import decimal
|
||||
import fractions
|
||||
import gc
|
||||
|
|
@ -21,6 +22,7 @@
|
|||
import typing
|
||||
import unittest
|
||||
import warnings
|
||||
import weakref
|
||||
from contextlib import ExitStack
|
||||
from functools import partial
|
||||
from inspect import CO_COROUTINE
|
||||
|
|
@ -52,6 +54,10 @@
|
|||
|
||||
# used as proof of globals being used
|
||||
A_GLOBAL_VALUE = 123
|
||||
A_SENTINEL = sentinel("A_SENTINEL")
|
||||
|
||||
class SentinelContainer:
|
||||
CLASS_SENTINEL = sentinel("SentinelContainer.CLASS_SENTINEL")
|
||||
|
||||
class Squares:
|
||||
|
||||
|
|
@ -1903,6 +1909,98 @@ class C:
|
|||
__repr__ = None
|
||||
self.assertRaises(TypeError, repr, C())
|
||||
|
||||
def test_sentinel(self):
|
||||
missing = sentinel("MISSING")
|
||||
other = sentinel("MISSING")
|
||||
|
||||
self.assertIsInstance(missing, sentinel)
|
||||
self.assertIs(type(missing), sentinel)
|
||||
self.assertEqual(missing.__name__, "MISSING")
|
||||
self.assertEqual(missing.__module__, __name__)
|
||||
self.assertIsNot(missing, other)
|
||||
self.assertEqual(repr(missing), "MISSING")
|
||||
self.assertTrue(missing)
|
||||
self.assertIs(copy.copy(missing), missing)
|
||||
self.assertIs(copy.deepcopy(missing), missing)
|
||||
self.assertEqual(missing, missing)
|
||||
self.assertNotEqual(missing, other)
|
||||
self.assertRaises(TypeError, sentinel)
|
||||
self.assertRaises(TypeError, sentinel, "MISSING", "EXTRA")
|
||||
self.assertRaises(TypeError, sentinel, name="MISSING")
|
||||
with self.assertRaisesRegex(TypeError, "must be str"):
|
||||
sentinel(1)
|
||||
self.assertTrue(sentinel.__flags__ & support._TPFLAGS_IMMUTABLETYPE)
|
||||
self.assertTrue(sentinel.__flags__ & support._TPFLAGS_HAVE_GC)
|
||||
self.assertFalse(sentinel.__flags__ & support._TPFLAGS_BASETYPE)
|
||||
with self.assertRaises(TypeError):
|
||||
class SubSentinel(sentinel):
|
||||
pass
|
||||
with self.assertRaises(TypeError):
|
||||
sentinel.attribute = "value"
|
||||
with self.assertRaises(AttributeError):
|
||||
missing.__name__ = "CHANGED"
|
||||
with self.assertRaises(AttributeError):
|
||||
missing.__module__ = "changed"
|
||||
with self.assertRaises(AttributeError):
|
||||
del missing.__name__
|
||||
with self.assertRaises(AttributeError):
|
||||
del missing.__module__
|
||||
|
||||
def test_sentinel_pickle(self):
|
||||
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
|
||||
with self.subTest(protocol=proto):
|
||||
self.assertIs(
|
||||
pickle.loads(pickle.dumps(A_SENTINEL, protocol=proto)),
|
||||
A_SENTINEL)
|
||||
self.assertIs(
|
||||
pickle.loads(pickle.dumps(
|
||||
SentinelContainer.CLASS_SENTINEL, protocol=proto)),
|
||||
SentinelContainer.CLASS_SENTINEL)
|
||||
|
||||
missing = sentinel("MISSING")
|
||||
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
|
||||
with self.subTest(protocol=proto):
|
||||
with self.assertRaises(pickle.PicklingError):
|
||||
pickle.dumps(missing, protocol=proto)
|
||||
|
||||
def test_sentinel_str_subclass_name_cycle(self):
|
||||
class Name(str):
|
||||
pass
|
||||
|
||||
name = Name("MISSING")
|
||||
missing = sentinel(name)
|
||||
self.assertIs(missing.__name__, name)
|
||||
self.assertTrue(gc.is_tracked(missing))
|
||||
|
||||
name.missing = missing
|
||||
ref = weakref.ref(name)
|
||||
del name, missing
|
||||
support.gc_collect()
|
||||
self.assertIsNone(ref())
|
||||
|
||||
def test_sentinel_union(self):
|
||||
missing = sentinel("MISSING")
|
||||
|
||||
self.assertIsInstance(missing | int, typing.Union)
|
||||
self.assertEqual((missing | int).__args__, (missing, int))
|
||||
self.assertIsInstance(int | missing, typing.Union)
|
||||
self.assertEqual((int | missing).__args__, (int, missing))
|
||||
self.assertIs(missing | missing, missing)
|
||||
self.assertEqual(repr(int | missing), "int | MISSING")
|
||||
self.assertIsInstance(missing | None, typing.Union)
|
||||
self.assertEqual((missing | None).__args__, (missing, type(None)))
|
||||
self.assertIsInstance(None | missing, typing.Union)
|
||||
self.assertEqual((None | missing).__args__, (type(None), missing))
|
||||
self.assertIsInstance(missing | list[int], typing.Union)
|
||||
self.assertEqual((missing | list[int]).__args__, (missing, list[int]))
|
||||
self.assertIsInstance(missing | (int | str), typing.Union)
|
||||
self.assertEqual((missing | (int | str)).__args__, (missing, int, str))
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
missing | 1
|
||||
with self.assertRaises(TypeError):
|
||||
1 | missing
|
||||
|
||||
def test_round(self):
|
||||
self.assertEqual(round(0.0), 0.0)
|
||||
self.assertEqual(type(round(0.0)), int)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import enum
|
||||
import os
|
||||
import pickle
|
||||
import sys
|
||||
import textwrap
|
||||
import unittest
|
||||
|
|
@ -63,6 +64,27 @@ def test_get_constant_borrowed(self):
|
|||
self.check_get_constant(_testlimitedcapi.get_constant_borrowed)
|
||||
|
||||
|
||||
class SentinelTest(unittest.TestCase):
|
||||
|
||||
def test_pysentinel_new(self):
|
||||
marker = _testcapi.pysentinel_new("CAPI_SENTINEL", __name__)
|
||||
self.assertIs(type(marker), sentinel)
|
||||
self.assertTrue(_testcapi.pysentinel_check(marker))
|
||||
self.assertFalse(_testcapi.pysentinel_check(object()))
|
||||
self.assertEqual(marker.__name__, "CAPI_SENTINEL")
|
||||
self.assertEqual(marker.__module__, __name__)
|
||||
self.assertEqual(repr(marker), "CAPI_SENTINEL")
|
||||
|
||||
no_module = _testcapi.pysentinel_new("NO_MODULE")
|
||||
self.assertIs(type(no_module), sentinel)
|
||||
self.assertEqual(no_module.__name__, "NO_MODULE")
|
||||
self.assertIs(no_module.__module__, None)
|
||||
|
||||
globals()["CAPI_SENTINEL"] = marker
|
||||
self.addCleanup(globals().pop, "CAPI_SENTINEL", None)
|
||||
self.assertIs(pickle.loads(pickle.dumps(marker)), marker)
|
||||
|
||||
|
||||
class PrintTest(unittest.TestCase):
|
||||
def testPyObjectPrintObject(self):
|
||||
|
||||
|
|
|
|||
|
|
@ -3150,31 +3150,7 @@ def _namedtuple_mro_entries(bases):
|
|||
NamedTuple.__mro_entries__ = _namedtuple_mro_entries
|
||||
|
||||
|
||||
class _SingletonMeta(type):
|
||||
def __setattr__(cls, attr, value):
|
||||
# TypeError is consistent with the behavior of NoneType
|
||||
raise TypeError(
|
||||
f"cannot set {attr!r} attribute of immutable type {cls.__name__!r}"
|
||||
)
|
||||
|
||||
|
||||
class _NoExtraItemsType(metaclass=_SingletonMeta):
|
||||
"""The type of the NoExtraItems singleton."""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __new__(cls):
|
||||
return globals().get("NoExtraItems") or object.__new__(cls)
|
||||
|
||||
def __repr__(self):
|
||||
return 'typing.NoExtraItems'
|
||||
|
||||
def __reduce__(self):
|
||||
return 'NoExtraItems'
|
||||
|
||||
NoExtraItems = _NoExtraItemsType()
|
||||
del _NoExtraItemsType
|
||||
del _SingletonMeta
|
||||
NoExtraItems = sentinel("NoExtraItems")
|
||||
|
||||
|
||||
def _get_typeddict_qualifiers(annotation_type):
|
||||
|
|
|
|||
|
|
@ -560,6 +560,7 @@ OBJECT_OBJS= \
|
|||
Objects/obmalloc.o \
|
||||
Objects/picklebufobject.o \
|
||||
Objects/rangeobject.o \
|
||||
Objects/sentinelobject.o \
|
||||
Objects/setobject.o \
|
||||
Objects/sliceobject.o \
|
||||
Objects/structseq.o \
|
||||
|
|
@ -1240,6 +1241,7 @@ PYTHON_HEADERS= \
|
|||
$(srcdir)/Include/pytypedefs.h \
|
||||
$(srcdir)/Include/rangeobject.h \
|
||||
$(srcdir)/Include/refcount.h \
|
||||
$(srcdir)/Include/sentinelobject.h \
|
||||
$(srcdir)/Include/setobject.h \
|
||||
$(srcdir)/Include/sliceobject.h \
|
||||
$(srcdir)/Include/structmember.h \
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
Add :class:`sentinel`, implementing :pep:`661`. PEP by Tal Einat; patch by
|
||||
Jelle Zijlstra.
|
||||
|
|
@ -555,6 +555,23 @@ pyobject_dump(PyObject *self, PyObject *args)
|
|||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
pysentinel_new(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char *name;
|
||||
const char *module_name = NULL;
|
||||
if (!PyArg_ParseTuple(args, "s|s", &name, &module_name)) {
|
||||
return NULL;
|
||||
}
|
||||
return PySentinel_New(name, module_name);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
pysentinel_check(PyObject *self, PyObject *obj)
|
||||
{
|
||||
return PyBool_FromLong(PySentinel_Check(obj));
|
||||
}
|
||||
|
||||
|
||||
static PyMethodDef test_methods[] = {
|
||||
{"call_pyobject_print", call_pyobject_print, METH_VARARGS},
|
||||
|
|
@ -585,6 +602,8 @@ static PyMethodDef test_methods[] = {
|
|||
{"clear_managed_dict", clear_managed_dict, METH_O, NULL},
|
||||
{"is_uniquely_referenced", is_uniquely_referenced, METH_O},
|
||||
{"pyobject_dump", pyobject_dump, METH_VARARGS},
|
||||
{"pysentinel_new", pysentinel_new, METH_VARARGS},
|
||||
{"pysentinel_check", pysentinel_check, METH_O},
|
||||
{NULL},
|
||||
};
|
||||
|
||||
|
|
|
|||
34
Objects/clinic/sentinelobject.c.h
generated
Normal file
34
Objects/clinic/sentinelobject.c.h
generated
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/*[clinic input]
|
||||
preserve
|
||||
[clinic start generated code]*/
|
||||
|
||||
#include "pycore_modsupport.h" // _PyArg_CheckPositional()
|
||||
|
||||
static PyObject *
|
||||
sentinel_new_impl(PyTypeObject *type, PyObject *name);
|
||||
|
||||
static PyObject *
|
||||
sentinel_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
PyTypeObject *base_tp = &PySentinel_Type;
|
||||
PyObject *name;
|
||||
|
||||
if ((type == base_tp || type->tp_init == base_tp->tp_init) &&
|
||||
!_PyArg_NoKeywords("sentinel", kwargs)) {
|
||||
goto exit;
|
||||
}
|
||||
if (!_PyArg_CheckPositional("sentinel", PyTuple_GET_SIZE(args), 1, 1)) {
|
||||
goto exit;
|
||||
}
|
||||
if (!PyUnicode_Check(PyTuple_GET_ITEM(args, 0))) {
|
||||
_PyArg_BadArgument("sentinel", "argument 1", "str", PyTuple_GET_ITEM(args, 0));
|
||||
goto exit;
|
||||
}
|
||||
name = PyTuple_GET_ITEM(args, 0);
|
||||
return_value = sentinel_new_impl(type, name);
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
}
|
||||
/*[clinic end generated code: output=7f28fc0bf0259cba input=a9049054013a1b77]*/
|
||||
|
|
@ -2597,6 +2597,7 @@ static PyTypeObject* static_types[] = {
|
|||
&PyRange_Type,
|
||||
&PyReversed_Type,
|
||||
&PySTEntry_Type,
|
||||
&PySentinel_Type,
|
||||
&PySeqIter_Type,
|
||||
&PySetIter_Type,
|
||||
&PySet_Type,
|
||||
|
|
|
|||
196
Objects/sentinelobject.c
Normal file
196
Objects/sentinelobject.c
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
/* Sentinel object implementation */
|
||||
|
||||
#include "Python.h"
|
||||
#include "descrobject.h" // PyMemberDef
|
||||
#include "pycore_ceval.h" // _PyThreadState_GET()
|
||||
#include "pycore_interpframe.h" // _PyFrame_IsIncomplete()
|
||||
#include "pycore_object.h" // _PyObject_GC_TRACK/UNTRACK()
|
||||
#include "pycore_stackref.h" // PyStackRef_AsPyObjectBorrow()
|
||||
#include "pycore_tuple.h" // _PyTuple_FromPair
|
||||
#include "pycore_typeobject.h" // _Py_BaseObject_RichCompare()
|
||||
#include "pycore_unionobject.h" // _Py_union_type_or()
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
PyObject *name;
|
||||
PyObject *module;
|
||||
} sentinelobject;
|
||||
|
||||
#define sentinelobject_CAST(op) \
|
||||
(assert(PySentinel_Check(op)), _Py_CAST(sentinelobject *, (op)))
|
||||
|
||||
/*[clinic input]
|
||||
class sentinel "sentinelobject *" "&PySentinel_Type"
|
||||
[clinic start generated code]*/
|
||||
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=8b88f8268d3b5775]*/
|
||||
|
||||
#include "clinic/sentinelobject.c.h"
|
||||
|
||||
|
||||
static PyObject *
|
||||
caller(void)
|
||||
{
|
||||
_PyInterpreterFrame *f = _PyThreadState_GET()->current_frame;
|
||||
if (f == NULL || PyStackRef_IsNull(f->f_funcobj)) {
|
||||
assert(!PyErr_Occurred());
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
PyFunctionObject *func = _PyFrame_GetFunction(f);
|
||||
assert(PyFunction_Check(func));
|
||||
PyObject *r = PyFunction_GetModule((PyObject *)func);
|
||||
if (!r) {
|
||||
assert(!PyErr_Occurred());
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
return Py_NewRef(r);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
sentinel_new_with_module(PyTypeObject *type, PyObject *name, PyObject *module)
|
||||
{
|
||||
assert(PyUnicode_Check(name));
|
||||
|
||||
sentinelobject *self = PyObject_GC_New(sentinelobject, type);
|
||||
if (self == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
self->name = Py_NewRef(name);
|
||||
self->module = Py_NewRef(module);
|
||||
_PyObject_GC_TRACK(self);
|
||||
return (PyObject *)self;
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
@classmethod
|
||||
sentinel.__new__ as sentinel_new
|
||||
|
||||
name: object(subclass_of='&PyUnicode_Type')
|
||||
/
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
sentinel_new_impl(PyTypeObject *type, PyObject *name)
|
||||
/*[clinic end generated code: output=4af55c6048bed30d input=3ab75704f39c119c]*/
|
||||
{
|
||||
PyObject *module = caller();
|
||||
PyObject *self = sentinel_new_with_module(type, name, module);
|
||||
Py_DECREF(module);
|
||||
return self;
|
||||
}
|
||||
|
||||
PyObject *
|
||||
PySentinel_New(const char *name, const char *module_name)
|
||||
{
|
||||
PyObject *name_obj = PyUnicode_FromString(name);
|
||||
if (name_obj == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
PyObject *module_obj = module_name == NULL
|
||||
? Py_None
|
||||
: PyUnicode_FromString(module_name);
|
||||
if (module_obj == NULL) {
|
||||
Py_DECREF(name_obj);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *sentinel = sentinel_new_with_module(
|
||||
&PySentinel_Type, name_obj, module_obj);
|
||||
Py_DECREF(module_obj);
|
||||
Py_DECREF(name_obj);
|
||||
return sentinel;
|
||||
}
|
||||
|
||||
static int
|
||||
sentinel_clear(PyObject *op)
|
||||
{
|
||||
sentinelobject *self = sentinelobject_CAST(op);
|
||||
Py_CLEAR(self->name);
|
||||
Py_CLEAR(self->module);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
sentinel_dealloc(PyObject *op)
|
||||
{
|
||||
_PyObject_GC_UNTRACK(op);
|
||||
(void)sentinel_clear(op);
|
||||
Py_TYPE(op)->tp_free(op);
|
||||
}
|
||||
|
||||
static int
|
||||
sentinel_traverse(PyObject *op, visitproc visit, void *arg)
|
||||
{
|
||||
sentinelobject *self = sentinelobject_CAST(op);
|
||||
Py_VISIT(self->name);
|
||||
Py_VISIT(self->module);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
sentinel_repr(PyObject *op)
|
||||
{
|
||||
sentinelobject *self = sentinelobject_CAST(op);
|
||||
return Py_NewRef(self->name);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
sentinel_copy(PyObject *self, PyObject *Py_UNUSED(ignored))
|
||||
{
|
||||
return Py_NewRef(self);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
sentinel_deepcopy(PyObject *self, PyObject *Py_UNUSED(memo))
|
||||
{
|
||||
return Py_NewRef(self);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
sentinel_reduce(PyObject *op, PyObject *Py_UNUSED(ignored))
|
||||
{
|
||||
sentinelobject *self = sentinelobject_CAST(op);
|
||||
return Py_NewRef(self->name);
|
||||
}
|
||||
|
||||
static PyMethodDef sentinel_methods[] = {
|
||||
{"__copy__", sentinel_copy, METH_NOARGS, NULL},
|
||||
{"__deepcopy__", sentinel_deepcopy, METH_O, NULL},
|
||||
{"__reduce__", sentinel_reduce, METH_NOARGS, NULL},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static PyMemberDef sentinel_members[] = {
|
||||
{"__name__", Py_T_OBJECT_EX, offsetof(sentinelobject, name), Py_READONLY},
|
||||
{"__module__", Py_T_OBJECT_EX, offsetof(sentinelobject, module), Py_READONLY},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
static PyNumberMethods sentinel_as_number = {
|
||||
.nb_or = _Py_union_type_or,
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(sentinel_doc,
|
||||
"sentinel(name, /)\n"
|
||||
"--\n\n"
|
||||
"Create a unique sentinel object with the given name.");
|
||||
|
||||
PyTypeObject PySentinel_Type = {
|
||||
PyVarObject_HEAD_INIT(&PyType_Type, 0)
|
||||
.tp_name = "sentinel",
|
||||
.tp_basicsize = sizeof(sentinelobject),
|
||||
.tp_dealloc = sentinel_dealloc,
|
||||
.tp_repr = sentinel_repr,
|
||||
.tp_as_number = &sentinel_as_number,
|
||||
.tp_hash = PyObject_GenericHash,
|
||||
.tp_getattro = PyObject_GenericGetAttr,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE
|
||||
| Py_TPFLAGS_HAVE_GC,
|
||||
.tp_doc = sentinel_doc,
|
||||
.tp_traverse = sentinel_traverse,
|
||||
.tp_clear = sentinel_clear,
|
||||
.tp_richcompare = _Py_BaseObject_RichCompare,
|
||||
.tp_methods = sentinel_methods,
|
||||
.tp_members = sentinel_members,
|
||||
.tp_new = sentinel_new,
|
||||
.tp_free = PyObject_GC_Del,
|
||||
};
|
||||
|
|
@ -245,6 +245,7 @@ is_unionable(PyObject *obj)
|
|||
{
|
||||
if (obj == Py_None ||
|
||||
PyType_Check(obj) ||
|
||||
PySentinel_Check(obj) ||
|
||||
_PyGenericAlias_Check(obj) ||
|
||||
_PyUnion_Check(obj) ||
|
||||
Py_IS_TYPE(obj, &_PyTypeAlias_Type)) {
|
||||
|
|
|
|||
|
|
@ -158,6 +158,7 @@
|
|||
<ClCompile Include="..\Objects\odictobject.c" />
|
||||
<ClCompile Include="..\Objects\picklebufobject.c" />
|
||||
<ClCompile Include="..\Objects\rangeobject.c" />
|
||||
<ClCompile Include="..\Objects\sentinelobject.c" />
|
||||
<ClCompile Include="..\Objects\setobject.c" />
|
||||
<ClCompile Include="..\Objects\sliceobject.c" />
|
||||
<ClCompile Include="..\Objects\structseq.c" />
|
||||
|
|
|
|||
|
|
@ -400,6 +400,9 @@
|
|||
<ClCompile Include="..\Objects\rangeobject.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Objects\sentinelobject.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Objects\setobject.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
|
|
|||
|
|
@ -384,6 +384,7 @@
|
|||
<ClInclude Include="..\Include\pytypedefs.h" />
|
||||
<ClInclude Include="..\Include\rangeobject.h" />
|
||||
<ClInclude Include="..\Include\refcount.h" />
|
||||
<ClInclude Include="..\Include\sentinelobject.h" />
|
||||
<ClInclude Include="..\Include\setobject.h" />
|
||||
<ClInclude Include="..\Include\sliceobject.h" />
|
||||
<ClInclude Include="..\Include\structmember.h" />
|
||||
|
|
@ -561,6 +562,7 @@
|
|||
<ClCompile Include="..\Objects\odictobject.c" />
|
||||
<ClCompile Include="..\Objects\picklebufobject.c" />
|
||||
<ClCompile Include="..\Objects\rangeobject.c" />
|
||||
<ClCompile Include="..\Objects\sentinelobject.c" />
|
||||
<ClCompile Include="..\Objects\setobject.c" />
|
||||
<ClCompile Include="..\Objects\sliceobject.c" />
|
||||
<ClCompile Include="..\Objects\structseq.c" />
|
||||
|
|
|
|||
|
|
@ -222,6 +222,9 @@
|
|||
<ClInclude Include="..\Include\runtime_structs.h">
|
||||
<Filter>Include</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Include\sentinelobject.h">
|
||||
<Filter>Include</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Include\setobject.h">
|
||||
<Filter>Include</Filter>
|
||||
</ClInclude>
|
||||
|
|
@ -1274,6 +1277,9 @@
|
|||
<ClCompile Include="..\Objects\rangeobject.c">
|
||||
<Filter>Objects</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Objects\sentinelobject.c">
|
||||
<Filter>Objects</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Objects\setobject.c">
|
||||
<Filter>Objects</Filter>
|
||||
</ClCompile>
|
||||
|
|
|
|||
|
|
@ -3555,6 +3555,7 @@ _PyBuiltin_Init(PyInterpreterState *interp)
|
|||
SETBUILTIN("object", &PyBaseObject_Type);
|
||||
SETBUILTIN("range", &PyRange_Type);
|
||||
SETBUILTIN("reversed", &PyReversed_Type);
|
||||
SETBUILTIN("sentinel", &PySentinel_Type);
|
||||
SETBUILTIN("set", &PySet_Type);
|
||||
SETBUILTIN("slice", &PySlice_Type);
|
||||
SETBUILTIN("staticmethod", &PyStaticMethod_Type);
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ Objects/picklebufobject.c - PyPickleBuffer_Type -
|
|||
Objects/rangeobject.c - PyLongRangeIter_Type -
|
||||
Objects/rangeobject.c - PyRangeIter_Type -
|
||||
Objects/rangeobject.c - PyRange_Type -
|
||||
Objects/sentinelobject.c - PySentinel_Type -
|
||||
Objects/setobject.c - PyFrozenSet_Type -
|
||||
Objects/setobject.c - PySetIter_Type -
|
||||
Objects/setobject.c - PySet_Type -
|
||||
|
|
|
|||
|
Can't render this file because it has a wrong number of fields in line 4.
|
Loading…
Add table
Add a link
Reference in a new issue