Skip to content

Commit

Permalink
pythongh-118201: Simplify conv_confname (python#126089)
Browse files Browse the repository at this point in the history
  • Loading branch information
mhsmith authored Nov 19, 2024
1 parent 5fcc3a4 commit c5c9286
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 111 deletions.
3 changes: 1 addition & 2 deletions Lib/test/support/os_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -632,8 +632,7 @@ def fd_count():
if hasattr(os, 'sysconf'):
try:
MAXFD = os.sysconf("SC_OPEN_MAX")
except (OSError, ValueError):
# gh-118201: ValueError is raised intermittently on iOS
except OSError:
pass

old_modes = None
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -2447,8 +2447,8 @@ def test_fchown(self):
support.is_emscripten or support.is_wasi,
"musl libc issue on Emscripten/WASI, bpo-46390"
)
@unittest.skipIf(support.is_apple_mobile, "gh-118201: Test is flaky on iOS")
def test_fpathconf(self):
self.assertIn("PC_NAME_MAX", os.pathconf_names)
self.check(os.pathconf, "PC_NAME_MAX")
self.check(os.fpathconf, "PC_NAME_MAX")
self.check_bool(os.pathconf, "PC_NAME_MAX")
Expand Down
34 changes: 31 additions & 3 deletions Lib/test/test_posix.py
Original file line number Diff line number Diff line change
Expand Up @@ -568,10 +568,38 @@ def test_dup(self):

@unittest.skipUnless(hasattr(posix, 'confstr'),
'test needs posix.confstr()')
@unittest.skipIf(support.is_apple_mobile, "gh-118201: Test is flaky on iOS")
def test_confstr(self):
self.assertRaises(ValueError, posix.confstr, "CS_garbage")
self.assertEqual(len(posix.confstr("CS_PATH")) > 0, True)
with self.assertRaisesRegex(
ValueError, "unrecognized configuration name"
):
posix.confstr("CS_garbage")

with self.assertRaisesRegex(
TypeError, "configuration names must be strings or integers"
):
posix.confstr(1.23)

path = posix.confstr("CS_PATH")
self.assertGreater(len(path), 0)
self.assertEqual(posix.confstr(posix.confstr_names["CS_PATH"]), path)

@unittest.skipUnless(hasattr(posix, 'sysconf'),
'test needs posix.sysconf()')
def test_sysconf(self):
with self.assertRaisesRegex(
ValueError, "unrecognized configuration name"
):
posix.sysconf("SC_garbage")

with self.assertRaisesRegex(
TypeError, "configuration names must be strings or integers"
):
posix.sysconf(1.23)

arg_max = posix.sysconf("SC_ARG_MAX")
self.assertGreater(arg_max, 0)
self.assertEqual(
posix.sysconf(posix.sysconf_names["SC_ARG_MAX"]), arg_max)

@unittest.skipUnless(hasattr(posix, 'dup2'),
'test needs posix.dup2()')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fixed intermittent failures of :any:`os.confstr`, :any:`os.pathconf` and
:any:`os.sysconf` on iOS and Android.
10 changes: 5 additions & 5 deletions Modules/clinic/posixmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

147 changes: 47 additions & 100 deletions Modules/posixmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -3113,18 +3113,22 @@ class Py_off_t_return_converter(long_return_converter):
type = 'Py_off_t'
conversion_fn = 'PyLong_FromPy_off_t'
class path_confname_converter(CConverter):
class confname_converter(CConverter):
type="int"
converter="conv_path_confname"
converter="conv_confname"
class confstr_confname_converter(path_confname_converter):
converter='conv_confstr_confname'
def converter_init(self, *, table):
self.table = table
class sysconf_confname_converter(path_confname_converter):
converter="conv_sysconf_confname"
def parse_arg(self, argname, displayname, *, limited_capi):
return self.format_code("""
if (!{converter}(module, {argname}, &{paramname}, "{table}")) {{{{
goto exit;
}}}}
""", argname=argname, converter=self.converter, table=self.table)
[python start generated code]*/
/*[python end generated code: output=da39a3ee5e6b4b0d input=1860d32584c2a539]*/
/*[python end generated code: output=da39a3ee5e6b4b0d input=8189d5ae78244626]*/

/*[clinic input]
Expand Down Expand Up @@ -13547,46 +13551,38 @@ struct constdef {
};

static int
conv_confname(PyObject *arg, int *valuep, struct constdef *table,
size_t tablesize)
conv_confname(PyObject *module, PyObject *arg, int *valuep, const char *tablename)
{
if (PyLong_Check(arg)) {
int value = PyLong_AsInt(arg);
if (value == -1 && PyErr_Occurred())
return 0;
*valuep = value;
return 1;
}
else {
/* look up the value in the table using a binary search */
size_t lo = 0;
size_t mid;
size_t hi = tablesize;
int cmp;
const char *confname;
if (!PyUnicode_Check(arg)) {
PyErr_SetString(PyExc_TypeError,
"configuration names must be strings or integers");
if (PyUnicode_Check(arg)) {
PyObject *table = PyObject_GetAttrString(module, tablename);
if (table == NULL) {
return 0;
}
confname = PyUnicode_AsUTF8(arg);
if (confname == NULL)

arg = PyObject_GetItem(table, arg);
Py_DECREF(table);
if (arg == NULL) {
PyErr_SetString(
PyExc_ValueError, "unrecognized configuration name");
return 0;
while (lo < hi) {
mid = (lo + hi) / 2;
cmp = strcmp(confname, table[mid].name);
if (cmp < 0)
hi = mid;
else if (cmp > 0)
lo = mid + 1;
else {
*valuep = table[mid].value;
return 1;
}
}
PyErr_SetString(PyExc_ValueError, "unrecognized configuration name");
return 0;
} else {
Py_INCREF(arg); // Match the Py_DECREF below.
}

int success = 0;
if (!PyLong_Check(arg)) {
PyErr_SetString(PyExc_TypeError,
"configuration names must be strings or integers");
} else {
int value = PyLong_AsInt(arg);
if (!(value == -1 && PyErr_Occurred())) {
*valuep = value;
success = 1;
}
}
Py_DECREF(arg);
return success;
}


Expand Down Expand Up @@ -13677,14 +13673,6 @@ static struct constdef posix_constants_pathconf[] = {
{"PC_TIMESTAMP_RESOLUTION", _PC_TIMESTAMP_RESOLUTION},
#endif
};

static int
conv_path_confname(PyObject *arg, int *valuep)
{
return conv_confname(arg, valuep, posix_constants_pathconf,
sizeof(posix_constants_pathconf)
/ sizeof(struct constdef));
}
#endif


Expand All @@ -13693,7 +13681,7 @@ conv_path_confname(PyObject *arg, int *valuep)
os.fpathconf -> long
fd: fildes
name: path_confname
name: confname(table="pathconf_names")
/
Return the configuration limit name for the file descriptor fd.
Expand All @@ -13703,7 +13691,7 @@ If there is no limit, return -1.

static long
os_fpathconf_impl(PyObject *module, int fd, int name)
/*[clinic end generated code: output=d5b7042425fc3e21 input=5b8d2471cfaae186]*/
/*[clinic end generated code: output=d5b7042425fc3e21 input=023d44589c9ed6aa]*/
{
long limit;

Expand All @@ -13721,7 +13709,7 @@ os_fpathconf_impl(PyObject *module, int fd, int name)
/*[clinic input]
os.pathconf -> long
path: path_t(allow_fd='PATH_HAVE_FPATHCONF')
name: path_confname
name: confname(table="pathconf_names")
Return the configuration limit name for the file or directory path.
Expand All @@ -13732,7 +13720,7 @@ On some platforms, path may also be specified as an open file descriptor.

static long
os_pathconf_impl(PyObject *module, path_t *path, int name)
/*[clinic end generated code: output=5bedee35b293a089 input=bc3e2a985af27e5e]*/
/*[clinic end generated code: output=5bedee35b293a089 input=6f6072f57b10c787]*/
{
long limit;

Expand Down Expand Up @@ -13909,27 +13897,19 @@ static struct constdef posix_constants_confstr[] = {
#endif
};

static int
conv_confstr_confname(PyObject *arg, int *valuep)
{
return conv_confname(arg, valuep, posix_constants_confstr,
sizeof(posix_constants_confstr)
/ sizeof(struct constdef));
}


/*[clinic input]
os.confstr
name: confstr_confname
name: confname(table="confstr_names")
/
Return a string-valued system configuration variable.
[clinic start generated code]*/

static PyObject *
os_confstr_impl(PyObject *module, int name)
/*[clinic end generated code: output=bfb0b1b1e49b9383 input=18fb4d0567242e65]*/
/*[clinic end generated code: output=bfb0b1b1e49b9383 input=4c6ffca2837ec959]*/
{
PyObject *result = NULL;
char buffer[255];
Expand Down Expand Up @@ -14466,26 +14446,18 @@ static struct constdef posix_constants_sysconf[] = {
#endif
};

static int
conv_sysconf_confname(PyObject *arg, int *valuep)
{
return conv_confname(arg, valuep, posix_constants_sysconf,
sizeof(posix_constants_sysconf)
/ sizeof(struct constdef));
}


/*[clinic input]
os.sysconf -> long
name: sysconf_confname
name: confname(table="sysconf_names")
/
Return an integer-valued system configuration variable.
[clinic start generated code]*/

static long
os_sysconf_impl(PyObject *module, int name)
/*[clinic end generated code: output=3662f945fc0cc756 input=279e3430a33f29e4]*/
/*[clinic end generated code: output=3662f945fc0cc756 input=930b8f23b5d15086]*/
{
long value;

Expand All @@ -14498,40 +14470,15 @@ os_sysconf_impl(PyObject *module, int name)
#endif /* HAVE_SYSCONF */


/* This code is used to ensure that the tables of configuration value names
* are in sorted order as required by conv_confname(), and also to build
* the exported dictionaries that are used to publish information about the
* names available on the host platform.
*
* Sorting the table at runtime ensures that the table is properly ordered
* when used, even for platforms we're not able to test on. It also makes
* it easier to add additional entries to the tables.
*/

static int
cmp_constdefs(const void *v1, const void *v2)
{
const struct constdef *c1 =
(const struct constdef *) v1;
const struct constdef *c2 =
(const struct constdef *) v2;

return strcmp(c1->name, c2->name);
}

static int
setup_confname_table(struct constdef *table, size_t tablesize,
const char *tablename, PyObject *module)
{
PyObject *d = NULL;
size_t i;

qsort(table, tablesize, sizeof(struct constdef), cmp_constdefs);
d = PyDict_New();
PyObject *d = PyDict_New();
if (d == NULL)
return -1;

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

0 comments on commit c5c9286

Please sign in to comment.