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

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

@@ -3344,7 +3344,7 @@ def test_path_t_converter(self):

33443344

cleanup_fn(result)

33453345
33463346

with self.assertRaisesRegex(

3347-

TypeError, 'should be string, bytes'):

3347+

TypeError, 'to return str or bytes'):

33483348

fn(int_fspath, *extra_args)

33493349
33503350

if allow_fd:

@@ -3357,6 +3357,23 @@ def test_path_t_converter(self):

33573357

'os.PathLike'):

33583358

fn(fd, *extra_args)

33593359
3360+

def test_path_t_converter_and_custom_class(self):

3361+

with self.assertRaisesRegex(

3362+

TypeError,

3363+

'__fspath__\(\) to return str or bytes, not int'

3364+

):

3365+

os.stat(FakePath(2))

3366+

with self.assertRaisesRegex(

3367+

TypeError,

3368+

'__fspath__\(\) to return str or bytes, not float'

3369+

):

3370+

os.stat(FakePath(2.34))

3371+

with self.assertRaisesRegex(

3372+

TypeError,

3373+

'__fspath__\(\) to return str or bytes, not object'

3374+

):

3375+

os.stat(FakePath(object()))

3376+
33603377
33613378

@unittest.skipUnless(hasattr(os, 'get_blocking'),

33623379

'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

@@ -954,28 +954,35 @@ path_converter(PyObject *o, void *p)

954954

if (!is_index && !is_buffer && !is_unicode && !is_bytes) {

955955

/* Inline PyOS_FSPath() for better error messages. */

956956

_Py_IDENTIFIER(__fspath__);

957-

PyObject *func = NULL;

957+

PyObject *func, *res;

958958
959959

func = _PyObject_LookupSpecial(o, &PyId___fspath__);

960960

if (NULL == func) {

961961

goto error_format;

962962

}

963-

/* still owns a reference to the original object */

964-

Py_DECREF(o);

965-

o = _PyObject_CallNoArg(func);

963+

res = _PyObject_CallNoArg(func);

966964

Py_DECREF(func);

967-

if (NULL == o) {

965+

if (NULL == res) {

968966

goto error_exit;

969967

}

970-

else if (PyUnicode_Check(o)) {

968+

else if (PyUnicode_Check(res)) {

971969

is_unicode = 1;

972970

}

973-

else if (PyBytes_Check(o)) {

971+

else if (PyBytes_Check(res)) {

974972

is_bytes = 1;

975973

}

976974

else {

977-

goto error_format;

975+

PyErr_Format(PyExc_TypeError,

976+

"expected %.200s.__fspath__() to return str or bytes, "

977+

"not %.200s", Py_TYPE(o)->tp_name,

978+

Py_TYPE(res)->tp_name);

979+

Py_DECREF(res);

980+

goto error_exit;

978981

}

982+
983+

/* still owns a reference to the original object */

984+

Py_DECREF(o);

985+

o = res;

979986

}

980987
981988

if (is_unicode) {