Skip to content

Commit 2eb9533

Browse files
Add tests.
1 parent 2d4588d commit 2eb9533

File tree

2 files changed

+133
-0
lines changed

2 files changed

+133
-0
lines changed

Lib/test/test_capi/test_misc.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,6 +1098,55 @@ class Data(_testcapi.ObjExtraData):
10981098
del d.extra
10991099
self.assertIsNone(d.extra)
11001100

1101+
def test_sys_getattr(self):
1102+
sys_getattr = _testcapi.sys_getattr
1103+
1104+
self.assertIs(sys_getattr('stdout'), sys.stdout)
1105+
with support.swap_attr(sys, '\U0001f40d', 42):
1106+
self.assertEqual(sys_getattr('\U0001f40d'), 42)
1107+
1108+
with self.assertRaisesRegex(RuntimeError, r'lost sys\.nonexisting'):
1109+
sys_getattr('nonexisting')
1110+
with self.assertRaisesRegex(RuntimeError, r'lost sys\.1'):
1111+
sys_getattr(1)
1112+
self.assertRaises(TypeError, sys_getattr, [])
1113+
# CRASHES sys_getattr(NULL)
1114+
1115+
def test_sys_getattrstring(self):
1116+
sys_getattr = _testcapi.sys_getattrstring
1117+
1118+
self.assertIs(sys_getattr(b'stdout'), sys.stdout)
1119+
with support.swap_attr(sys, '\U0001f40d', 42):
1120+
self.assertEqual(sys_getattr('\U0001f40d'.encode()), 42)
1121+
1122+
with self.assertRaisesRegex(RuntimeError, r'lost sys\.nonexisting'):
1123+
sys_getattr(b'nonexisting')
1124+
self.assertRaises(UnicodeDecodeError, sys_getattr, b'\xff')
1125+
# CRASHES sys_getattr(NULL)
1126+
1127+
def test_sys_getoptionalattr(self):
1128+
sys_getattr = _testcapi.sys_getoptionalattr
1129+
1130+
self.assertIs(sys_getattr('stdout'), sys.stdout)
1131+
with support.swap_attr(sys, '\U0001f40d', 42):
1132+
self.assertEqual(sys_getattr('\U0001f40d'), 42)
1133+
1134+
self.assertIs(sys_getattr('nonexisting'), AttributeError)
1135+
self.assertIs(sys_getattr(1), AttributeError)
1136+
self.assertRaises(TypeError, sys_getattr, [])
1137+
# CRASHES sys_getattr(NULL)
1138+
1139+
def test_sys_getoptionalattrstring(self):
1140+
sys_getattr = _testcapi.sys_getoptionalattrstring
1141+
1142+
self.assertIs(sys_getattr(b'stdout'), sys.stdout)
1143+
with support.swap_attr(sys, '\U0001f40d', 42):
1144+
self.assertEqual(sys_getattr('\U0001f40d'.encode()), 42)
1145+
1146+
self.assertIs(sys_getattr(b'nonexisting'), AttributeError)
1147+
self.assertRaises(UnicodeDecodeError, sys_getattr, b'\xff')
1148+
# CRASHES sys_getattr(NULL)
1149+
11011150
def test_sys_getobject(self):
11021151
getobject = _testcapi.sys_getobject
11031152

Modules/_testcapimodule.c

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3231,6 +3231,86 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
32313231
}
32323232

32333233

3234+
static PyObject *
3235+
sys_getattr(PyObject *Py_UNUSED(module), PyObject *name)
3236+
{
3237+
NULLABLE(name);
3238+
PyObject *result = PySys_GetAttr(name);
3239+
if (result == NULL && PyErr_Occurred()) {
3240+
return NULL;
3241+
}
3242+
if (result == NULL) {
3243+
result = PyExc_AttributeError;
3244+
Py_INCREF(PyExc_AttributeError);
3245+
}
3246+
return result;
3247+
}
3248+
3249+
static PyObject *
3250+
sys_getattrstring(PyObject *Py_UNUSED(module), PyObject *arg)
3251+
{
3252+
const char *name;
3253+
Py_ssize_t size;
3254+
if (!PyArg_Parse(arg, "z#", &name, &size)) {
3255+
return NULL;
3256+
}
3257+
PyObject *result = PySys_GetAttrString(name);
3258+
if (result == NULL && PyErr_Occurred()) {
3259+
return NULL;
3260+
}
3261+
if (result == NULL) {
3262+
result = PyExc_AttributeError;
3263+
Py_INCREF(PyExc_AttributeError);
3264+
}
3265+
return result;
3266+
}
3267+
3268+
static PyObject *
3269+
sys_getoptionalattr(PyObject *Py_UNUSED(module), PyObject *name)
3270+
{
3271+
PyObject *value = UNINITIALIZED_PTR;
3272+
NULLABLE(name);
3273+
3274+
switch (PySys_GetOptionalAttr(name, &value)) {
3275+
case -1:
3276+
assert(value == NULL);
3277+
return NULL;
3278+
case 0:
3279+
assert(value == NULL);
3280+
return Py_NewRef(PyExc_AttributeError);
3281+
case 1:
3282+
return value;
3283+
default:
3284+
Py_FatalError("PySys_GetOptionalAttr() returned invalid code");
3285+
Py_UNREACHABLE();
3286+
}
3287+
}
3288+
3289+
static PyObject *
3290+
sys_getoptionalattrstring(PyObject *Py_UNUSED(module), PyObject *arg)
3291+
{
3292+
PyObject *value = UNINITIALIZED_PTR;
3293+
const char *name;
3294+
Py_ssize_t size;
3295+
if (!PyArg_Parse(arg, "z#", &name, &size)) {
3296+
return NULL;
3297+
}
3298+
3299+
switch (PySys_GetOptionalAttrString(name, &value)) {
3300+
case -1:
3301+
assert(value == NULL);
3302+
return NULL;
3303+
case 0:
3304+
assert(value == NULL);
3305+
return Py_NewRef(PyExc_AttributeError);
3306+
case 1:
3307+
return value;
3308+
default:
3309+
Py_FatalError("PySys_GetOptionalAttrString() returned invalid code");
3310+
Py_UNREACHABLE();
3311+
}
3312+
}
3313+
32343314
static PyObject *
32353315
sys_getobject(PyObject *Py_UNUSED(module), PyObject *arg)
32363316
{
@@ -3392,6 +3472,10 @@ static PyMethodDef TestMethods[] = {
33923472
{"function_set_kw_defaults", function_set_kw_defaults, METH_VARARGS, NULL},
33933473
{"check_pyimport_addmodule", check_pyimport_addmodule, METH_VARARGS},
33943474
{"test_weakref_capi", test_weakref_capi, METH_NOARGS},
3475+
{"sys_getattr", sys_getattr, METH_O},
3476+
{"sys_getattrstring", sys_getattrstring, METH_O},
3477+
{"sys_getoptionalattr", sys_getoptionalattr, METH_O},
3478+
{"sys_getoptionalattrstring", sys_getoptionalattrstring, METH_O},
33953479
{"sys_getobject", sys_getobject, METH_O},
33963480
{"sys_setobject", sys_setobject, METH_VARARGS},
33973481
{NULL, NULL} /* sentinel */

0 commit comments

Comments
 (0)