Coverage Report

Created: 2026-05-30 06:18

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
    PyObject *repr;
18
} sentinelobject;
19
20
#define sentinelobject_CAST(op) \
21
1.47k
    (assert(PySentinel_Check(op)), _Py_CAST(sentinelobject *, (op)))
22
23
/*[clinic input]
24
class sentinel "sentinelobject *" "&PySentinel_Type"
25
[clinic start generated code]*/
26
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=8b88f8268d3b5775]*/
27
28
#include "clinic/sentinelobject.c.h"
29
30
31
static PyObject *
32
caller(void)
33
47
{
34
47
    _PyInterpreterFrame *f = _PyThreadState_GET()->current_frame;
35
47
    if (f == NULL || PyStackRef_IsNull(f->f_funcobj)) {
36
0
        assert(!PyErr_Occurred());
37
0
        Py_RETURN_NONE;
38
0
    }
39
47
    PyFunctionObject *func = _PyFrame_GetFunction(f);
40
47
    assert(PyFunction_Check(func));
41
47
    PyObject *r = PyFunction_GetModule((PyObject *)func);
42
47
    if (!r) {
43
0
        assert(!PyErr_Occurred());
44
0
        Py_RETURN_NONE;
45
0
    }
46
47
    return Py_NewRef(r);
47
47
}
48
49
static PyObject *
50
sentinel_new_with_module(PyTypeObject *type, PyObject *name, PyObject *module, PyObject *repr)
51
47
{
52
47
    assert(PyUnicode_Check(name));
53
54
47
    sentinelobject *self = PyObject_GC_New(sentinelobject, type);
55
47
    if (self == NULL) {
56
0
        return NULL;
57
0
    }
58
47
    self->name = Py_NewRef(name);
59
47
    self->module = Py_NewRef(module);
60
47
    self->repr = Py_XNewRef(repr);
61
47
    _PyObject_GC_TRACK(self);
62
47
    return (PyObject *)self;
63
47
}
64
65
/*[clinic input]
66
@classmethod
67
sentinel.__new__ as sentinel_new
68
69
    name: object(subclass_of='&PyUnicode_Type')
70
    /
71
    *
72
    repr: object = None
73
[clinic start generated code]*/
74
75
static PyObject *
76
sentinel_new_impl(PyTypeObject *type, PyObject *name, PyObject *repr)
77
/*[clinic end generated code: output=1eb7fab52e57d8c8 input=28cab6c468997b35]*/
78
47
{
79
47
    if (repr == Py_None) {
80
47
        repr = NULL;
81
47
    }
82
0
    else if (!PyUnicode_Check(repr)) {
83
0
        _PyArg_BadArgument("sentinel", "argument 'repr'", "str or None", repr);
84
0
        return NULL;
85
0
    }
86
47
    PyObject *module = caller();
87
47
    PyObject *self = sentinel_new_with_module(type, name, module, repr);
88
47
    Py_DECREF(module);
89
47
    return self;
90
47
}
91
92
PyObject *
93
PySentinel_New(const char *name, const char *module_name, const char *repr)
94
0
{
95
0
    PyObject *name_obj = PyUnicode_FromString(name);
96
0
    if (name_obj == NULL) {
97
0
        return NULL;
98
0
    }
99
0
    PyObject *repr_obj = NULL;
100
0
    if (repr != NULL) {
101
0
        repr_obj = PyUnicode_FromString(repr);
102
0
        if (repr_obj == NULL) {
103
0
            Py_DECREF(name_obj);
104
0
            return NULL;
105
0
        }
106
0
    }
107
0
    PyObject *module_obj = module_name == NULL
108
0
        ? Py_None
109
0
        : PyUnicode_FromString(module_name);
110
0
    if (module_obj == NULL) {
111
0
        Py_DECREF(name_obj);
112
0
        Py_XDECREF(repr_obj);
113
0
        return NULL;
114
0
    }
115
116
0
    PyObject *sentinel = sentinel_new_with_module(
117
0
        &PySentinel_Type, name_obj, module_obj, repr_obj);
118
0
    Py_DECREF(module_obj);
119
0
    Py_DECREF(name_obj);
120
0
    Py_XDECREF(repr_obj);
121
0
    return sentinel;
122
0
}
123
124
static int
125
sentinel_clear(PyObject *op)
126
0
{
127
0
    sentinelobject *self = sentinelobject_CAST(op);
128
0
    Py_CLEAR(self->name);
129
0
    Py_CLEAR(self->module);
130
0
    Py_CLEAR(self->repr);
131
0
    return 0;
132
0
}
133
134
static void
135
sentinel_dealloc(PyObject *op)
136
0
{
137
0
    _PyObject_GC_UNTRACK(op);
138
0
    (void)sentinel_clear(op);
139
0
    Py_TYPE(op)->tp_free(op);
140
0
}
141
142
static int
143
sentinel_traverse(PyObject *op, visitproc visit, void *arg)
144
1.47k
{
145
1.47k
    sentinelobject *self = sentinelobject_CAST(op);
146
1.47k
    Py_VISIT(self->name);
147
1.47k
    Py_VISIT(self->module);
148
1.47k
    Py_VISIT(self->repr);
149
1.47k
    return 0;
150
1.47k
}
151
152
static PyObject *
153
sentinel_repr(PyObject *op)
154
0
{
155
0
    sentinelobject *self = sentinelobject_CAST(op);
156
0
    if (self->repr != NULL) {
157
0
        return Py_NewRef(self->repr);
158
0
    }
159
0
    return Py_NewRef(self->name);
160
0
}
161
162
static PyObject *
163
sentinel_copy(PyObject *self, PyObject *Py_UNUSED(ignored))
164
0
{
165
0
    return Py_NewRef(self);
166
0
}
167
168
static PyObject *
169
sentinel_deepcopy(PyObject *self, PyObject *Py_UNUSED(memo))
170
0
{
171
0
    return Py_NewRef(self);
172
0
}
173
174
static PyObject *
175
sentinel_reduce(PyObject *op, PyObject *Py_UNUSED(ignored))
176
0
{
177
0
    sentinelobject *self = sentinelobject_CAST(op);
178
0
    return Py_NewRef(self->name);
179
0
}
180
181
static PyMethodDef sentinel_methods[] = {
182
    {"__copy__", sentinel_copy, METH_NOARGS, NULL},
183
    {"__deepcopy__", sentinel_deepcopy, METH_O, NULL},
184
    {"__reduce__", sentinel_reduce, METH_NOARGS, NULL},
185
    {NULL, NULL}
186
};
187
188
static PyMemberDef sentinel_members[] = {
189
    {"__name__", Py_T_OBJECT_EX, offsetof(sentinelobject, name), Py_READONLY},
190
    {"__module__", Py_T_OBJECT_EX, offsetof(sentinelobject, module), 0},
191
    {NULL}
192
};
193
194
static PyNumberMethods sentinel_as_number = {
195
    .nb_or = _Py_union_type_or,
196
};
197
198
PyDoc_STRVAR(sentinel_doc,
199
"sentinel(name, /, *, repr=None)\n"
200
"--\n\n"
201
"Create a unique sentinel object with the given name.");
202
203
PyTypeObject PySentinel_Type = {
204
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
205
    .tp_name = "sentinel",
206
    .tp_basicsize = sizeof(sentinelobject),
207
    .tp_dealloc = sentinel_dealloc,
208
    .tp_repr = sentinel_repr,
209
    .tp_as_number = &sentinel_as_number,
210
    .tp_hash = PyObject_GenericHash,
211
    .tp_getattro = PyObject_GenericGetAttr,
212
    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE
213
                | Py_TPFLAGS_HAVE_GC,
214
    .tp_doc = sentinel_doc,
215
    .tp_traverse = sentinel_traverse,
216
    .tp_clear = sentinel_clear,
217
    .tp_richcompare = _Py_BaseObject_RichCompare,
218
    .tp_methods = sentinel_methods,
219
    .tp_members = sentinel_members,
220
    .tp_new = sentinel_new,
221
    .tp_free = PyObject_GC_Del,
222
};