Skip to content

Commit b01e960

Browse files
committed
Add st_{a,m,c}time to os.stat
1 parent 0a683f2 commit b01e960

File tree

2 files changed

+112
-41
lines changed

2 files changed

+112
-41
lines changed

tests/snippets/stdlib_os.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,13 @@ def __exit__(self, exc_type, exc_val, exc_tb):
143143
print(stat_res.st_gid)
144144
print(stat_res.st_size)
145145
assert stat_res.st_size == len(CONTENT2) + len(CONTENT3)
146+
print(stat_res.st_atime)
147+
print(stat_res.st_ctime)
148+
print(stat_res.st_mtime)
149+
# test that it all of these times are greater than the 10 May 2019, when this test was written
150+
assert stat_res.st_atime > 1557500000
151+
assert stat_res.st_ctime > 1557500000
152+
assert stat_res.st_mtime > 1557500000
146153

147154
# stat default is follow_symlink=True
148155
os.stat(fname).st_ino == os.stat(symlink_file).st_ino

vm/src/stdlib/os.rs

Lines changed: 105 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use std::cell::RefCell;
22
use std::fs::File;
33
use std::fs::OpenOptions;
4-
use std::io::{ErrorKind, Read, Write};
4+
use std::io::{self, ErrorKind, Read, Write};
5+
use std::time::{Duration, SystemTime};
56
use std::{env, fs};
67

78
use num_traits::cast::ToPrimitive;
@@ -261,7 +262,7 @@ impl DirEntryRef {
261262
.is_symlink())
262263
}
263264

264-
fn stat(self, follow_symlinks: FollowSymlinks, vm: &VirtualMachine) -> PyResult {
265+
fn stat(self, follow_symlinks: FollowSymlinks, vm: &VirtualMachine) -> PyResult<StatResult> {
265266
os_stat(self.path(vm).try_into_ref(vm)?, follow_symlinks, vm)
266267
}
267268
}
@@ -317,6 +318,9 @@ struct StatResult {
317318
st_uid: u32,
318319
st_gid: u32,
319320
st_size: u64,
321+
st_atime: f64,
322+
st_ctime: f64,
323+
st_mtime: f64,
320324
}
321325

322326
impl PyValue for StatResult {
@@ -355,47 +359,93 @@ impl StatResultRef {
355359
fn st_size(self, _vm: &VirtualMachine) -> u64 {
356360
self.st_size
357361
}
362+
363+
fn st_atime(self, _vm: &VirtualMachine) -> f64 {
364+
self.st_atime
365+
}
366+
367+
fn st_ctime(self, _vm: &VirtualMachine) -> f64 {
368+
self.st_ctime
369+
}
370+
371+
fn st_mtime(self, _vm: &VirtualMachine) -> f64 {
372+
self.st_mtime
373+
}
374+
}
375+
376+
// Copied code from Duration::as_secs_f64 as it's still unstable
377+
fn duration_as_secs_f64(duration: Duration) -> f64 {
378+
(duration.as_secs() as f64) + (duration.subsec_nanos() as f64) / (1_000_000_000 as f64)
379+
}
380+
381+
fn to_seconds_from_unix_epoch(sys_time: SystemTime) -> f64 {
382+
match sys_time.duration_since(SystemTime::UNIX_EPOCH) {
383+
Ok(duration) => duration_as_secs_f64(duration),
384+
Err(err) => -duration_as_secs_f64(err.duration()),
385+
}
386+
}
387+
388+
fn to_seconds_from_nanos(secs: i64, nanos: i64) -> f64 {
389+
let duration = Duration::new(secs as u64, nanos as u32);
390+
duration_as_secs_f64(duration)
358391
}
359392

360393
#[cfg(unix)]
361394
macro_rules! os_unix_stat_inner {
362-
( $path:expr, $follow_symlinks:expr, $vm:expr) => {{
363-
let metadata = match $follow_symlinks.follow_symlinks {
364-
true => fs::metadata($path),
365-
false => fs::symlink_metadata($path),
366-
};
367-
let meta = metadata.map_err(|s| $vm.new_os_error(s.to_string()))?;
368-
369-
Ok(StatResult {
370-
st_mode: meta.st_mode(),
371-
st_ino: meta.st_ino(),
372-
st_dev: meta.st_dev(),
373-
st_nlink: meta.st_nlink(),
374-
st_uid: meta.st_uid(),
375-
st_gid: meta.st_gid(),
376-
st_size: meta.st_size(),
395+
( $path:expr, $follow_symlinks:expr, $vm:expr ) => {{
396+
fn get_stats(path: &str, follow_symlinks: bool) -> io::Result<StatResult> {
397+
let meta = match follow_symlinks {
398+
true => fs::metadata(path)?,
399+
false => fs::symlink_metadata(path)?,
400+
};
401+
402+
Ok(StatResult {
403+
st_mode: meta.st_mode(),
404+
st_ino: meta.st_ino(),
405+
st_dev: meta.st_dev(),
406+
st_nlink: meta.st_nlink(),
407+
st_uid: meta.st_uid(),
408+
st_gid: meta.st_gid(),
409+
st_size: meta.st_size(),
410+
st_atime: to_seconds_from_unix_epoch(meta.accessed()?),
411+
st_mtime: to_seconds_from_unix_epoch(meta.modified()?),
412+
st_ctime: to_seconds_from_nanos(meta.st_ctime(), meta.st_ctime_nsec()),
413+
})
377414
}
378-
.into_ref($vm)
379-
.into_object())
415+
416+
get_stats(&$path.value, $follow_symlinks.follow_symlinks)
417+
.map_err(|s| $vm.new_os_error(s.to_string()))
380418
}};
381419
}
382420

383421
#[cfg(target_os = "linux")]
384-
fn os_stat(path: PyStringRef, follow_symlinks: FollowSymlinks, vm: &VirtualMachine) -> PyResult {
422+
fn os_stat(
423+
path: PyStringRef,
424+
follow_symlinks: FollowSymlinks,
425+
vm: &VirtualMachine,
426+
) -> PyResult<StatResult> {
385427
use std::os::linux::fs::MetadataExt;
386-
os_unix_stat_inner!(&path.value, follow_symlinks, vm)
428+
os_unix_stat_inner!(path, follow_symlinks, vm)
387429
}
388430

389431
#[cfg(target_os = "macos")]
390-
fn os_stat(path: PyStringRef, follow_symlinks: FollowSymlinks, vm: &VirtualMachine) -> PyResult {
432+
fn os_stat(
433+
path: PyStringRef,
434+
follow_symlinks: FollowSymlinks,
435+
vm: &VirtualMachine,
436+
) -> PyResult<StatResult> {
391437
use std::os::macos::fs::MetadataExt;
392-
os_unix_stat_inner!(&path.value, follow_symlinks, vm)
438+
os_unix_stat_inner!(path, follow_symlinks, vm)
393439
}
394440

395441
#[cfg(target_os = "android")]
396-
fn os_stat(path: PyStringRef, follow_symlinks: FollowSymlinks, vm: &VirtualMachine) -> PyResult {
442+
fn os_stat(
443+
path: PyStringRef,
444+
follow_symlinks: FollowSymlinks,
445+
vm: &VirtualMachine,
446+
) -> PyResult<StatResult> {
397447
use std::os::android::fs::MetadataExt;
398-
os_unix_stat_inner!(&path.value, follow_symlinks, vm)
448+
os_unix_stat_inner!(path, follow_symlinks, vm)
399449
}
400450

401451
// Copied from CPython fileutils.c
@@ -420,24 +470,35 @@ fn attributes_to_mode(attr: u32) -> u32 {
420470
}
421471

422472
#[cfg(windows)]
423-
fn os_stat(path: PyStringRef, follow_symlinks: FollowSymlinks, vm: &VirtualMachine) -> PyResult {
473+
fn os_stat(
474+
path: PyStringRef,
475+
follow_symlinks: FollowSymlinks,
476+
vm: &VirtualMachine,
477+
) -> PyResult<StatResult> {
424478
use std::os::windows::fs::MetadataExt;
425-
let metadata = match follow_symlinks.follow_symlinks {
426-
true => fs::metadata(&path.value),
427-
false => fs::symlink_metadata(&path.value),
428-
};
429-
let meta = metadata.map_err(|s| vm.new_os_error(s.to_string()))?;
430-
Ok(StatResult {
431-
st_mode: attributes_to_mode(meta.file_attributes()),
432-
st_ino: 0, // TODO: Not implemented in std::os::windows::fs::MetadataExt.
433-
st_dev: 0, // TODO: Not implemented in std::os::windows::fs::MetadataExt.
434-
st_nlink: 0, // TODO: Not implemented in std::os::windows::fs::MetadataExt.
435-
st_uid: 0, // 0 on windows
436-
st_gid: 0, // 0 on windows
437-
st_size: meta.file_size(),
479+
480+
fn get_stats(path: &str, follow_symlinks: bool) -> io::Result<StatResult> {
481+
let meta = match follow_symlinks {
482+
true => fs::metadata(path)?,
483+
false => fs::symlink_metadata(path)?,
484+
};
485+
486+
Ok(StatResult {
487+
st_mode: attributes_to_mode(meta.file_attributes()),
488+
st_ino: 0, // TODO: Not implemented in std::os::windows::fs::MetadataExt.
489+
st_dev: 0, // TODO: Not implemented in std::os::windows::fs::MetadataExt.
490+
st_nlink: 0, // TODO: Not implemented in std::os::windows::fs::MetadataExt.
491+
st_uid: 0, // 0 on windows
492+
st_gid: 0, // 0 on windows
493+
st_size: meta.file_size(),
494+
st_atime: to_seconds_from_unix_epoch(meta.accessed()?),
495+
st_mtime: to_seconds_from_unix_epoch(meta.modified()?),
496+
st_ctime: to_seconds_from_unix_epoch(meta.created()?),
497+
})
438498
}
439-
.into_ref(vm)
440-
.into_object())
499+
500+
get_stats(&path.value, follow_symlinks.follow_symlinks)
501+
.map_err(|s| vm.new_os_error(s.to_string()))
441502
}
442503

443504
#[cfg(not(any(
@@ -510,6 +571,9 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
510571
"st_uid" => ctx.new_property(StatResultRef::st_uid),
511572
"st_gid" => ctx.new_property(StatResultRef::st_gid),
512573
"st_size" => ctx.new_property(StatResultRef::st_size),
574+
"st_atime" => ctx.new_property(StatResultRef::st_atime),
575+
"st_ctime" => ctx.new_property(StatResultRef::st_ctime),
576+
"st_mtime" => ctx.new_property(StatResultRef::st_mtime),
513577
});
514578

515579
py_module!(vm, "_os", {

0 commit comments

Comments
 (0)