Skip to content

Commit adb6632

Browse files
Merge pull request RustPython#852 from palaviv/scandir
Add os.scandir
2 parents d55d884 + 64e6ea0 commit adb6632

File tree

2 files changed

+123
-1
lines changed

2 files changed

+123
-1
lines changed

tests/snippets/stdlib_os.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ def __exit__(self, exc_type, exc_val, exc_tb):
5656

5757

5858
FILE_NAME = "test1"
59+
FILE_NAME2 = "test2"
60+
FOLDER = "dir1"
5961
CONTENT = b"testing"
6062
CONTENT2 = b"rustpython"
6163
CONTENT3 = b"BOYA"
@@ -73,3 +75,26 @@ def __exit__(self, exc_type, exc_val, exc_tb):
7375
assert os.read(fd, len(CONTENT2)) == CONTENT2
7476
assert os.read(fd, len(CONTENT3)) == CONTENT3
7577
os.close(fd)
78+
79+
fname2 = tmpdir + os.sep + FILE_NAME2
80+
with open(fname2, "wb"):
81+
pass
82+
folder = tmpdir + os.sep + FOLDER
83+
os.mkdir(folder)
84+
85+
names = set()
86+
paths = set()
87+
dirs = set()
88+
files = set()
89+
for dir_entry in os.scandir(tmpdir):
90+
names.add(dir_entry.name)
91+
paths.add(dir_entry.path)
92+
if dir_entry.is_dir():
93+
dirs.add(dir_entry.name)
94+
if dir_entry.is_file():
95+
files.add(dir_entry.name)
96+
97+
assert names == set([FILE_NAME, FILE_NAME2, FOLDER])
98+
assert paths == set([fname, fname2, folder])
99+
assert dirs == set([FOLDER])
100+
assert files == set([FILE_NAME, FILE_NAME2])

vm/src/stdlib/os.rs

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::cell::RefCell;
12
use std::fs::File;
23
use std::fs::OpenOptions;
34
use std::io::{ErrorKind, Read, Write};
@@ -10,9 +11,11 @@ use crate::obj::objbytes::PyBytesRef;
1011
use crate::obj::objdict::PyDictRef;
1112
use crate::obj::objint;
1213
use crate::obj::objint::PyIntRef;
14+
use crate::obj::objiter;
1315
use crate::obj::objstr;
1416
use crate::obj::objstr::PyStringRef;
15-
use crate::pyobject::{ItemProtocol, PyObjectRef, PyResult};
17+
use crate::obj::objtype::PyClassRef;
18+
use crate::pyobject::{ItemProtocol, PyObjectRef, PyRef, PyResult, PyValue};
1619
use crate::vm::VirtualMachine;
1720

1821
#[cfg(unix)]
@@ -190,6 +193,85 @@ fn _os_environ(vm: &VirtualMachine) -> PyDictRef {
190193
environ
191194
}
192195

196+
#[derive(Debug)]
197+
struct DirEntry {
198+
entry: fs::DirEntry,
199+
}
200+
201+
type DirEntryRef = PyRef<DirEntry>;
202+
203+
impl PyValue for DirEntry {
204+
fn class(vm: &VirtualMachine) -> PyClassRef {
205+
vm.class("os", "DirEntry")
206+
}
207+
}
208+
209+
impl DirEntryRef {
210+
fn name(self, _vm: &VirtualMachine) -> String {
211+
self.entry.file_name().into_string().unwrap()
212+
}
213+
214+
fn path(self, _vm: &VirtualMachine) -> String {
215+
self.entry.path().to_str().unwrap().to_string()
216+
}
217+
218+
fn is_dir(self, vm: &VirtualMachine) -> PyResult<bool> {
219+
Ok(self
220+
.entry
221+
.file_type()
222+
.map_err(|s| vm.new_os_error(s.to_string()))?
223+
.is_dir())
224+
}
225+
226+
fn is_file(self, vm: &VirtualMachine) -> PyResult<bool> {
227+
Ok(self
228+
.entry
229+
.file_type()
230+
.map_err(|s| vm.new_os_error(s.to_string()))?
231+
.is_file())
232+
}
233+
}
234+
235+
#[derive(Debug)]
236+
pub struct ScandirIterator {
237+
entries: RefCell<fs::ReadDir>,
238+
}
239+
240+
impl PyValue for ScandirIterator {
241+
fn class(vm: &VirtualMachine) -> PyClassRef {
242+
vm.class("os", "ScandirIter")
243+
}
244+
}
245+
246+
type ScandirIteratorRef = PyRef<ScandirIterator>;
247+
248+
impl ScandirIteratorRef {
249+
fn next(self, vm: &VirtualMachine) -> PyResult {
250+
match self.entries.borrow_mut().next() {
251+
Some(entry) => match entry {
252+
Ok(entry) => Ok(DirEntry { entry }.into_ref(vm).into_object()),
253+
Err(s) => Err(vm.new_os_error(s.to_string())),
254+
},
255+
None => Err(objiter::new_stop_iteration(vm)),
256+
}
257+
}
258+
259+
fn iter(self, _vm: &VirtualMachine) -> Self {
260+
self
261+
}
262+
}
263+
264+
fn os_scandir(path: PyStringRef, vm: &VirtualMachine) -> PyResult {
265+
match fs::read_dir(&path.value) {
266+
Ok(iter) => Ok(ScandirIterator {
267+
entries: RefCell::new(iter),
268+
}
269+
.into_ref(vm)
270+
.into_object()),
271+
Err(s) => Err(vm.new_os_error(s.to_string())),
272+
}
273+
}
274+
193275
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
194276
let ctx = &vm.ctx;
195277

@@ -201,6 +283,18 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
201283

202284
let environ = _os_environ(vm);
203285

286+
let scandir_iter = py_class!(ctx, "ScandirIter", ctx.object(), {
287+
"__iter__" => ctx.new_rustfunc(ScandirIteratorRef::iter),
288+
"__next__" => ctx.new_rustfunc(ScandirIteratorRef::next),
289+
});
290+
291+
let dir_entry = py_class!(ctx, "DirEntry", ctx.object(), {
292+
"name" => ctx.new_property(DirEntryRef::name),
293+
"path" => ctx.new_property(DirEntryRef::path),
294+
"is_dir" => ctx.new_rustfunc(DirEntryRef::is_dir),
295+
"is_file" => ctx.new_rustfunc(DirEntryRef::is_file),
296+
});
297+
204298
py_module!(vm, "_os", {
205299
"open" => ctx.new_rustfunc(os_open),
206300
"close" => ctx.new_rustfunc(os_close),
@@ -217,6 +311,9 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
217311
"unsetenv" => ctx.new_rustfunc(os_unsetenv),
218312
"environ" => environ,
219313
"name" => ctx.new_str(os_name),
314+
"scandir" => ctx.new_rustfunc(os_scandir),
315+
"ScandirIter" => scandir_iter,
316+
"DirEntry" => dir_entry,
220317
"O_RDONLY" => ctx.new_int(0),
221318
"O_WRONLY" => ctx.new_int(1),
222319
"O_RDWR" => ctx.new_int(2),

0 commit comments

Comments
 (0)