Coverage Report

Created: 2026-05-16 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpython/Objects/sentinelobject.c
Line
Count
Source
1
/* Sentinel object implementation */
2
3
#include "Python.h"
4
#include "descrobject.h"          // PyMemberDef
5
#include "pycore_ceval.h"         // _PyThreadState_GET()
6
#include "pycore_interpframe.h"   // _PyFrame_IsIncomplete()
7
#include "pycore_object.h"        // _PyObject_GC_TRACK/UNTRACK()
8
#include "pycore_stackref.h"      // PyStackRef_AsPyObjectBorrow()
9
#include "pycore_tuple.h"         // _PyTuple_FromPair
10
#include "pycore_typeobject.h"    // _Py_BaseObject_RichCompare()
11
#include "pycore_unionobject.h"   // _Py_union_type_or()
12
13
typedef struct {
14
    PyObject_HEAD
15
    PyObject *name;
16
    PyObject *module;
17
} sentinelobject;
18
19
#define sentinelobject_CAST(op) \
20
1.43k
    (assert(PySentinel_Check(op)), _Py_CAST(sentinelobject *, (op)))
21
22
/*[clinic input]
23
class sentinel "sentinelobject *" "&PySentinel_Type"
24
[clinic start generated code]*/
25
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=8b88f8268d3b5775]*/
26
27
#include "clinic/sentinelobject.c.h"
28
29
30
static PyObject *
31
caller(void)
32
47
{
33
47
    _PyInterpreterFrame *f = _PyThreadState_GET()->current_frame;
34
47
    if (f == NULL || PyStackRef_IsNull(f->f_funcobj)) {
35
0
        assert(!PyErr_Occurred());
36
0
        Py_RETURN_NONE;
37
0
    }
38
47
    PyFunctionObject *func = _PyFrame_GetFunction(f);
39
47
    assert(PyFunction_Check(func));
40
47
    PyObject *r = PyFunction_GetModule((PyObject *)func);
41
47
    if (!r) {
42
0
        assert(!PyErr_Occurred());
43
0
        Py_RETURN_NONE;
44
0
    }
45
47
    return Py_NewRef(r);
46
47
}
47
48
static PyObject *
49
sentinel_new_with_module(PyTypeObject *type, PyObject *name, PyObject *module)
50
47
{
51
47
    assert(PyUnicode_Check(name));
52
53
47
    sentinelobject *self = PyObject_GC_New(sentinelobject, type);
54
47
    if (self == NULL) {
55
0
        return NULL;
56
0
    }
57
47
    self->name = Py_NewRef(name);
58
47
    self->module = Py_NewRef(module);
59
47
    _PyObject_GC_TRACK(self);
60
47
    return (PyObject *)self;
61
47
}
62
63
/*[clinic input]
64
@classmethod
65
sentinel.__new__ as sentinel_new
66
67
    name: object(subclass_of='&PyUnicode_Type')
68
    /
69
[clinic start generated code]*/
70
71
static PyObject *
72
sentinel_new_impl(PyTypeObject *type, PyObject *name)
73
/*[clinic end generated code: output=4af55c6048bed30d input=3ab75704f39c119c]*/
74
47
{
75
47
    PyObject *module = caller();
76
47
    PyObject *self = sentinel_new_with_module(type, name, module);
77
47
    Py_DECREF(module);
78
47
    return self;
79
47
}
80
81
PyObject *
82
PySentinel_New(const char *name, const char *module_name)
83
0
{
84
0
    PyObject *name_obj = PyUnicode_FromString(name);
85
0
    if (name_obj == NULL) {
86
0
        return NULL;
87
0
    }
88
0
    PyObject *module_obj = module_name == NULL
89
0
        ? Py_None
90
0
        : PyUnicode_FromString(module_name);
91
0
    if (module_obj == NULL) {
92
0
        Py_DECREF(name_obj);
93
0
        return NULL;
94
0
    }
95
96
0
    PyObject *sentinel = sentinel_new_with_module(
97
0
        &PySentinel_Type, name_obj, module_obj);
98
0
    Py_DECREF(module_obj);
99
0
    Py_DECREF(name_obj);
100
0
    return sentinel;
101
0
}
102
103
static int
104
sentinel_clear(PyObject *op)
105
0
{
106
0
    sentinelobject *self = sentinelobject_CAST(op);
107
0
    Py_CLEAR(self->name);
108
0
    Py_CLEAR(self->module);
109
0
    return 0;
110
0
}
111
112
static void
113
sentinel_dealloc(PyObject *op)
114
0
{
115
0
    _PyObject_GC_UNTRACK(op);
116
0
    (void)sentinel_clear(op);
117
0
    Py_TYPE(op)->tp_free(op);
118
0
}
119
120
static int
121
sentinel_traverse(PyObject *op, visitproc visit, void *arg)
122
1.43k
{
123
1.43k
    sentinelobject *self = sentinelobject_CAST(op);
124
1.43k
    Py_VISIT(self->name);
125
1.43k
    Py_VISIT(self->module);
126
1.43k
    return 0;
127
1.43k
}
128
129
static PyObject *
130
sentinel_repr(PyObject *op)
131
0
{
132
0
    sentinelobject *self = sentinelobject_CAST(op);
133
0
    return Py_NewRef(self->name);
134
0
}
135
136
static PyObject *
137
sentinel_copy(PyObject *self, PyObject *Py_UNUSED(ignored))
138
0
{
139
0
    return Py_NewRef(self);
140
0
}
141
142
static PyObject *
143
sentinel_deepcopy(PyObject *self, PyObject *Py_UNUSED(memo))
144
0
{
145
0
    return Py_NewRef(self);
146
0
}
147
148
static PyObject *
149
sentinel_reduce(PyObject *op, PyObject *Py_UNUSED(ignored))
150
0
{
151
0
    sentinelobject *self = sentinelobject_CAST(op);
152
0
    return Py_NewRef(self->name);
153
0
}
154
155
static PyMethodDef sentinel_methods[] = {
156
    {"__copy__", sentinel_copy, METH_NOARGS, NULL},
157
    {"__deepcopy__", sentinel_deepcopy, METH_O, NULL},
158
    {"__reduce__", sentinel_reduce, METH_NOARGS, NULL},
159
    {NULL, NULL}
160
};
161
162
static PyMemberDef sentinel_members[] = {
163
    {"__name__", Py_T_OBJECT_EX, offsetof(sentinelobject, name), Py_READONLY},
164
    {"__module__", Py_T_OBJECT_EX, offsetof(sentinelobject, module), Py_READONLY},
165
    {NULL}
166
};
167
168
static PyNumberMethods sentinel_as_number = {
169
    .nb_or = _Py_union_type_or,
170
};
171
172
PyDoc_STRVAR(sentinel_doc,
173
"sentinel(name, /)\n"
174
"--\n\n"
175
"Create a unique sentinel object with the given name.");
176
177
PyTypeObject PySentinel_Type = {
178
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
179
    .tp_name = "sentinel",
180
    .tp_basicsize = sizeof(sentinelobject),
181
    .tp_dealloc = sentinel_dealloc,
182
    .tp_repr = sentinel_repr,
183
    .tp_as_number = &sentinel_as_number,
184
    .tp_hash = PyObject_GenericHash,
185
    .tp_getattro = PyObject_GenericGetAttr,
186
    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE
187
                | Py_TPFLAGS_HAVE_GC,
188
    .tp_doc = sentinel_doc,
189
    .tp_traverse = sentinel_traverse,
190
    .tp_clear = sentinel_clear,
191
    .tp_richcompare = _Py_BaseObject_RichCompare,
192
    .tp_methods = sentinel_methods,
193
    .tp_members = sentinel_members,
194
    .tp_new = sentinel_new,
195
    .tp_free = PyObject_GC_Del,
196
};