bpo-35942: Improve the error message if __fspath__ returns invalid ty… · python/cpython@a01065a

File tree

3 files changed

lines changed

  • Misc/NEWS.d/next/Core and Builtins

3 files changed

lines changed

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()')

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.

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) {