Skip to content

Commit cfc5851

Browse files
gh-106307: Add _PyMapping_LookupItem()
Replacement of PyObject_GetItem() which doesn't raise KeyError. int _PyMapping_LookupItem(PyObject *obj, PyObject *key, PyObject **result) Return 1 and set *result != NULL if a key is found. Return 0 and set *result == NULL if a key is not found; a KeyError is silenced. Return -1 and set *result == NULL if an error other than KeyError is raised.
1 parent 04dfc6f commit cfc5851

File tree

8 files changed

+639
-846
lines changed

8 files changed

+639
-846
lines changed

Include/cpython/object.h

+11
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,17 @@ PyAPI_FUNC(int)
320320
_PyObject_GenericSetAttrWithDict(PyObject *, PyObject *,
321321
PyObject *, PyObject *);
322322

323+
/* Replacements of PyObject_GetItem() which don't raise KeyError.
324+
325+
Return 1 and set *result != NULL if a key is found.
326+
Return 0 and set *result == NULL if a key is not found;
327+
a KeyError is silenced.
328+
Return -1 and set *result == NULL if an error other than KeyError
329+
is raised.
330+
*/
331+
PyAPI_FUNC(int) _PyMapping_LookupItem(PyObject *, PyObject *, PyObject **);
332+
PyAPI_FUNC(int) _PyMapping_LookupItemString(PyObject *, const char *, PyObject **);
333+
323334
PyAPI_FUNC(PyObject *) _PyObject_FunctionStr(PyObject *);
324335

325336
/* Safely decref `dst` and set `dst` to `src`.

Modules/_lzmamodule.c

+9-13
Original file line numberDiff line numberDiff line change
@@ -242,15 +242,10 @@ parse_filter_spec_lzma(_lzma_state *state, PyObject *spec)
242242
/* First, fill in default values for all the options using a preset.
243243
Then, override the defaults with any values given by the caller. */
244244

245-
preset_obj = PyMapping_GetItemString(spec, "preset");
246-
if (preset_obj == NULL) {
247-
if (PyErr_ExceptionMatches(PyExc_KeyError)) {
248-
PyErr_Clear();
249-
}
250-
else {
251-
return NULL;
252-
}
253-
} else {
245+
if (_PyMapping_LookupItemString(spec, "preset", &preset_obj) < 0) {
246+
return NULL;
247+
}
248+
if (preset_obj != NULL) {
254249
int ok = uint32_converter(preset_obj, &preset);
255250
Py_DECREF(preset_obj);
256251
if (!ok) {
@@ -347,11 +342,12 @@ lzma_filter_converter(_lzma_state *state, PyObject *spec, void *ptr)
347342
"Filter specifier must be a dict or dict-like object");
348343
return 0;
349344
}
350-
id_obj = PyMapping_GetItemString(spec, "id");
345+
if (_PyMapping_LookupItemString(spec, "id", &id_obj) < 0) {
346+
return 0;
347+
}
351348
if (id_obj == NULL) {
352-
if (PyErr_ExceptionMatches(PyExc_KeyError))
353-
PyErr_SetString(PyExc_ValueError,
354-
"Filter specifier must have an \"id\" entry");
349+
PyErr_SetString(PyExc_ValueError,
350+
"Filter specifier must have an \"id\" entry");
355351
return 0;
356352
}
357353
f->id = PyLong_AsUnsignedLongLong(id_obj);

Modules/_pickle.c

+3-7
Original file line numberDiff line numberDiff line change
@@ -4438,13 +4438,9 @@ save(PickleState *st, PicklerObject *self, PyObject *obj, int pers_save)
44384438
Py_INCREF(reduce_func);
44394439
}
44404440
} else {
4441-
reduce_func = PyObject_GetItem(self->dispatch_table,
4442-
(PyObject *)type);
4443-
if (reduce_func == NULL) {
4444-
if (PyErr_ExceptionMatches(PyExc_KeyError))
4445-
PyErr_Clear();
4446-
else
4447-
goto error;
4441+
if (_PyMapping_LookupItem(self->dispatch_table, (PyObject *)type,
4442+
&reduce_func) < 0) {
4443+
goto error;
44484444
}
44494445
}
44504446
if (reduce_func != NULL) {

Objects/abstract.c

+39
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,30 @@ PyObject_GetItem(PyObject *o, PyObject *key)
199199
return type_error("'%.200s' object is not subscriptable", o);
200200
}
201201

202+
int
203+
_PyMapping_LookupItem(PyObject *o, PyObject *key, PyObject **pvalue)
204+
{
205+
if (PyDict_CheckExact(o)) {
206+
*pvalue = PyDict_GetItemWithError(o, key); /* borrowed */
207+
if (*pvalue) {
208+
Py_INCREF(*pvalue);
209+
return 1;
210+
}
211+
return PyErr_Occurred() ? -1 : 0;
212+
}
213+
214+
*pvalue = PyObject_GetItem(o, key);
215+
if (*pvalue) {
216+
return 1;
217+
}
218+
assert(PyErr_Occurred());
219+
if (!PyErr_ExceptionMatches(PyExc_KeyError)) {
220+
return -1;
221+
}
222+
PyErr_Clear();
223+
return 0;
224+
}
225+
202226
int
203227
PyObject_SetItem(PyObject *o, PyObject *key, PyObject *value)
204228
{
@@ -2366,6 +2390,21 @@ PyMapping_GetItemString(PyObject *o, const char *key)
23662390
return r;
23672391
}
23682392

2393+
int
2394+
_PyMapping_LookupItemString(PyObject *o, const char *key, PyObject **pvalue)
2395+
{
2396+
if (key == NULL) {
2397+
return null_error();
2398+
}
2399+
PyObject *okey = PyUnicode_FromString(key);
2400+
if (okey == NULL) {
2401+
return NULL;
2402+
}
2403+
int r = _PyMapping_LookupItem(o, okey, pvalue);
2404+
Py_DECREF(okey);
2405+
return r;
2406+
}
2407+
23692408
int
23702409
PyMapping_SetItemString(PyObject *o, const char *key, PyObject *value)
23712410
{

Python/bytecodes.c

+24-92
Original file line numberDiff line numberDiff line change
@@ -1086,26 +1086,11 @@ dummy_func(
10861086
}
10871087

10881088
inst(LOAD_BUILD_CLASS, ( -- bc)) {
1089-
if (PyDict_CheckExact(BUILTINS())) {
1090-
bc = _PyDict_GetItemWithError(BUILTINS(),
1091-
&_Py_ID(__build_class__));
1092-
if (bc == NULL) {
1093-
if (!_PyErr_Occurred(tstate)) {
1094-
_PyErr_SetString(tstate, PyExc_NameError,
1095-
"__build_class__ not found");
1096-
}
1097-
ERROR_IF(true, error);
1098-
}
1099-
Py_INCREF(bc);
1100-
}
1101-
else {
1102-
bc = PyObject_GetItem(BUILTINS(), &_Py_ID(__build_class__));
1103-
if (bc == NULL) {
1104-
if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError))
1105-
_PyErr_SetString(tstate, PyExc_NameError,
1106-
"__build_class__ not found");
1107-
ERROR_IF(true, error);
1108-
}
1089+
ERROR_IF(_PyMapping_LookupItem(BUILTINS(), &_Py_ID(__build_class__), &bc) < 0, error);
1090+
if (bc == NULL) {
1091+
_PyErr_SetString(tstate, PyExc_NameError,
1092+
"__build_class__ not found");
1093+
ERROR_IF(true, error);
11091094
}
11101095
}
11111096

@@ -1280,25 +1265,9 @@ dummy_func(
12801265

12811266
op(_LOAD_FROM_DICT_OR_GLOBALS, (mod_or_class_dict -- v)) {
12821267
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg);
1283-
if (PyDict_CheckExact(mod_or_class_dict)) {
1284-
v = PyDict_GetItemWithError(mod_or_class_dict, name);
1285-
if (v != NULL) {
1286-
Py_INCREF(v);
1287-
}
1288-
else if (_PyErr_Occurred(tstate)) {
1289-
Py_DECREF(mod_or_class_dict);
1290-
goto error;
1291-
}
1292-
}
1293-
else {
1294-
v = PyObject_GetItem(mod_or_class_dict, name);
1295-
if (v == NULL) {
1296-
if (!_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) {
1297-
Py_DECREF(mod_or_class_dict);
1298-
goto error;
1299-
}
1300-
_PyErr_Clear(tstate);
1301-
}
1268+
if (_PyMapping_LookupItem(mod_or_class_dict, name, &v) < 0) {
1269+
Py_DECREF(mod_or_class_dict);
1270+
goto error;
13021271
}
13031272
Py_DECREF(mod_or_class_dict);
13041273
if (v == NULL) {
@@ -1310,28 +1279,14 @@ dummy_func(
13101279
goto error;
13111280
}
13121281
else {
1313-
if (PyDict_CheckExact(BUILTINS())) {
1314-
v = PyDict_GetItemWithError(BUILTINS(), name);
1315-
if (v == NULL) {
1316-
if (!_PyErr_Occurred(tstate)) {
1317-
format_exc_check_arg(
1318-
tstate, PyExc_NameError,
1319-
NAME_ERROR_MSG, name);
1320-
}
1321-
goto error;
1322-
}
1323-
Py_INCREF(v);
1282+
if (_PyMapping_LookupItem(BUILTINS(), name, &v) < 0) {
1283+
goto error;
13241284
}
1325-
else {
1326-
v = PyObject_GetItem(BUILTINS(), name);
1327-
if (v == NULL) {
1328-
if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) {
1329-
format_exc_check_arg(
1330-
tstate, PyExc_NameError,
1331-
NAME_ERROR_MSG, name);
1332-
}
1333-
goto error;
1334-
}
1285+
if (v == NULL) {
1286+
format_exc_check_arg(
1287+
tstate, PyExc_NameError,
1288+
NAME_ERROR_MSG, name);
1289+
goto error;
13351290
}
13361291
}
13371292
}
@@ -1381,19 +1336,14 @@ dummy_func(
13811336
/* Slow-path if globals or builtins is not a dict */
13821337

13831338
/* namespace 1: globals */
1384-
v = PyObject_GetItem(GLOBALS(), name);
1339+
ERROR_IF(_PyMapping_LookupItem(GLOBALS(), name, &v) < 0, error);
13851340
if (v == NULL) {
1386-
ERROR_IF(!_PyErr_ExceptionMatches(tstate, PyExc_KeyError), error);
1387-
_PyErr_Clear(tstate);
1388-
13891341
/* namespace 2: builtins */
1390-
v = PyObject_GetItem(BUILTINS(), name);
1342+
ERROR_IF(_PyMapping_LookupItem(BUILTINS(), name, &v) < 0, error);
13911343
if (v == NULL) {
1392-
if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) {
1393-
format_exc_check_arg(
1394-
tstate, PyExc_NameError,
1395-
NAME_ERROR_MSG, name);
1396-
}
1344+
format_exc_check_arg(
1345+
tstate, PyExc_NameError,
1346+
NAME_ERROR_MSG, name);
13971347
ERROR_IF(true, error);
13981348
}
13991349
}
@@ -1466,25 +1416,9 @@ dummy_func(
14661416
assert(class_dict);
14671417
assert(oparg >= 0 && oparg < _PyFrame_GetCode(frame)->co_nlocalsplus);
14681418
name = PyTuple_GET_ITEM(_PyFrame_GetCode(frame)->co_localsplusnames, oparg);
1469-
if (PyDict_CheckExact(class_dict)) {
1470-
value = PyDict_GetItemWithError(class_dict, name);
1471-
if (value != NULL) {
1472-
Py_INCREF(value);
1473-
}
1474-
else if (_PyErr_Occurred(tstate)) {
1475-
Py_DECREF(class_dict);
1476-
goto error;
1477-
}
1478-
}
1479-
else {
1480-
value = PyObject_GetItem(class_dict, name);
1481-
if (value == NULL) {
1482-
if (!_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) {
1483-
Py_DECREF(class_dict);
1484-
goto error;
1485-
}
1486-
_PyErr_Clear(tstate);
1487-
}
1419+
if (_PyMapping_LookupItem(class_dict, name, &value) < 0) {
1420+
Py_DECREF(class_dict);
1421+
goto error;
14881422
}
14891423
Py_DECREF(class_dict);
14901424
if (!value) {
@@ -1622,10 +1556,8 @@ dummy_func(
16221556
}
16231557
else {
16241558
/* do the same if locals() is not a dict */
1625-
ann_dict = PyObject_GetItem(LOCALS(), &_Py_ID(__annotations__));
1559+
ERROR_IF(_PyMapping_LookupItem(LOCALS(), &_Py_ID(__annotations__), &ann_dict) < 0, error);
16261560
if (ann_dict == NULL) {
1627-
ERROR_IF(!_PyErr_ExceptionMatches(tstate, PyExc_KeyError), error);
1628-
_PyErr_Clear(tstate);
16291561
ann_dict = PyDict_New();
16301562
ERROR_IF(ann_dict == NULL, error);
16311563
err = PyObject_SetItem(LOCALS(), &_Py_ID(__annotations__),

0 commit comments

Comments
 (0)