diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 46d49dfa5dd..18b539b098e 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -1994,6 +1994,9 @@ features: control over errors, you can catch :exc:`OSError` when calling one of the ``DirEntry`` methods and handle as appropriate. + To be directly usable as a path-like object, ``DirEntry`` implements the + :class:`os.PathLike` interface. + Attributes and methods on a ``DirEntry`` instance are as follows: .. attribute:: name @@ -2106,6 +2109,9 @@ features: .. versionadded:: 3.5 + .. versionchanged:: 3.6 + Added support for the :class:`os.PathLike` interface. + .. function:: stat(path, \*, dir_fd=None, follow_symlinks=True) diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 6853ebbd7ff..3f955713c43 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2824,11 +2824,13 @@ class TestScandir(unittest.TestCase): def setUp(self): self.path = os.path.realpath(support.TESTFN) + self.bytes_path = os.fsencode(self.path) self.addCleanup(support.rmtree, self.path) os.mkdir(self.path) def create_file(self, name="file.txt"): - filename = os.path.join(self.path, name) + path = self.bytes_path if isinstance(name, bytes) else self.path + filename = os.path.join(path, name) create_file(filename, b'python') return filename @@ -2917,15 +2919,16 @@ class TestScandir(unittest.TestCase): self.check_entry(entry, 'symlink_file.txt', False, True, True) def get_entry(self, name): - entries = list(os.scandir(self.path)) + path = self.bytes_path if isinstance(name, bytes) else self.path + entries = list(os.scandir(path)) self.assertEqual(len(entries), 1) entry = entries[0] self.assertEqual(entry.name, name) return entry - def create_file_entry(self): - filename = self.create_file() + def create_file_entry(self, name='file.txt'): + filename = self.create_file(name=name) return self.get_entry(os.path.basename(filename)) def test_current_directory(self): @@ -2946,6 +2949,18 @@ class TestScandir(unittest.TestCase): entry = self.create_file_entry() self.assertEqual(repr(entry), "") + def test_fspath_protocol(self): + entry = self.create_file_entry() + self.assertEqual(os.fspath(entry), os.path.join(self.path, 'file.txt')) + + def test_fspath_protocol_bytes(self): + bytes_filename = os.fsencode('bytesfile.txt') + bytes_entry = self.create_file_entry(name=bytes_filename) + fspath = os.fspath(bytes_entry) + self.assertIsInstance(fspath, bytes) + self.assertEqual(fspath, + os.path.join(os.fsencode(self.path),bytes_filename)) + def test_removed_dir(self): path = os.path.join(self.path, 'dir') diff --git a/Misc/NEWS b/Misc/NEWS index 39b1cffbe13..a4f857eea42 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -38,11 +38,14 @@ Core and Builtins Library ------- +- Issue #27186: Add os.PathLike support to DirEntry (part of PEP 519). + Initial patch by Jelle Zijlstra. + - Issue #20900: distutils register command now decodes HTTP responses correctly. Initial patch by ingrid. - Issue #27186: Add os.PathLike support to pathlib, removing its provisional - status (part of PEP 519). + status (part of PEP 519). Initial patch by Dusty Phillips. - Issue #27186: Add support for os.PathLike objects to os.fsencode() and os.fsdecode() (part of PEP 519). diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index f4510dbec9a..ecdeab4925c 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -11718,6 +11718,13 @@ DirEntry_repr(DirEntry *self) return PyUnicode_FromFormat("", self->name); } +static PyObject * +DirEntry_fspath(DirEntry *self) +{ + Py_INCREF(self->path); + return self->path; +} + static PyMemberDef DirEntry_members[] = { {"name", T_OBJECT_EX, offsetof(DirEntry, name), READONLY, "the entry's base filename, relative to scandir() \"path\" argument"}, @@ -11742,6 +11749,9 @@ static PyMethodDef DirEntry_methods[] = { {"inode", (PyCFunction)DirEntry_inode, METH_NOARGS, "return inode of the entry; cached per entry", }, + {"__fspath__", (PyCFunction)DirEntry_fspath, METH_NOARGS, + "returns the path for the entry", + }, {NULL} };