Commit fdaea06d authored by Larry Hastings's avatar Larry Hastings

Issue #15176: Clarified behavior, documentation, and implementation

of os.listdir().
parent 3b52778c
...@@ -1488,16 +1488,19 @@ features: ...@@ -1488,16 +1488,19 @@ features:
.. function:: listdir(path='.') .. function:: listdir(path='.')
Return a list containing the names of the entries in the directory given by Return a list containing the names of the entries in the directory given by
*path* (default: ``'.'``). The list is in arbitrary order. It does not *path*. The list is in arbitrary order, and does not include the special
include the special entries ``'.'`` and ``'..'`` even if they are present in entries ``'.'`` and ``'..'`` even if they are present in the directory.
the directory.
This function can be called with a bytes or string argument, and returns *path* may be either of type ``str`` or of type ``bytes``. If *path*
filenames of the same datatype. is of type ``bytes``, the filenames returned will also be of type ``bytes``;
in all other circumstances, they will be of type ``str``.
This function can also support :ref:`specifying a file descriptor This function can also support :ref:`specifying a file descriptor
<path_fd>`; the file descriptor must refer to a directory. <path_fd>`; the file descriptor must refer to a directory.
.. note::
To encode ``str`` filenames to ``bytes``, use :func:`~os.fsencode`.
Availability: Unix, Windows. Availability: Unix, Windows.
.. versionchanged:: 3.2 .. versionchanged:: 3.2
......
...@@ -448,16 +448,21 @@ class PosixTester(unittest.TestCase): ...@@ -448,16 +448,21 @@ class PosixTester(unittest.TestCase):
self.assertRaises(OSError, posix.chdir, support.TESTFN) self.assertRaises(OSError, posix.chdir, support.TESTFN)
def test_listdir(self): def test_listdir(self):
if hasattr(posix, 'listdir'): self.assertTrue(support.TESTFN in posix.listdir(os.curdir))
self.assertTrue(support.TESTFN in posix.listdir(os.curdir))
def test_listdir_default(self): def test_listdir_default(self):
# When listdir is called without argument, it's the same as listdir(os.curdir) # When listdir is called without argument,
if hasattr(posix, 'listdir'): # it's the same as listdir(os.curdir).
self.assertTrue(support.TESTFN in posix.listdir()) self.assertTrue(support.TESTFN in posix.listdir())
@unittest.skipUnless(os.listdir in os.supports_fd, "test needs fd support for os.listdir()") def test_listdir_bytes(self):
def test_flistdir(self): # When listdir is called with a bytes object,
# the returned strings are of type bytes.
self.assertTrue(os.fsencode(support.TESTFN) in posix.listdir(b'.'))
@unittest.skipUnless(posix.listdir in os.supports_fd,
"test needs fd support for posix.listdir()")
def test_listdir_fd(self):
f = posix.open(posix.getcwd(), posix.O_RDONLY) f = posix.open(posix.getcwd(), posix.O_RDONLY)
self.addCleanup(posix.close, f) self.addCleanup(posix.close, f)
self.assertEqual( self.assertEqual(
......
...@@ -59,6 +59,9 @@ Core and Builtins ...@@ -59,6 +59,9 @@ Core and Builtins
Library Library
------- -------
- Issue #15176: Clarified behavior, documentation, and implementation
of os.listdir().
- Issue #15164: Change return value of platform.uname() from a - Issue #15164: Change return value of platform.uname() from a
plain tuple to a collections.namedtuple. plain tuple to a collections.namedtuple.
......
...@@ -3272,14 +3272,16 @@ exit: ...@@ -3272,14 +3272,16 @@ exit:
PyDoc_STRVAR(posix_listdir__doc__, PyDoc_STRVAR(posix_listdir__doc__,
"listdir(path='.') -> list_of_strings\n\n\ "listdir(path='.') -> list_of_filenames\n\n\
Return a list containing the names of the entries in the directory.\n\ Return a list containing the names of the files in the directory.\n\
\n\
The list is in arbitrary order. It does not include the special\n\ The list is in arbitrary order. It does not include the special\n\
entries '.' and '..' even if they are present in the directory.\n\ entries '.' and '..' even if they are present in the directory.\n\
\n\ \n\
path can always be specified as a string.\n\ path can be specified as either str or bytes. If path is bytes,\n\
On some platforms, path may also be specified as an open file descriptor.\n\ the filenames returned will also be bytes; in all other circumstances\n\
the filenames returned will be str.\n\
On some platforms, path may also be specified as an open file descriptor;\n\
the file descriptor must refer to a directory.\n\
If this functionality is unavailable, using it raises NotImplementedError."); If this functionality is unavailable, using it raises NotImplementedError.");
static PyObject * static PyObject *
...@@ -3316,7 +3318,7 @@ posix_listdir(PyObject *self, PyObject *args, PyObject *kwargs) ...@@ -3316,7 +3318,7 @@ posix_listdir(PyObject *self, PyObject *args, PyObject *kwargs)
PyObject *v; PyObject *v;
DIR *dirp = NULL; DIR *dirp = NULL;
struct dirent *ep; struct dirent *ep;
int arg_is_unicode = 1; int return_str; /* if false, return bytes */
#endif #endif
memset(&path, 0, sizeof(path)); memset(&path, 0, sizeof(path));
...@@ -3538,11 +3540,6 @@ exit: ...@@ -3538,11 +3540,6 @@ exit:
#else #else
errno = 0; errno = 0;
/* v is never read, so it does not need to be initialized yet. */
if (path.narrow && !PyArg_ParseTuple(args, "U:listdir", &v)) {
arg_is_unicode = 0;
PyErr_Clear();
}
#ifdef HAVE_FDOPENDIR #ifdef HAVE_FDOPENDIR
if (path.fd != -1) { if (path.fd != -1) {
/* closedir() closes the FD, so we duplicate it */ /* closedir() closes the FD, so we duplicate it */
...@@ -3555,6 +3552,8 @@ exit: ...@@ -3555,6 +3552,8 @@ exit:
goto exit; goto exit;
} }
return_str = 1;
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
dirp = fdopendir(fd); dirp = fdopendir(fd);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
...@@ -3562,7 +3561,17 @@ exit: ...@@ -3562,7 +3561,17 @@ exit:
else else
#endif #endif
{ {
char *name = path.narrow ? path.narrow : "."; char *name;
if (path.narrow) {
name = path.narrow;
/* only return bytes if they specified a bytes object */
return_str = !(PyBytes_Check(path.object));
}
else {
name = ".";
return_str = 1;
}
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
dirp = opendir(name); dirp = opendir(name);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
...@@ -3593,7 +3602,7 @@ exit: ...@@ -3593,7 +3602,7 @@ exit:
(NAMLEN(ep) == 1 || (NAMLEN(ep) == 1 ||
(ep->d_name[1] == '.' && NAMLEN(ep) == 2))) (ep->d_name[1] == '.' && NAMLEN(ep) == 2)))
continue; continue;
if (arg_is_unicode) if (return_str)
v = PyUnicode_DecodeFSDefaultAndSize(ep->d_name, NAMLEN(ep)); v = PyUnicode_DecodeFSDefaultAndSize(ep->d_name, NAMLEN(ep));
else else
v = PyBytes_FromStringAndSize(ep->d_name, NAMLEN(ep)); v = PyBytes_FromStringAndSize(ep->d_name, NAMLEN(ep));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment