8000 gh-106046: Improve error message from `os.fspath` if `__fspath__` is … · python/cpython@93a970f · GitHub
[go: up one dir, main page]

Skip to content

Commit 93a970f

Browse files
authored
gh-106046: Improve error message from os.fspath if __fspath__ is set to None (#106082)
1 parent 8c24a83 commit 93a970f

File tree

5 files changed

+52
-4
lines changed

5 files changed

+52
-4
lines changed

Doc/reference/datamodel.rst

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3179,8 +3179,9 @@ An example of an asynchronous context manager class::
31793179
lead to some very strange behaviour if it is handled incorrectly.
31803180
31813181
.. [#] The :meth:`~object.__hash__`, :meth:`~object.__iter__`,
3182-
:meth:`~object.__reversed__`, and :meth:`~object.__contains__` methods have
3183-
special handling for this; others
3182+
:meth:`~object.__reversed__`, :meth:`~object.__contains__`,
3183+
:meth:`~object.__class_getitem__` and :meth:`~os.PathLike.__fspath__`
3184+
methods have special handling for this. Others
31843185
will still raise a :exc:`TypeError`, but may do so by relying on
31853186
the behavior that ``None`` is not callable.
31863187

Lib/os.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,6 +1061,12 @@ def _fspath(path):
10611061
else:
10621062
raise TypeError("expected str, bytes or os.PathLike object, "
10631063
"not " + path_type.__name__)
1064+
except TypeError:
1065+
if path_type.__fspath__ is None:
1066+
raise TypeError("expected str, bytes or os.PathLike object, "
1067+
"not " + path_type.__name__) from None
1068+
else:
1069+
raise
10641070
if isinstance(path_repr, (str, bytes)):
10651071
return path_repr
10661072
else:

Lib/test/test_os.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4647,6 +4647,45 @@ def __fspath__(self):
46474647
return ''
46484648
self.assertFalse(hasattr(A(), '__dict__'))
46494649

4650+
def test_fspath_set_to_None(self):
4651+
class Foo:
4652+
__fspath__ = None
4653+
4654+
class Bar:
4655+
def __fspath__(self):
4656+
return 'bar'
4657+
4658+
class Baz(Bar):
4659+
__fspath__ = None
4660+
4661+
good_error_msg = (
4662+
r"expected str, bytes or os.PathLike object, not {}".format
4663+
)
4664+
4665+
with self.assertRaisesRegex(TypeError, good_error_msg("Foo")):
4666+
self.fspath(Foo())
4667+
4668+
self.assertEqual(self.fspath(Bar()), 'bar')
4669+
4670+
with self.assertRaisesRegex(TypeError, good_error_msg("Baz")):
4671+
self.fspath(Baz())
4672+
4673+
with self.assertRaisesRegex(TypeError, good_error_msg("Foo")):
4674+
open(Foo())
4675+
4676+
with self.assertRaisesRegex(TypeError, good_error_msg("Baz")):
4677+
open(Baz())
4678+
4679+
other_good_error_msg = (
4680+
r"should be string, bytes or os.PathLike, not {}".format
4681+
)
4682+
4683+
with self.assertRaisesRegex(TypeError, other_good_error_msg("Foo")):
4684+
os.rename(Foo(), "foooo")
4685+
4686+
with self.assertRaisesRegex(TypeError, other_good_error_msg("Baz")):
4687+
os.rename(Baz(), "bazzz")
4688+
46504689
class TimesTests(unittest.TestCase):
46514690
def test_times(self):
46524691
times = os.times()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Improve the error message from :func:`os.fspath` if called on an object
2+
where ``__fspath__`` is set to ``None``. Patch by Alex Waygood.

Modules/posixmodule.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,7 +1197,7 @@ path_converter(PyObject *o, void *p)
11971197
PyObject *func, *res;
11981198

11991199
func = _PyObject_LookupSpecial(o, &_Py_ID(__fspath__));
1200-
if (NULL == func) {
1200+
if ((NULL == func) || (func == Py_None)) {
12011201
goto error_format;
12021202
}
12031203
res = _PyObject_CallNoArgs(func);
@@ -15430,7 +15430,7 @@ PyOS_FSPath(PyObject *path)
1543015430
}
1543115431

1543215432
func = _PyObject_LookupSpecial(path, &_Py_ID(__fspath__));
15433-
if (NULL == func) {
15433+
if ((NULL == func) || (func == Py_None)) {
1543415434
return PyErr_Format(PyExc_TypeError,
1543515435
"expected str, bytes or os.PathLike object, "
1543615436
"not %.200s",

0 commit comments

Comments
 (0)
0