Skip to content

Commit 9466eb8

Browse files
committed
gh-118201: Simplify conv_confname (#126089)
(cherry picked from commit c5c9286)
1 parent 3feebdd commit 9466eb8

File tree

6 files changed

+87
-111
lines changed

6 files changed

+87
-111
lines changed

Lib/test/support/os_helper.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -632,8 +632,7 @@ def fd_count():
632632
if hasattr(os, 'sysconf'):
633633
try:
634634
MAXFD = os.sysconf("SC_OPEN_MAX")
635-
except (OSError, ValueError):
636-
# gh-118201: ValueError is raised intermittently on iOS
635+
except OSError:
637636
pass
638637

639638
old_modes = None

Lib/test/test_os.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2397,8 +2397,8 @@ def test_fchown(self):
23972397
support.is_emscripten or support.is_wasi,
23982398
"musl libc issue on Emscripten/WASI, bpo-46390"
23992399
)
2400-
@unittest.skipIf(support.is_apple_mobile, "gh-118201: Test is flaky on iOS")
24012400
def test_fpathconf(self):
2401+
self.assertIn("PC_NAME_MAX", os.pathconf_names)
24022402
self.check(os.pathconf, "PC_NAME_MAX")
24032403
self.check(os.fpathconf, "PC_NAME_MAX")
24042404
self.check_bool(os.pathconf, "PC_NAME_MAX")

Lib/test/test_posix.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -568,10 +568,38 @@ def test_dup(self):
568568

569569
@unittest.skipUnless(hasattr(posix, 'confstr'),
570570
'test needs posix.confstr()')
571-
@unittest.skipIf(support.is_apple_mobile, "gh-118201: Test is flaky on iOS")
572571
def test_confstr(self):
573-
self.assertRaises(ValueError, posix.confstr, "CS_garbage")
574-
self.assertEqual(len(posix.confstr("CS_PATH")) > 0, True)
572+
with self.assertRaisesRegex(
573+
ValueError, "unrecognized configuration name"
574+
):
575+
posix.confstr("CS_garbage")
576+
577+
with self.assertRaisesRegex(
578+
TypeError, "configuration names must be strings or integers"
579+
):
580+
posix.confstr(1.23)
581+
582+
path = posix.confstr("CS_PATH")
583+
self.assertGreater(len(path), 0)
584+
self.assertEqual(posix.confstr(posix.confstr_names["CS_PATH"]), path)
585+
586+
@unittest.skipUnless(hasattr(posix, 'sysconf'),
587+
'test needs posix.sysconf()')
588+
def test_sysconf(self):
589+
with self.assertRaisesRegex(
590+
ValueError, "unrecognized configuration name"
591+
):
592+
posix.sysconf("SC_garbage")
593+
594+
with self.assertRaisesRegex(
595+
TypeError, "configuration names must be strings or integers"
596+
):
597+
posix.sysconf(1.23)
598+
599+
arg_max = posix.sysconf("SC_ARG_MAX")
600+
self.assertGreater(arg_max, 0)
601+
self.assertEqual(
602+
posix.sysconf(posix.sysconf_names["SC_ARG_MAX"]), arg_max)
575603

576604
@unittest.skipUnless(hasattr(posix, 'dup2'),
577605
'test needs posix.dup2()')
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fixed intermittent failures of :any:`os.confstr`, :any:`os.pathconf` and
2+
:any:`os.sysconf` on iOS and Android.

Modules/clinic/posixmodule.c.h

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/posixmodule.c

Lines changed: 47 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -3078,18 +3078,22 @@ class Py_off_t_return_converter(long_return_converter):
30783078
type = 'Py_off_t'
30793079
conversion_fn = 'PyLong_FromPy_off_t'
30803080
3081-
class path_confname_converter(CConverter):
3081+
class confname_converter(CConverter):
30823082
type="int"
3083-
converter="conv_path_confname"
3083+
converter="conv_confname"
30843084
3085-
class confstr_confname_converter(path_confname_converter):
3086-
converter='conv_confstr_confname'
3085+
def converter_init(self, *, table):
3086+
self.table = table
30873087
3088-
class sysconf_confname_converter(path_confname_converter):
3089-
converter="conv_sysconf_confname"
3088+
def parse_arg(self, argname, displayname, *, limited_capi):
3089+
return self.format_code("""
3090+
if (!{converter}(module, {argname}, &{paramname}, "{table}")) {{{{
3091+
goto exit;
3092+
}}}}
3093+
""", argname=argname, converter=self.converter, table=self.table)
30903094
30913095
[python start generated code]*/
3092-
/*[python end generated code: output=da39a3ee5e6b4b0d input=577cb476e5d64960]*/
3096+
/*[python end generated code: output=da39a3ee5e6b4b0d input=a6199b1618d73f53]*/
30933097

30943098
/*[clinic input]
30953099
@@ -13503,46 +13507,38 @@ struct constdef {
1350313507
};
1350413508

1350513509
static int
13506-
conv_confname(PyObject *arg, int *valuep, struct constdef *table,
13507-
size_t tablesize)
13510+
conv_confname(PyObject *module, PyObject *arg, int *valuep, const char *tablename)
1350813511
{
13509-
if (PyLong_Check(arg)) {
13510-
int value = PyLong_AsInt(arg);
13511-
if (value == -1 && PyErr_Occurred())
13512-
return 0;
13513-
*valuep = value;
13514-
return 1;
13515-
}
13516-
else {
13517-
/* look up the value in the table using a binary search */
13518-
size_t lo = 0;
13519-
size_t mid;
13520-
size_t hi = tablesize;
13521-
int cmp;
13522-
const char *confname;
13523-
if (!PyUnicode_Check(arg)) {
13524-
PyErr_SetString(PyExc_TypeError,
13525-
"configuration names must be strings or integers");
13512+
if (PyUnicode_Check(arg)) {
13513+
PyObject *table = PyObject_GetAttrString(module, tablename);
13514+
if (table == NULL) {
1352613515
return 0;
1352713516
}
13528-
confname = PyUnicode_AsUTF8(arg);
13529-
if (confname == NULL)
13517+
13518+
arg = PyObject_GetItem(table, arg);
13519+
Py_DECREF(table);
13520+
if (arg == NULL) {
13521+
PyErr_SetString(
13522+
PyExc_ValueError, "unrecognized configuration name");
1353013523
return 0;
13531-
while (lo < hi) {
13532-
mid = (lo + hi) / 2;
13533-
cmp = strcmp(confname, table[mid].name);
13534-
if (cmp < 0)
13535-
hi = mid;
13536-
else if (cmp > 0)
13537-
lo = mid + 1;
13538-
else {
13539-
*valuep = table[mid].value;
13540-
return 1;
13541-
}
1354213524
}
13543-
PyErr_SetString(PyExc_ValueError, "unrecognized configuration name");
13544-
return 0;
13525+
} else {
13526+
Py_INCREF(arg); // Match the Py_DECREF below.
13527+
}
13528+
13529+
int success = 0;
13530+
if (!PyLong_Check(arg)) {
13531+
PyErr_SetString(PyExc_TypeError,
13532+
"configuration names must be strings or integers");
13533+
} else {
13534+
int value = PyLong_AsInt(arg);
13535+
if (!(value == -1 && PyErr_Occurred())) {
13536+
*valuep = value;
13537+
success = 1;
13538+
}
1354513539
}
13540+
Py_DECREF(arg);
13541+
return success;
1354613542
}
1354713543

1354813544

@@ -13633,14 +13629,6 @@ static struct constdef posix_constants_pathconf[] = {
1363313629
{"PC_TIMESTAMP_RESOLUTION", _PC_TIMESTAMP_RESOLUTION},
1363413630
#endif
1363513631
};
13636-
13637-
static int
13638-
conv_path_confname(PyObject *arg, int *valuep)
13639-
{
13640-
return conv_confname(arg, valuep, posix_constants_pathconf,
13641-
sizeof(posix_constants_pathconf)
13642-
/ sizeof(struct constdef));
13643-
}
1364413632
#endif
1364513633

1364613634

@@ -13649,7 +13637,7 @@ conv_path_confname(PyObject *arg, int *valuep)
1364913637
os.fpathconf -> long
1365013638
1365113639
fd: fildes
13652-
name: path_confname
13640+
name: confname(table="pathconf_names")
1365313641
/
1365413642
1365513643
Return the configuration limit name for the file descriptor fd.
@@ -13659,7 +13647,7 @@ If there is no limit, return -1.
1365913647

1366013648
static long
1366113649
os_fpathconf_impl(PyObject *module, int fd, int name)
13662-
/*[clinic end generated code: output=d5b7042425fc3e21 input=5b8d2471cfaae186]*/
13650+
/*[clinic end generated code: output=d5b7042425fc3e21 input=023d44589c9ed6aa]*/
1366313651
{
1366413652
long limit;
1366513653

@@ -13677,7 +13665,7 @@ os_fpathconf_impl(PyObject *module, int fd, int name)
1367713665
/*[clinic input]
1367813666
os.pathconf -> long
1367913667
path: path_t(allow_fd='PATH_HAVE_FPATHCONF')
13680-
name: path_confname
13668+
name: confname(table="pathconf_names")
1368113669
1368213670
Return the configuration limit name for the file or directory path.
1368313671
@@ -13688,7 +13676,7 @@ On some platforms, path may also be specified as an open file descriptor.
1368813676

1368913677
static long
1369013678
os_pathconf_impl(PyObject *module, path_t *path, int name)
13691-
/*[clinic end generated code: output=5bedee35b293a089 input=bc3e2a985af27e5e]*/
13679+
/*[clinic end generated code: output=5bedee35b293a089 input=6f6072f57b10c787]*/
1369213680
{
1369313681
long limit;
1369413682

@@ -13865,27 +13853,19 @@ static struct constdef posix_constants_confstr[] = {
1386513853
#endif
1386613854
};
1386713855

13868-
static int
13869-
conv_confstr_confname(PyObject *arg, int *valuep)
13870-
{
13871-
return conv_confname(arg, valuep, posix_constants_confstr,
13872-
sizeof(posix_constants_confstr)
13873-
/ sizeof(struct constdef));
13874-
}
13875-
1387613856

1387713857
/*[clinic input]
1387813858
os.confstr
1387913859
13880-
name: confstr_confname
13860+
name: confname(table="confstr_names")
1388113861
/
1388213862
1388313863
Return a string-valued system configuration variable.
1388413864
[clinic start generated code]*/
1388513865

1388613866
static PyObject *
1388713867
os_confstr_impl(PyObject *module, int name)
13888-
/*[clinic end generated code: output=bfb0b1b1e49b9383 input=18fb4d0567242e65]*/
13868+
/*[clinic end generated code: output=bfb0b1b1e49b9383 input=4c6ffca2837ec959]*/
1388913869
{
1389013870
PyObject *result = NULL;
1389113871
char buffer[255];
@@ -14422,26 +14402,18 @@ static struct constdef posix_constants_sysconf[] = {
1442214402
#endif
1442314403
};
1442414404

14425-
static int
14426-
conv_sysconf_confname(PyObject *arg, int *valuep)
14427-
{
14428-
return conv_confname(arg, valuep, posix_constants_sysconf,
14429-
sizeof(posix_constants_sysconf)
14430-
/ sizeof(struct constdef));
14431-
}
14432-
1443314405

1443414406
/*[clinic input]
1443514407
os.sysconf -> long
14436-
name: sysconf_confname
14408+
name: confname(table="sysconf_names")
1443714409
/
1443814410
1443914411
Return an integer-valued system configuration variable.
1444014412
[clinic start generated code]*/
1444114413

1444214414
static long
1444314415
os_sysconf_impl(PyObject *module, int name)
14444-
/*[clinic end generated code: output=3662f945fc0cc756 input=279e3430a33f29e4]*/
14416+
/*[clinic end generated code: output=3662f945fc0cc756 input=930b8f23b5d15086]*/
1444514417
{
1444614418
long value;
1444714419

@@ -14454,40 +14426,15 @@ os_sysconf_impl(PyObject *module, int name)
1445414426
#endif /* HAVE_SYSCONF */
1445514427

1445614428

14457-
/* This code is used to ensure that the tables of configuration value names
14458-
* are in sorted order as required by conv_confname(), and also to build
14459-
* the exported dictionaries that are used to publish information about the
14460-
* names available on the host platform.
14461-
*
14462-
* Sorting the table at runtime ensures that the table is properly ordered
14463-
* when used, even for platforms we're not able to test on. It also makes
14464-
* it easier to add additional entries to the tables.
14465-
*/
14466-
14467-
static int
14468-
cmp_constdefs(const void *v1, const void *v2)
14469-
{
14470-
const struct constdef *c1 =
14471-
(const struct constdef *) v1;
14472-
const struct constdef *c2 =
14473-
(const struct constdef *) v2;
14474-
14475-
return strcmp(c1->name, c2->name);
14476-
}
14477-
1447814429
static int
1447914430
setup_confname_table(struct constdef *table, size_t tablesize,
1448014431
const char *tablename, PyObject *module)
1448114432
{
14482-
PyObject *d = NULL;
14483-
size_t i;
14484-
14485-
qsort(table, tablesize, sizeof(struct constdef), cmp_constdefs);
14486-
d = PyDict_New();
14433+
PyObject *d = PyDict_New();
1448714434
if (d == NULL)
1448814435
return -1;
1448914436

14490-
for (i=0; i < tablesize; ++i) {
14437+
for (size_t i=0; i < tablesize; ++i) {
1449114438
PyObject *o = PyLong_FromLong(table[i].value);
1449214439
if (o == NULL || PyDict_SetItemString(d, table[i].name, o) == -1) {
1449314440
Py_XDECREF(o);

0 commit comments

Comments
 (0)