Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit d887948

Browse filesBrowse files
authored
Merge pull request #4782 from youknowone/bytes-as-osstr
relocate FsPath from stdlib::os and rename PyPathLike to OsPath
2 parents e429270 + 69280ee commit d887948
Copy full SHA for d887948

File tree

12 files changed

+311
-264
lines changed
Filter options

12 files changed

+311
-264
lines changed

‎common/src/os.rs

Copy file name to clipboardExpand all lines: common/src/os.rs
+17-1Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// TODO: we can move more os-specific bindings/interfaces from stdlib::{os, posix, nt} to here
22

3-
use std::io;
3+
use std::{io, str::Utf8Error};
44

55
#[cfg(windows)]
66
pub fn errno() -> io::Error {
@@ -21,3 +21,19 @@ pub fn errno() -> io::Error {
2121
pub fn errno() -> io::Error {
2222
io::Error::last_os_error()
2323
}
24+
25+
#[cfg(unix)]
26+
pub fn bytes_as_osstr(b: &[u8]) -> Result<&std::ffi::OsStr, Utf8Error> {
27+
use std::os::unix::ffi::OsStrExt;
28+
Ok(std::ffi::OsStr::from_bytes(b))
29+
}
30+
31+
#[cfg(not(unix))]
32+
pub fn bytes_as_osstr(b: &[u8]) -> Result<&std::ffi::OsStr, Utf8Error> {
33+
Ok(std::str::from_utf8(b)?.as_ref())
34+
}
35+
36+
#[cfg(unix)]
37+
pub use std::os::unix::ffi;
38+
#[cfg(target_os = "wasi")]
39+
pub use std::os::wasi::ffi;

‎stdlib/src/posixsubprocess.rs

Copy file name to clipboardExpand all lines: stdlib/src/posixsubprocess.rs
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::vm::{
22
builtins::PyListRef,
33
function::ArgSequence,
4-
stdlib::{os::PyPathLike, posix},
4+
stdlib::{os::OsPath, posix},
55
{PyObjectRef, PyResult, TryFromObject, VirtualMachine},
66
};
77
use nix::{errno::Errno, unistd};
@@ -60,7 +60,7 @@ struct CStrPathLike {
6060
}
6161
impl TryFromObject for CStrPathLike {
6262
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
63-
let s = PyPathLike::try_from_object(vm, obj)?.into_cstring(vm)?;
63+
let s = OsPath::try_from_object(vm, obj)?.into_cstring(vm)?;
6464
Ok(CStrPathLike { s })
6565
}
6666
}

‎stdlib/src/socket.rs

Copy file name to clipboardExpand all lines: stdlib/src/socket.rs
+3-5Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ mod _socket {
1414
use crate::vm::{
1515
builtins::{PyBaseExceptionRef, PyListRef, PyStrRef, PyTupleRef, PyTypeRef},
1616
convert::{IntoPyException, ToPyObject, TryFromBorrowedObject, TryFromObject},
17-
function::{ArgBytesLike, ArgMemoryBuffer, Either, OptionalArg, OptionalOption},
17+
function::{ArgBytesLike, ArgMemoryBuffer, Either, FsPath, OptionalArg, OptionalOption},
1818
types::{DefaultConstructor, Initializer},
1919
utils::ToCString,
2020
AsObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
@@ -1977,12 +1977,10 @@ mod _socket {
19771977

19781978
#[cfg(not(target_os = "redox"))]
19791979
#[pyfunction]
1980-
fn if_nametoindex(name: PyObjectRef, vm: &VirtualMachine) -> PyResult<IfIndex> {
1981-
let name = crate::vm::stdlib::os::FsPath::try_from(name, true, vm)?;
1982-
let name = ffi::CString::new(name.as_bytes()).map_err(|err| err.into_pyexception(vm))?;
1980+
fn if_nametoindex(name: FsPath, vm: &VirtualMachine) -> PyResult<IfIndex> {
1981+
let name = name.to_cstring(vm)?;
19831982

19841983
let ret = unsafe { c::if_nametoindex(name.as_ptr()) };
1985-
19861984
if ret == 0 {
19871985
Err(vm.new_os_error("no interface with this name".to_owned()))
19881986
} else {

‎stdlib/src/sqlite.rs

Copy file name to clipboardExpand all lines: stdlib/src/sqlite.rs
+3-4Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,9 @@ mod _sqlite {
5858
PyInt, PyIntRef, PySlice, PyStr, PyStrRef, PyTuple, PyTupleRef, PyType, PyTypeRef,
5959
},
6060
convert::IntoObject,
61-
function::{ArgCallable, ArgIterable, FuncArgs, OptionalArg, PyComparisonValue},
61+
function::{ArgCallable, ArgIterable, FsPath, FuncArgs, OptionalArg, PyComparisonValue},
6262
protocol::{PyBuffer, PyIterReturn, PyMappingMethods, PySequence, PySequenceMethods},
6363
sliceable::{SaturatedSliceIter, SliceableSequenceOp},
64-
stdlib::os::PyPathLike,
6564
types::{
6665
AsMapping, AsSequence, Callable, Comparable, Constructor, Hashable, IterNext,
6766
IterNextIterable, Iterable, PyComparisonOp,
@@ -293,7 +292,7 @@ mod _sqlite {
293292
#[derive(FromArgs)]
294293
struct ConnectArgs {
295294
#[pyarg(any)]
296-
database: PyPathLike,
295+
database: FsPath,
297296
#[pyarg(any, default = "5.0")]
298297
timeout: f64,
299298
#[pyarg(any, default = "0")]
@@ -828,7 +827,7 @@ mod _sqlite {
828827
#[pyclass(with(Constructor, Callable), flags(BASETYPE))]
829828
impl Connection {
830829
fn new(args: ConnectArgs, vm: &VirtualMachine) -> PyResult<Self> {
831-
let path = args.database.into_cstring(vm)?;
830+
let path = args.database.to_cstring(vm)?;
832831
let db = Sqlite::from(SqliteRaw::open(path.as_ptr(), args.uri, vm)?);
833832
let timeout = (args.timeout * 1000.0) as c_int;
834833
db.busy_timeout(timeout);

‎stdlib/src/ssl.rs

Copy file name to clipboardExpand all lines: stdlib/src/ssl.rs
+10-7Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ mod _ssl {
3636
convert::{ToPyException, ToPyObject},
3737
exceptions,
3838
function::{
39-
ArgBytesLike, ArgCallable, ArgMemoryBuffer, ArgStrOrBytesLike, Either, OptionalArg,
39+
ArgBytesLike, ArgCallable, ArgMemoryBuffer, ArgStrOrBytesLike, Either, FsPath,
40+
OptionalArg,
4041
},
41-
stdlib::os::PyPathLike,
4242
types::Constructor,
4343
utils::ToCString,
4444
PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
@@ -726,10 +726,12 @@ mod _ssl {
726726
);
727727
}
728728
let mut ctx = self.builder();
729-
ctx.set_certificate_chain_file(&certfile)
729+
let key_path = keyfile.map(|path| path.to_path_buf(vm)).transpose()?;
730+
let cert_path = certfile.to_path_buf(vm)?;
731+
ctx.set_certificate_chain_file(&cert_path)
730732
.and_then(|()| {
731733
ctx.set_private_key_file(
732-
keyfile.as_ref().unwrap_or(&certfile),
734+
key_path.as_ref().unwrap_or(&cert_path),
733735
ssl::SslFiletype::PEM,
734736
)
735737
})
@@ -819,9 +821,9 @@ mod _ssl {
819821

820822
#[derive(FromArgs)]
821823
struct LoadCertChainArgs {
822-
certfile: PyPathLike,
824+
certfile: FsPath,
823825
#[pyarg(any, optional)]
824-
keyfile: Option<PyPathLike>,
826+
keyfile: Option<FsPath>,
825827
#[pyarg(any, optional)]
826828
password: Option<Either<PyStrRef, ArgCallable>>,
827829
}
@@ -1308,7 +1310,8 @@ mod _ssl {
13081310
}
13091311

13101312
#[pyfunction]
1311-
fn _test_decode_cert(path: PyPathLike, vm: &VirtualMachine) -> PyResult {
1313+
fn _test_decode_cert(path: FsPath, vm: &VirtualMachine) -> PyResult {
1314+
let path = path.to_path_buf(vm)?;
13121315
let pem = std::fs::read(path).map_err(|e| e.to_pyexception(vm))?;
13131316
let x509 = X509::from_pem(&pem).map_err(|e| convert_openssl_error(vm, e))?;
13141317
cert_to_py(vm, &x509, false)

‎vm/src/function/fspath.rs

Copy file name to clipboard
+130Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
use crate::{
2+
builtins::{PyBytes, PyBytesRef, PyStrRef},
3+
convert::{IntoPyException, ToPyObject},
4+
function::PyStr,
5+
protocol::PyBuffer,
6+
PyObjectRef, PyResult, TryFromObject, VirtualMachine,
7+
};
8+
use std::{ffi::OsStr, path::PathBuf};
9+
10+
#[derive(Clone)]
11+
pub enum FsPath {
12+
Str(PyStrRef),
13+
Bytes(PyBytesRef),
14+
}
15+
16+
impl FsPath {
17+
// PyOS_FSPath in CPython
18+
pub fn try_from(obj: PyObjectRef, check_for_nul: bool, vm: &VirtualMachine) -> PyResult<Self> {
19+
let check_nul = |b: &[u8]| {
20+
if !check_for_nul || memchr::memchr(b'\0', b).is_none() {
21+
Ok(())
22+
} else {
23+
Err(crate::exceptions::cstring_error(vm))
24+
}
25+
};
26+
let match1 = |obj: PyObjectRef| {
27+
let pathlike = match_class!(match obj {
28+
s @ PyStr => {
29+
check_nul(s.as_str().as_bytes())?;
30+
FsPath::Str(s)
31+
}
32+
b @ PyBytes => {
33+
check_nul(&b)?;
34+
FsPath::Bytes(b)
35+
}
36+
obj => return Ok(Err(obj)),
37+
});
38+
Ok(Ok(pathlike))
39+
};
40+
let obj = match match1(obj)? {
41+
Ok(pathlike) => return Ok(pathlike),
42+
Err(obj) => obj,
43+
};
44+
let method =
45+
vm.get_method_or_type_error(obj.clone(), identifier!(vm, __fspath__), || {
46+
format!(
47+
"should be string, bytes, os.PathLike or integer, not {}",
48+
obj.class().name()
49+
)
50+
})?;
51+
let result = method.call((), vm)?;
52+
match1(result)?.map_err(|result| {
53+
vm.new_type_error(format!(
54+
"expected {}.__fspath__() to return str or bytes, not {}",
55+
obj.class().name(),
56+
result.class().name(),
57+
))
58+
})
59+
}
60+
61+
pub fn as_os_str(&self, vm: &VirtualMachine) -> PyResult<&OsStr> {
62+
// TODO: FS encodings
63+
match self {
64+
FsPath::Str(s) => Ok(s.as_str().as_ref()),
65+
FsPath::Bytes(b) => Self::bytes_as_osstr(b.as_bytes(), vm),
66+
}
67+
}
68+
69+
pub fn as_bytes(&self) -> &[u8] {
70+
// TODO: FS encodings
71+
match self {
72+
FsPath::Str(s) => s.as_str().as_bytes(),
73+
FsPath::Bytes(b) => b.as_bytes(),
74+
}
75+
}
76+
77+
pub fn as_str(&self) -> &str {
78+
match self {
79+
FsPath::Bytes(b) => std::str::from_utf8(b).unwrap(),
80+
FsPath::Str(s) => s.as_str(),
81+
}
82+
}
83+
84+
pub fn to_path_buf(&self, vm: &VirtualMachine) -> PyResult<PathBuf> {
85+
let path = match self {
86+
FsPath::Str(s) => PathBuf::from(s.as_str()),
87+
FsPath::Bytes(b) => PathBuf::from(Self::bytes_as_osstr(b, vm)?),
88+
};
89+
Ok(path)
90+
}
91+
92+
pub fn to_cstring(&self, vm: &VirtualMachine) -> PyResult<std::ffi::CString> {
93+
std::ffi::CString::new(self.as_bytes()).map_err(|e| e.into_pyexception(vm))
94+
}
95+
96+
#[cfg(windows)]
97+
pub fn to_widecstring(&self, vm: &VirtualMachine) -> PyResult<widestring::WideCString> {
98+
widestring::WideCString::from_os_str(self.as_os_str(vm)?)
99+
.map_err(|err| err.into_pyexception(vm))
100+
}
101+
102+
pub fn bytes_as_osstr<'a>(b: &'a [u8], vm: &VirtualMachine) -> PyResult<&'a std::ffi::OsStr> {
103+
rustpython_common::os::bytes_as_osstr(b)
104+
.map_err(|_| vm.new_unicode_decode_error("can't decode path for utf-8".to_owned()))
105+
}
106+
}
107+
108+
impl ToPyObject for FsPath {
109+
fn to_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
110+
match self {
111+
Self::Str(s) => s.into(),
112+
Self::Bytes(b) => b.into(),
113+
}
114+
}
115+
}
116+
117+
impl TryFromObject for FsPath {
118+
// PyUnicode_FSDecoder in CPython
119+
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
120+
let obj = match obj.try_to_value::<PyBuffer>(vm) {
121+
Ok(buffer) => {
122+
let mut bytes = vec![];
123+
buffer.append_to(&mut bytes);
124+
vm.ctx.new_bytes(bytes).into()
125+
}
126+
Err(_) => obj,
127+
};
128+
Self::try_from(obj, true, vm)
129+
}
130+
}

‎vm/src/function/mod.rs

Copy file name to clipboardExpand all lines: vm/src/function/mod.rs
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ mod arithmetic;
33
mod buffer;
44
mod builtin;
55
mod either;
6+
mod fspath;
67
mod getset;
78
mod number;
89
mod protocol;
@@ -16,6 +17,7 @@ pub use buffer::{ArgAsciiBuffer, ArgBytesLike, ArgMemoryBuffer, ArgStrOrBytesLik
1617
pub(self) use builtin::{BorrowedParam, OwnedParam, RefParam};
1718
pub use builtin::{IntoPyNativeFunc, PyNativeFunc};
1819
pub use either::Either;
20+
pub use fspath::FsPath;
1921
pub use getset::PySetterValue;
2022
pub(super) use getset::{IntoPyGetterFunc, IntoPySetterFunc, PyGetterFunc, PySetterFunc};
2123
pub use number::{ArgIndex, ArgIntoBool, ArgIntoComplex, ArgIntoFloat, ArgPrimitiveIndex, ArgSize};

‎vm/src/stdlib/io.rs

Copy file name to clipboardExpand all lines: vm/src/stdlib/io.rs
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3545,7 +3545,7 @@ mod _io {
35453545

35463546
// check file descriptor validity
35473547
#[cfg(unix)]
3548-
if let Ok(crate::stdlib::os::PathOrFd::Fd(fd)) = file.clone().try_into_value(vm) {
3548+
if let Ok(crate::stdlib::os::OsPathOrFd::Fd(fd)) = file.clone().try_into_value(vm) {
35493549
nix::fcntl::fcntl(fd, nix::fcntl::F_GETFD)
35503550
.map_err(|_| crate::stdlib::os::errno_err(vm))?;
35513551
}
@@ -3874,7 +3874,7 @@ mod fileio {
38743874
} else if let Some(i) = name.payload::<crate::builtins::PyInt>() {
38753875
i.try_to_primitive(vm)?
38763876
} else {
3877-
let path = os::PyPathLike::try_from_object(vm, name.clone())?;
3877+
let path = os::OsPath::try_from_object(vm, name.clone())?;
38783878
if !args.closefd {
38793879
return Err(
38803880
vm.new_value_error("Cannot use closefd=False with file name".to_owned())

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.