Skip to content

Commit 82f83ef

Browse files
committed
Support more open flags
1 parent 7817cec commit 82f83ef

File tree

7 files changed

+82
-30
lines changed

7 files changed

+82
-30
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/snippets/stdlib_os.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44

55
from testutils import assert_raises
66

7-
fd = os.open('README.md', 0)
7+
fd = os.open('README.md', os.O_RDONLY)
88
assert fd > 0
99

1010
os.close(fd)
1111
assert_raises(OSError, lambda: os.read(fd, 10))
12-
assert_raises(FileNotFoundError, lambda: os.open('DOES_NOT_EXIST', 0))
12+
assert_raises(FileNotFoundError, lambda: os.open('DOES_NOT_EXIST', os.O_RDONLY))
13+
assert_raises(FileNotFoundError, lambda: os.open('DOES_NOT_EXIST', os.O_WRONLY))
1314

1415

1516
assert os.O_RDONLY == 0
@@ -88,14 +89,17 @@ def __exit__(self, exc_type, exc_val, exc_tb):
8889

8990
with TestWithTempDir() as tmpdir:
9091
fname = os.path.join(tmpdir, FILE_NAME)
91-
with open(fname, "wb"):
92-
pass
93-
fd = os.open(fname, 1)
92+
fd = os.open(fname, os.O_WRONLY | os.O_CREAT | os.O_EXCL)
9493
assert os.write(fd, CONTENT2) == len(CONTENT2)
94+
os.close(fd)
95+
96+
fd = os.open(fname, os.O_WRONLY | os.O_APPEND)
9597
assert os.write(fd, CONTENT3) == len(CONTENT3)
9698
os.close(fd)
9799

98-
fd = os.open(fname, 0)
100+
assert_raises(FileExistsError, lambda: os.open(fname, os.O_WRONLY | os.O_CREAT | os.O_EXCL))
101+
102+
fd = os.open(fname, os.O_RDONLY)
99103
assert os.read(fd, len(CONTENT2)) == CONTENT2
100104
assert os.read(fd, len(CONTENT3)) == CONTENT3
101105
os.close(fd)

vm/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ indexmap = "1.0.2"
3232
crc = "^1.0.0"
3333
bincode = "1.1.4"
3434
unicode_categories = "0.1.1"
35+
bitflags = "1.1"
3536

3637

3738
# TODO: release and publish to crates.io

vm/src/builtins.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -856,6 +856,7 @@ pub fn make_module(vm: &VirtualMachine, module: PyObjectRef) {
856856
"IndexError" => ctx.exceptions.index_error.clone(),
857857
"ImportError" => ctx.exceptions.import_error.clone(),
858858
"FileNotFoundError" => ctx.exceptions.file_not_found_error.clone(),
859+
"FileExistsError" => ctx.exceptions.file_exists_error.clone(),
859860
"StopIteration" => ctx.exceptions.stop_iteration.clone(),
860861
"ZeroDivisionError" => ctx.exceptions.zero_division_error.clone(),
861862
"KeyError" => ctx.exceptions.key_error.clone(),

vm/src/exceptions.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ pub struct ExceptionZoo {
148148
pub base_exception_type: PyClassRef,
149149
pub exception_type: PyClassRef,
150150
pub file_not_found_error: PyClassRef,
151+
pub file_exists_error: PyClassRef,
151152
pub import_error: PyClassRef,
152153
pub index_error: PyClassRef,
153154
pub key_error: PyClassRef,
@@ -203,6 +204,7 @@ impl ExceptionZoo {
203204
let not_implemented_error = create_type("NotImplementedError", &type_type, &runtime_error);
204205
let file_not_found_error = create_type("FileNotFoundError", &type_type, &os_error);
205206
let permission_error = create_type("PermissionError", &type_type, &os_error);
207+
let file_exists_error = create_type("FileExistsError", &type_type, &os_error);
206208

207209
let warning = create_type("Warning", &type_type, &exception_type);
208210
let bytes_warning = create_type("BytesWarning", &type_type, &warning);
@@ -224,6 +226,7 @@ impl ExceptionZoo {
224226
base_exception_type,
225227
exception_type,
226228
file_not_found_error,
229+
file_exists_error,
227230
import_error,
228231
index_error,
229232
key_error,

vm/src/stdlib/io.rs

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,6 @@ use crate::obj::objtype::PyClassRef;
2121
use crate::pyobject::{BufferProtocol, PyObjectRef, PyRef, PyResult, PyValue};
2222
use crate::vm::VirtualMachine;
2323

24-
fn compute_c_flag(mode: &str) -> u16 {
25-
match mode {
26-
"w" => 512,
27-
"x" => 512,
28-
"a" => 8,
29-
"+" => 2,
30-
_ => 0,
31-
}
32-
}
33-
3424
#[derive(Debug)]
3525
struct PyStringIO {
3626
data: RefCell<String>,
@@ -132,6 +122,21 @@ fn buffered_reader_read(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
132122
Ok(vm.ctx.new_bytes(result))
133123
}
134124

125+
fn compute_c_flag(mode: &str) -> u32 {
126+
let flags = match mode {
127+
"w" => os::FileCreationFlags::O_WRONLY | os::FileCreationFlags::O_CREAT,
128+
"x" => {
129+
os::FileCreationFlags::O_WRONLY
130+
| os::FileCreationFlags::O_CREAT
131+
| os::FileCreationFlags::O_EXCL
132+
}
133+
"a" => os::FileCreationFlags::O_APPEND,
134+
"+" => os::FileCreationFlags::O_RDWR,
135+
_ => os::FileCreationFlags::O_RDONLY,
136+
};
137+
flags.bits()
138+
}
139+
135140
fn file_io_init(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
136141
arg_check!(
137142
vm,

vm/src/stdlib/os.rs

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::io::{self, ErrorKind, Read, Write};
55
use std::time::{Duration, SystemTime};
66
use std::{env, fs};
77

8+
use bitflags::bitflags;
89
use num_traits::cast::ToPrimitive;
910

1011
use crate::function::{IntoPyNativeFunc, PyFuncArgs};
@@ -81,13 +82,26 @@ pub fn os_close(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
8182
Ok(vm.get_none())
8283
}
8384

85+
bitflags! {
86+
pub struct FileCreationFlags: u32 {
87+
// https://elixir.bootlin.com/linux/v4.8/source/include/uapi/asm-generic/fcntl.h
88+
const O_RDONLY = 0o0000_0000;
89+
const O_WRONLY = 0o0000_0001;
90+
const O_RDWR = 0o0000_0002;
91+
const O_CREAT = 0o0000_0100;
92+
const O_EXCL = 0o0000_0200;
93+
const O_APPEND = 0o0000_2000;
94+
const O_NONBLOCK = 0o0000_4000;
95+
}
96+
}
97+
8498
pub fn os_open(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
8599
arg_check!(
86100
vm,
87101
args,
88102
required = [
89103
(name, Some(vm.ctx.str_type())),
90-
(mode, Some(vm.ctx.int_type()))
104+
(flags, Some(vm.ctx.int_type()))
91105
],
92106
optional = [(dir_fd, Some(vm.ctx.int_type()))]
93107
);
@@ -102,14 +116,32 @@ pub fn os_open(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
102116
};
103117
let fname = &make_path(vm, name, &dir_fd).value;
104118

105-
let handle = match objint::get_value(mode).to_u16().unwrap() {
106-
0 => OpenOptions::new().read(true).open(&fname),
107-
1 => OpenOptions::new().write(true).open(&fname),
108-
2 => OpenOptions::new().read(true).write(true).open(&fname),
109-
512 => OpenOptions::new().write(true).create(true).open(&fname),
110-
_ => OpenOptions::new().read(true).open(&fname),
119+
let flags = FileCreationFlags::from_bits(objint::get_value(flags).to_u32().unwrap())
120+
.ok_or(vm.new_value_error("Unsupported flag".to_string()))?;
121+
122+
let mut options = &mut OpenOptions::new();
123+
124+
if flags.contains(FileCreationFlags::O_WRONLY) {
125+
options = options.write(true);
126+
} else if flags.contains(FileCreationFlags::O_RDWR) {
127+
options = options.read(true).write(true);
128+
} else {
129+
options = options.read(true);
111130
}
112-
.map_err(|err| match err.kind() {
131+
132+
if flags.contains(FileCreationFlags::O_APPEND) {
133+
options = options.append(true);
134+
}
135+
136+
if flags.contains(FileCreationFlags::O_CREAT) {
137+
if flags.contains(FileCreationFlags::O_EXCL) {
138+
options = options.create_new(true);
139+
} else {
140+
options = options.create(true);
141+
}
142+
}
143+
144+
let handle = options.open(&fname).map_err(|err| match err.kind() {
113145
ErrorKind::NotFound => {
114146
let exc_type = vm.ctx.exceptions.file_not_found_error.clone();
115147
vm.new_exception(exc_type, format!("No such file or directory: {}", &fname))
@@ -118,6 +150,10 @@ pub fn os_open(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
118150
let exc_type = vm.ctx.exceptions.permission_error.clone();
119151
vm.new_exception(exc_type, format!("Permission denied: {}", &fname))
120152
}
153+
ErrorKind::AlreadyExists => {
154+
let exc_type = vm.ctx.exceptions.file_exists_error.clone();
155+
vm.new_exception(exc_type, format!("File exists: {}", &fname))
156+
}
121157
_ => vm.new_value_error("Unhandled file IO error".to_string()),
122158
})?;
123159

@@ -743,12 +779,13 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
743779
"getcwd" => ctx.new_rustfunc(os_getcwd),
744780
"chdir" => ctx.new_rustfunc(os_chdir),
745781
"fspath" => ctx.new_rustfunc(os_fspath),
746-
"O_RDONLY" => ctx.new_int(0),
747-
"O_WRONLY" => ctx.new_int(1),
748-
"O_RDWR" => ctx.new_int(2),
749-
"O_NONBLOCK" => ctx.new_int(4),
750-
"O_APPEND" => ctx.new_int(8),
751-
"O_CREAT" => ctx.new_int(512)
782+
"O_RDONLY" => ctx.new_int(FileCreationFlags::O_RDONLY.bits()),
783+
"O_WRONLY" => ctx.new_int(FileCreationFlags::O_WRONLY.bits()),
784+
"O_RDWR" => ctx.new_int(FileCreationFlags::O_RDWR.bits()),
785+
"O_NONBLOCK" => ctx.new_int(FileCreationFlags::O_NONBLOCK.bits()),
786+
"O_APPEND" => ctx.new_int(FileCreationFlags::O_APPEND.bits()),
787+
"O_EXCL" => ctx.new_int(FileCreationFlags::O_EXCL.bits()),
788+
"O_CREAT" => ctx.new_int(FileCreationFlags::O_CREAT.bits())
752789
});
753790

754791
for support in support_funcs {

0 commit comments

Comments
 (0)