Skip to content

Commit d3927ff

Browse files
authored
Merge pull request RustPython#1033 from palaviv/open-flags
Support more open flags
2 parents 7817cec + 92ad30e commit d3927ff

File tree

7 files changed

+86
-31
lines changed

7 files changed

+86
-31
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: 55 additions & 15 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,15 +82,31 @@ 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
],
92-
optional = [(dir_fd, Some(vm.ctx.int_type()))]
106+
optional = [
107+
(_mode, Some(vm.ctx.int_type())),
108+
(dir_fd, Some(vm.ctx.int_type()))
109+
]
93110
);
94111

95112
let name = name.clone().downcast::<PyString>().unwrap();
@@ -102,14 +119,32 @@ pub fn os_open(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
102119
};
103120
let fname = &make_path(vm, name, &dir_fd).value;
104121

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),
122+
let flags = FileCreationFlags::from_bits(objint::get_value(flags).to_u32().unwrap())
123+
.ok_or(vm.new_value_error("Unsupported flag".to_string()))?;
124+
125+
let mut options = &mut OpenOptions::new();
126+
127+
if flags.contains(FileCreationFlags::O_WRONLY) {
128+
options = options.write(true);
129+
} else if flags.contains(FileCreationFlags::O_RDWR) {
130+
options = options.read(true).write(true);
131+
} else {
132+
options = options.read(true);
111133
}
112-
.map_err(|err| match err.kind() {
134+
135+
if flags.contains(FileCreationFlags::O_APPEND) {
136+
options = options.append(true);
137+
}
138+
139+
if flags.contains(FileCreationFlags::O_CREAT) {
140+
if flags.contains(FileCreationFlags::O_EXCL) {
141+
options = options.create_new(true);
142+
} else {
143+
options = options.create(true);
144+
}
145+
}
146+
147+
let handle = options.open(&fname).map_err(|err| match err.kind() {
113148
ErrorKind::NotFound => {
114149
let exc_type = vm.ctx.exceptions.file_not_found_error.clone();
115150
vm.new_exception(exc_type, format!("No such file or directory: {}", &fname))
@@ -118,6 +153,10 @@ pub fn os_open(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
118153
let exc_type = vm.ctx.exceptions.permission_error.clone();
119154
vm.new_exception(exc_type, format!("Permission denied: {}", &fname))
120155
}
156+
ErrorKind::AlreadyExists => {
157+
let exc_type = vm.ctx.exceptions.file_exists_error.clone();
158+
vm.new_exception(exc_type, format!("File exists: {}", &fname))
159+
}
121160
_ => vm.new_value_error("Unhandled file IO error".to_string()),
122161
})?;
123162

@@ -743,12 +782,13 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
743782
"getcwd" => ctx.new_rustfunc(os_getcwd),
744783
"chdir" => ctx.new_rustfunc(os_chdir),
745784
"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)
785+
"O_RDONLY" => ctx.new_int(FileCreationFlags::O_RDONLY.bits()),
786+
"O_WRONLY" => ctx.new_int(FileCreationFlags::O_WRONLY.bits()),
787+
"O_RDWR" => ctx.new_int(FileCreationFlags::O_RDWR.bits()),
788+
"O_NONBLOCK" => ctx.new_int(FileCreationFlags::O_NONBLOCK.bits()),
789+
"O_APPEND" => ctx.new_int(FileCreationFlags::O_APPEND.bits()),
790+
"O_EXCL" => ctx.new_int(FileCreationFlags::O_EXCL.bits()),
791+
"O_CREAT" => ctx.new_int(FileCreationFlags::O_CREAT.bits())
752792
});
753793

754794
for support in support_funcs {

0 commit comments

Comments
 (0)