1
1
use std:: cell:: RefCell ;
2
2
use std:: fs:: File ;
3
3
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 } ;
5
6
use std:: { env, fs} ;
6
7
7
8
use num_traits:: cast:: ToPrimitive ;
@@ -261,7 +262,7 @@ impl DirEntryRef {
261
262
. is_symlink ( ) )
262
263
}
263
264
264
- fn stat ( self , follow_symlinks : FollowSymlinks , vm : & VirtualMachine ) -> PyResult {
265
+ fn stat ( self , follow_symlinks : FollowSymlinks , vm : & VirtualMachine ) -> PyResult < StatResult > {
265
266
os_stat ( self . path ( vm) . try_into_ref ( vm) ?, follow_symlinks, vm)
266
267
}
267
268
}
@@ -317,6 +318,9 @@ struct StatResult {
317
318
st_uid : u32 ,
318
319
st_gid : u32 ,
319
320
st_size : u64 ,
321
+ st_atime : f64 ,
322
+ st_ctime : f64 ,
323
+ st_mtime : f64 ,
320
324
}
321
325
322
326
impl PyValue for StatResult {
@@ -355,47 +359,93 @@ impl StatResultRef {
355
359
fn st_size ( self , _vm : & VirtualMachine ) -> u64 {
356
360
self . st_size
357
361
}
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)
358
391
}
359
392
360
393
#[ cfg( unix) ]
361
394
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
+ } )
377
414
}
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( ) ) )
380
418
} } ;
381
419
}
382
420
383
421
#[ 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 > {
385
427
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)
387
429
}
388
430
389
431
#[ 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 > {
391
437
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)
393
439
}
394
440
395
441
#[ 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 > {
397
447
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)
399
449
}
400
450
401
451
// Copied from CPython fileutils.c
@@ -420,24 +470,35 @@ fn attributes_to_mode(attr: u32) -> u32 {
420
470
}
421
471
422
472
#[ 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 > {
424
478
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
+ } )
438
498
}
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 ( ) ) )
441
502
}
442
503
443
504
#[ cfg( not( any(
@@ -510,6 +571,9 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
510
571
"st_uid" => ctx. new_property( StatResultRef :: st_uid) ,
511
572
"st_gid" => ctx. new_property( StatResultRef :: st_gid) ,
512
573
"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) ,
513
577
} ) ;
514
578
515
579
py_module ! ( vm, "_os" , {
0 commit comments