Skip to content

Commit a01065a

Browse files
bpo-35942: Improve the error message if __fspath__ returns invalid types in path_converter (GH-11831)
The error message emitted when returning invalid types from __fspath__ in interfaces that allow passing PathLike objects has been improved and now it does explain the origin of the error. (cherry picked from commit 09fbcd6) Co-authored-by: Pablo Galindo <Pablogsal@gmail.com>
1 parent a7f929d commit a01065a

3 files changed

Lines changed: 36 additions & 9 deletions

File tree

Lib/test/test_os.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3297,7 +3297,7 @@ def test_path_t_converter(self):
32973297
cleanup_fn(result)
32983298

32993299
with self.assertRaisesRegex(
3300-
TypeError, 'should be string, bytes'):
3300+
TypeError, 'to return str or bytes'):
33013301
fn(int_fspath, *extra_args)
33023302

33033303
if allow_fd:
@@ -3310,6 +3310,23 @@ def test_path_t_converter(self):
33103310
'os.PathLike'):
33113311
fn(fd, *extra_args)
33123312

3313+
def test_path_t_converter_and_custom_class(self):
3314+
with self.assertRaisesRegex(
3315+
TypeError,
3316+
'__fspath__\(\) to return str or bytes, not int'
3317+
):
3318+
os.stat(FakePath(2))
3319+
with self.assertRaisesRegex(
3320+
TypeError,
3321+
'__fspath__\(\) to return str or bytes, not float'
3322+
):
3323+
os.stat(FakePath(2.34))
3324+
with self.assertRaisesRegex(
3325+
TypeError,
3326+
'__fspath__\(\) to return str or bytes, not object'
3327+
):
3328+
os.stat(FakePath(object()))
3329+
33133330

33143331
@unittest.skipUnless(hasattr(os, 'get_blocking'),
33153332
'needs os.get_blocking() and os.set_blocking()')
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
The error message emmited when returning invalid types from ``__fspath__``
2+
in interfaces that allow passing :class:`~os.PathLike` objects has been
3+
improved and now it does explain the origin of the error.

Modules/posixmodule.c

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -979,28 +979,35 @@ path_converter(PyObject *o, void *p)
979979
if (!is_index && !is_buffer && !is_unicode && !is_bytes) {
980980
/* Inline PyOS_FSPath() for better error messages. */
981981
_Py_IDENTIFIER(__fspath__);
982-
PyObject *func = NULL;
982+
PyObject *func, *res;
983983

984984
func = _PyObject_LookupSpecial(o, &PyId___fspath__);
985985
if (NULL == func) {
986986
goto error_format;
987987
}
988-
/* still owns a reference to the original object */
989-
Py_DECREF(o);
990-
o = _PyObject_CallNoArg(func);
988+
res = _PyObject_CallNoArg(func);
991989
Py_DECREF(func);
992-
if (NULL == o) {
990+
if (NULL == res) {
993991
goto error_exit;
994992
}
995-
else if (PyUnicode_Check(o)) {
993+
else if (PyUnicode_Check(res)) {
996994
is_unicode = 1;
997995
}
998-
else if (PyBytes_Check(o)) {
996+
else if (PyBytes_Check(res)) {
999997
is_bytes = 1;
1000998
}
1001999
else {
1002-
goto error_format;
1000+
PyErr_Format(PyExc_TypeError,
1001+
"expected %.200s.__fspath__() to return str or bytes, "
1002+
"not %.200s", Py_TYPE(o)->tp_name,
1003+
Py_TYPE(res)->tp_name);
1004+
Py_DECREF(res);
1005+
goto error_exit;
10031006
}
1007+
1008+
/* still owns a reference to the original object */
1009+
Py_DECREF(o);
1010+
o = res;
10041011
}
10051012

10061013
if (is_unicode) {

0 commit comments

Comments
 (0)