Skip to content

Commit 41e9d57

Browse files
committed
fix(py): os.stat_result [] returns int while getattr returns float for st_?time(fixes #35)
1 parent 8133e64 commit 41e9d57

File tree

1 file changed

+83
-50
lines changed
  • src/pylib/Lib/os_impl/posix_like

1 file changed

+83
-50
lines changed

src/pylib/Lib/os_impl/posix_like/stat.nim

Lines changed: 83 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11

22
import ../common
3-
4-
const DWin = defined(windows)
3+
import std/macros
54

65
type Time64 = int64
76

@@ -44,57 +43,91 @@ when DWin:
4443
{.pop.}
4544
else:
4645
import std/posix
47-
48-
template toTime(x): untyped = x.tv_sec
49-
template st_atime*(s: Stat): untyped = Time64 toTime s.st_atim
50-
template st_mtime*(s: Stat): untyped = Time64 toTime s.st_mtim
51-
template st_ctime*(s: Stat): untyped = Time64 toTime s.st_ctim
52-
53-
const statHasMore = defined(linux) # XXX: this check is not suitable.
54-
when statHasMore:
55-
type
56-
stat_result* = tuple[
57-
st_mode: Mode, st_ino: Ino, st_dev: Dev, st_nlink: Nlink,
58-
st_uid: Uid, st_gid: Gid, st_size: Off,
59-
st_atime, st_mtime, st_ctime: Time64,
60-
61-
st_blocks: Blkcnt,
62-
st_blksize: Blksize,
63-
st_rdev: Dev,
64-
# st_flags
65-
] ## Python's `os.stat_result` (a NamedTuple)
66-
else:
67-
type
68-
stat_result* = tuple[
69-
st_mode: Mode, st_ino: Ino, st_dev: Dev, st_nlink: Nlink,
70-
st_uid: Uid, st_gid: Gid, st_size: Off,
71-
st_atime, st_mtime, st_ctime: Time64,
72-
] ## Python's `os.stat_result` (a NamedTuple)
7346

74-
import std/macros
47+
type
48+
stat_result* = ref object
49+
data*: Stat ## inner, used by `getattr` of `stat_result`
50+
51+
macro genTimeGetter(amc: static[char]) =
52+
result = newStmtList()
53+
let s_st_xtim = "st_" & amc & "tim"
54+
template get(attr: string): NimNode =
55+
newDotExpr(x, ident(attr))
56+
let
57+
xtim = ident s_st_xtim
58+
xtime = ident(s_st_xtim & 'e')
59+
let
60+
xtime_ns = ident(s_st_xtim & "e_ns")
61+
result = quote do:
62+
func `xtime`*(self: stat_result): float =
63+
when compiles(self.data.`xtim`):
64+
self.data.`xtim`.tv_sec.float + self.data.`xtim`.tv_nsec/1_000_000_000
65+
else:
66+
self.data.`xtime`.float
67+
func `xtime_ns`*(self: stat_result): BiggestInt{.pysince(3,3).} =
68+
when compiles(self.data.`xtim`):
69+
self.data.`xtim`.tv_sec.BiggestInt * 1_000_000_000 +
70+
self.data.`xtim`.tv_nsec.BiggestInt
71+
else:
72+
self.data.`xtime`.BiggestInt * 1_000_000_000
7573

76-
macro to_result(s: Stat): stat_result =
77-
var templ: stat_result
78-
result = nnkTupleConstr.newNimNode
79-
for kStr, _ in templ.fieldPairs:
80-
let k = ident kStr
81-
result.add newColonExpr(k, newDotExpr(s, k))
82-
# `k`: `s`.`k`
83-
84-
proc statFor(st: var Stat, path: int|PathLike) =
85-
let ret =
86-
when path is int:
87-
fstat(path.cint, st)
88-
else:
89-
when DWin:
90-
wstat( newWideCString(path.fspath), st)
74+
genTimeGetter 'a'
75+
genTimeGetter 'm'
76+
genTimeGetter 'c'
77+
78+
{.experimental: "dotOperators".}
79+
template `.`*(self: stat_result; attr): untyped =
80+
## as Python's `__getattr__`
81+
self.data.attr
82+
83+
const visible_size = 10
84+
85+
func getitem(self: stat_result, i: int): BiggestInt =
86+
# this proc was once generated by macro
87+
case i
88+
of 0: result = BiggestInt self.data.st_ino
89+
of 1: result = BiggestInt self.data.st_mode
90+
of 2: result = BiggestInt self.data.st_nlink
91+
of 3: result = BiggestInt self.data.st_uid
92+
of 4: result = BiggestInt self.data.st_gid
93+
of 5: result = BiggestInt self.data.st_dev
94+
of 6: result = BiggestInt self.data.st_size
95+
of 7: result = BiggestInt self.data.st_atime
96+
of 8: result = BiggestInt self.data.st_mtime
97+
of 9: result = BiggestInt self.data.st_ctime
98+
else:
99+
raise newException(IndexDefect, "tuple index out of range")
100+
101+
func `[]`*(self: stat_result, i: int): BiggestInt =
102+
self.getitem(if i < 0: visible_size + i else: i)
103+
104+
func to_result(s: sink Stat): stat_result =
105+
result = stat_result(data: s)
106+
107+
when defined(js):
108+
proc statFor(st: var Stat, path: int|PathLike) =
109+
catchJsErrAndRaise:
110+
st =
111+
when path is int:
112+
fstatSync(path.cint)
113+
else:
114+
statSync cstring $path
115+
116+
else:
117+
proc statFor(st: var Stat, path: int|PathLike) =
118+
let ret =
119+
when path is int:
120+
fstat(path.cint, st)
121+
else:
122+
when DWin:
123+
wstat( newWideCString(path.fspath), st)
124+
else:
125+
stat(cstring path.fspath, st)
126+
if ret != 0.cint:
127+
when path is int:
128+
raiseErrno($path)
91129
else:
92-
stat(cstring path.fspath, st)
93-
if ret != 0.cint:
94-
when path is int:
95-
raiseErrno($path)
96-
else:
97-
path.raiseErrnoWithPath()
130+
path.raiseErrnoWithPath()
98131

99132
template statAttr*(path: PathLike|int, attr: untyped): untyped =
100133
## stat(`path`).`attr`

0 commit comments

Comments
 (0)