Skip to content

Commit d874702

Browse files
committed
unix/modos: Implement ilistdir().
ilistdir() returns iterator which yields triples of (name, type, ino) where ino is inode number for entry's data, type of entry (file/dir/etc.), and name of file/dir. listdir() can be easily implemented in terms of this iterator (which is otherwise more efficient in terms of memory use and may save expensive call to stat() for each returned entry). CPython has os.scandir() which also returns an iterator, but it yields more complex objects of DirEntry type. scandir() can also be easily implemented in terms of ilistdir().
1 parent 1a1ccea commit d874702

3 files changed

Lines changed: 57 additions & 0 deletions

File tree

unix/modos.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include <errno.h>
3232
#include <stdlib.h>
3333
#include <string.h>
34+
#include <dirent.h>
3435
#include "py/mpconfig.h"
3536

3637
#include "py/nlr.h"
@@ -155,6 +156,51 @@ STATIC mp_obj_t mod_os_mkdir(mp_obj_t path_in) {
155156
}
156157
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_os_mkdir_obj, mod_os_mkdir);
157158

159+
typedef struct _mp_obj_listdir_t {
160+
mp_obj_base_t base;
161+
mp_fun_1_t iternext;
162+
DIR *dir;
163+
} mp_obj_listdir_t;
164+
165+
STATIC mp_obj_t listdir_next(mp_obj_t self_in) {
166+
mp_obj_listdir_t *self = MP_OBJ_TO_PTR(self_in);
167+
168+
if (self->dir == NULL) {
169+
goto done;
170+
}
171+
struct dirent *dirent = readdir(self->dir);
172+
if (dirent == NULL) {
173+
closedir(self->dir);
174+
self->dir = NULL;
175+
done:
176+
return MP_OBJ_STOP_ITERATION;
177+
}
178+
179+
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL));
180+
t->items[0] = mp_obj_new_str(dirent->d_name, strlen(dirent->d_name), false);
181+
#ifdef _DIRENT_HAVE_D_TYPE
182+
t->items[1] = MP_OBJ_NEW_SMALL_INT(dirent->d_type);
183+
#else
184+
// DT_UNKNOWN should have 0 value on any reasonable system
185+
t->items[1] = 0;
186+
#endif
187+
t->items[2] = MP_OBJ_NEW_SMALL_INT(dirent->d_ino);
188+
return MP_OBJ_FROM_PTR(t);
189+
}
190+
191+
STATIC mp_obj_t mod_os_ilistdir(mp_uint_t n_args, const mp_obj_t *args) {
192+
const char *path = ".";
193+
if (n_args > 0) {
194+
path = mp_obj_str_get_str(args[0]);
195+
}
196+
mp_obj_listdir_t *o = m_new_obj(mp_obj_listdir_t);
197+
o->base.type = &mp_type_polymorph_iter;
198+
o->dir = opendir(path);
199+
o->iternext = listdir_next;
200+
return MP_OBJ_FROM_PTR(o);
201+
}
202+
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_os_ilistdir_obj, 0, 1, mod_os_ilistdir);
203+
158204
STATIC const mp_rom_map_elem_t mp_module_os_globals_table[] = {
159205
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uos) },
160206
{ MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&mod_os_stat_obj) },
@@ -165,6 +211,7 @@ STATIC const mp_rom_map_elem_t mp_module_os_globals_table[] = {
165211
{ MP_ROM_QSTR(MP_QSTR_unlink), MP_ROM_PTR(&mod_os_unlink_obj) },
166212
{ MP_ROM_QSTR(MP_QSTR_getenv), MP_ROM_PTR(&mod_os_getenv_obj) },
167213
{ MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mod_os_mkdir_obj) },
214+
{ MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&mod_os_ilistdir_obj) },
168215
};
169216

170217
STATIC MP_DEFINE_CONST_DICT(mp_module_os_globals, mp_module_os_globals_table);

unix/mpconfigport.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,3 +250,12 @@ extern const struct _mp_obj_fun_builtin_t mp_builtin_open_obj;
250250
#include <alloca.h>
251251
#endif
252252
#endif
253+
254+
// From "man readdir": "Under glibc, programs can check for the availability
255+
// of the fields [in struct dirent] not defined in POSIX.1 by testing whether
256+
// the macros [...], _DIRENT_HAVE_D_TYPE are defined."
257+
// Other libc's don't define it, but proactively assume that dirent->d_type
258+
// is available on a modern *nix system.
259+
#ifndef _DIRENT_HAVE_D_TYPE
260+
#define _DIRENT_HAVE_D_TYPE (1)
261+
#endif

unix/qstrdefsport.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ Q(system)
4343
Q(unlink)
4444
Q(getenv)
4545
Q(mkdir)
46+
Q(ilistdir)
4647

4748
Q(uselect)
4849
Q(poll)

0 commit comments

Comments
 (0)