Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-118201: Simplify conv_confname #126089

Merged
merged 6 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -3112,18 +3112,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 @@ -13546,46 +13550,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 @@ -13676,14 +13672,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 @@ -13692,7 +13680,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 @@ -13702,7 +13690,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 @@ -13720,7 +13708,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 @@ -13731,7 +13719,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 @@ -13908,27 +13896,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 @@ -14465,26 +14445,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 @@ -14497,40 +14469,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
Loading