Use CRT functions for time module on Windows#7090
Use CRT functions for time module on Windows#7090youknowone wants to merge 2 commits intoRustPython:mainfrom
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughBroadened platform guards and refactored stdlib time handling for cross-platform behavior: added Windows FFI/bindings and helpers (wcsftime, win_mktime, tz extraction), centralized CheckedTm/StructTime conversions and pyobj_to_time_t, unified strftime/mktime paths across Unix and Windows, and preserved non-Unix/Windows fallbacks. Changes
Sequence Diagram(s)sequenceDiagram
participant VM as VirtualMachine
participant Time as stdlib::time
participant OS as libc / Windows API
VM->>Time: call strftime/asctime/ctime/mktime with Py args
Time->>Time: pyobj_to_time_t / tm_from_struct_time / CheckedTm
alt Windows
Time->>OS: win_mktime / wcsftime / GetTimeZoneInformation
OS-->>Time: time_t / wide string / TIME_ZONE_INFORMATION
else Unix
Time->>OS: mktime / libc::strftime / localtime/gmtime
OS-->>Time: time_t / C string / tm struct
end
Time-->>VM: return Python string/StructTime/float
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Code has been automatically formatted The code in this PR has been formatted using:
git pull origin time |
b7251ab to
7870255
Compare
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
crates/vm/src/stdlib/time.rs (1)
498-531: 🛠️ Refactor suggestion | 🟠 MajorMerge duplicate
unix/windowsblocks into a singlecfg(any(unix, windows))block.The
#[cfg(unix)]and#[cfg(windows)]blocks ingmtimeare identical.localtime(line 539) already uses a mergedcfg(any(unix, windows))— do the same here.♻️ Suggested diff
- #[cfg(unix)] - { - let ts = match secs { - OptionalArg::Present(Some(value)) => pyobj_to_time_t(value, vm)?, - OptionalArg::Present(None) | OptionalArg::Missing => current_time_t(), - }; - gmtime_from_timestamp(ts, vm) - } - - #[cfg(windows)] + #[cfg(any(unix, windows))] { let ts = match secs { OptionalArg::Present(Some(value)) => pyobj_to_time_t(value, vm)?, OptionalArg::Present(None) | OptionalArg::Missing => current_time_t(), }; gmtime_from_timestamp(ts, vm) }As per coding guidelines: "When branches differ only in a value but share common logic, extract the differing value first, then call the common logic once to avoid duplicate code."
🧹 Nitpick comments (3)
crates/vm/src/stdlib/time.rs (3)
1350-1367:pyobj_to_time_tis duplicated verbatim across Unix and Windows platform modules.The Unix (line 1020) and Windows (line 1350) implementations are identical. Consider extracting this into a shared
#[cfg(any(unix, windows))]helper to reduce duplication.
1415-1419:get_tz_info()is duplicated between thedeclmodule (line 202) and theplatformmodule (line 1415).Both are identical functions wrapping
GetTimeZoneInformation. Consider making one call the other, or extracting it to a shared location.
1390-1396:current_time_t()uses.unwrap()which panics if the system clock is before UNIX_EPOCH.This is extremely unlikely in practice, but the Unix counterpart (line 1064) uses
libc::time()which doesn't panic. Consider usingunwrap_or(0)or saturating to 0 for defensive consistency.
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
crates/vm/src/stdlib/time.rs (1)
607-610:⚠️ Potential issue | 🟡 MinorWrong function name in error message:
"asctime"should be"ctime".Line 609 passes
"asctime"asfunc_nametochecked_tm_from_struct_time, but this is thectimefunction. If the validation ever fails, the error message would incorrectly sayasctime(): illegal time tuple argument.Proposed fix
- let tm = checked_tm_from_struct_time(&local, vm, "asctime")?.tm; + let tm = checked_tm_from_struct_time(&local, vm, "ctime")?.tm;
🤖 Fix all issues with AI agents
In `@crates/vm/src/stdlib/time.rs`:
- Around line 1313-1316: The Windows platform module is cfg(gated) only by
#[cfg(windows)] but imports get_tz_info and TIME_ZONE_INFORMATION which are
currently #[cfg(target_env = "msvc")], causing build failures on MinGW/gnu
targets; fix by making the cfgs consistent: either change the platform module's
attribute to #[cfg(all(windows, target_env = "msvc"))] so the whole module is
only compiled for MSVC, or relax get_tz_info and TIME_ZONE_INFORMATION (and
associated windows_sys imports) to #[cfg(windows)] so they are available on all
Windows targets; update the declarations for get_tz_info and
TIME_ZONE_INFORMATION (and any windows_sys uses) or the module attribute for mod
platform accordingly so the compiler sees matching cfgs for imports and usage
(e.g., references: get_tz_info, TIME_ZONE_INFORMATION, mod platform, and the
call site that uses get_tz_info).
- Around line 1329-1334: The Windows-specific extern declarations for
_gmtime64_s, _localtime64_s and c_mktime currently use libc::time_t and lack an
architecture guard, which is unsound on 32-bit Windows; add
#[cfg(target_pointer_width = "64")] to the Windows platform module containing
the extern "C" block (the functions named _gmtime64_s, _localtime64_s, and the
linked c_mktime/_mktime64) so these 64-bit CRT bindings are only compiled on
64-bit targets, or alternatively change the function signatures to use explicit
i64 types for the time parameters to ensure correctness on all targets.
🧹 Nitpick comments (2)
crates/vm/src/stdlib/time.rs (2)
470-486: Potential panic on out-of-rangetm_wday/tm_monindices.
asctime_from_tmindexes arrays of size 7 and 12 usingtm.tm_wday as usizeandtm.tm_mon as usizewith no bounds check. While callers typically validate these, ifasctime_from_tmis ever reached with invalid values (e.g., via a future code path), it will panic with an index-out-of-bounds. Consider a defensive.get()or a debug assertion.This is a minor concern since
checked_tm_from_struct_timevalidates month in[0, 11]and wday>= 0(plus% 7caps it at 6), andlocaltime_from_timestampdelegates to the C library which produces valid values.
1408-1465: Significant code duplication withunix_mktimeandchecked_tm_from_struct_time.The field extraction pattern (
try_into_value+map_err(|_| invalid_tuple())) is repeated nearly identically inwin_mktime(here),unix_mktime(Line 1080), andchecked_tm_from_struct_time(Line 332). While the validation semantics differ (mktime normalizes, strftime validates), the extraction logic is the same.Consider extracting a shared helper that converts
StructTimeDatafields into raw(year, mon, mday, hour, min, sec, wday, yday, isdst)tuple, then let each caller apply its own validation/adjustments. This would remove ~50 lines of duplicated code. As per coding guidelines: "When branches differ only in a value but share common logic, extract the differing value first, then call the common logic once to avoid duplicate code."
| unsafe extern "C" { | ||
| fn _gmtime64_s(tm: *mut libc::tm, time: *const libc::time_t) -> libc::c_int; | ||
| fn _localtime64_s(tm: *mut libc::tm, time: *const libc::time_t) -> libc::c_int; | ||
| #[link_name = "_mktime64"] | ||
| fn c_mktime(tm: *mut libc::tm) -> libc::time_t; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's look at the specific code in question
sed -n '1320,1340p' crates/vm/src/stdlib/time.rsRepository: RustPython/RustPython
Length of output: 844
🏁 Script executed:
# Check what targets the crate supports in Cargo.toml
cat Cargo.toml | grep -A 20 "targets\|platform" || echo "No explicit targets found in root Cargo.toml"
# Check the vm crate's Cargo.toml
cat crates/vm/Cargo.toml | head -30Repository: RustPython/RustPython
Length of output: 969
🏁 Script executed:
# Search for any 32-bit Windows or i686 target configuration in the codebase
rg -i 'target_pointer_width.*32|i686.*windows|x86.*windows' --type=rustRepository: RustPython/RustPython
Length of output: 722
🏁 Script executed:
# Search for conditional compilation for Windows architecture
rg -i 'cfg.*windows|target_os.*windows' crates/vm/src/stdlib/time.rs -B2 -A2Repository: RustPython/RustPython
Length of output: 5592
🏁 Script executed:
# Check how libc::time_t is used elsewhere in the file
rg -n 'libc::time_t' crates/vm/src/stdlib/time.rsRepository: RustPython/RustPython
Length of output: 829
🏁 Script executed:
# Check the full Windows platform module to see if there are any 32-bit checks
sed -n '1300,1400p' crates/vm/src/stdlib/time.rsRepository: RustPython/RustPython
Length of output: 3836
🏁 Script executed:
# Search for any documentation or comments about 32-bit Windows support in the time module
rg -i '32.*bit|32-bit|x86' crates/vm/src/stdlib/time.rs -B3 -A3Repository: RustPython/RustPython
Length of output: 47
🏁 Script executed:
# Check if there's any cfg guard on the Windows module declaration
rg -B10 'mod platform' crates/vm/src/stdlib/time.rs | grep -A10 '#\[cfg'Repository: RustPython/RustPython
Length of output: 438
🏁 Script executed:
# Verify whether the crate targets 32-bit Windows by checking build scripts or CI config
find . -name "*.yml" -o -name "*.yaml" -o -name "build.rs" | xargs grep -l "windows.*32\|i686.*windows" 2>/dev/null | head -5Repository: RustPython/RustPython
Length of output: 96
🏁 Script executed:
# Check libc crate version and documentation reference for Windows time_t
rg 'libc.*=|libc =' crates/vm/Cargo.tomlRepository: RustPython/RustPython
Length of output: 92
🏁 Script executed:
# Check the root Cargo.toml to see workspace dependencies for libc
cat Cargo.toml | grep -A 5 '\[workspace\]' | head -20Repository: RustPython/RustPython
Length of output: 130
🏁 Script executed:
# Check if there's explicit documentation about supported platforms
find . -name "README*" -o -name "CONTRIBUTING*" | xargs grep -i "32.*bit\|platform\|windows" 2>/dev/null | head -10Repository: RustPython/RustPython
Length of output: 697
🏁 Script executed:
# Check GitHub Actions workflows for 32-bit Windows builds
cat .github/workflows/release.yml | grep -i "windows\|32\|i686\|x86" | head -20Repository: RustPython/RustPython
Length of output: 1016
🏁 Script executed:
# Check the actual libc version to understand time_t on Windows
rg 'libc.*=' Cargo.tomlRepository: RustPython/RustPython
Length of output: 109
🏁 Script executed:
# Verify if the Windows module has any 64-bit specific guards
grep -B5 -A15 '#\[cfg(windows)\]' crates/vm/src/stdlib/time.rs | grep -A15 'mod platform'Repository: RustPython/RustPython
Length of output: 631
🏁 Script executed:
# Create a small test to understand libc types on Windows
python3 << 'EOF'
# According to libc crate documentation:
# On Windows 64-bit: time_t is i64
# On Windows 32-bit: time_t is i32
# But _gmtime64_s always expects __time64_t which is i64 (64-bit signed)
# This is a mismatch on 32-bit platforms
print("On Windows 64-bit:")
print(" libc::time_t = i64")
print(" _gmtime64_s expects __time64_t = i64")
print(" Parameter: *const libc::time_t = *const i64 ✓ CORRECT")
print()
print("On Windows 32-bit:")
print(" libc::time_t = i32")
print(" _gmtime64_s expects __time64_t = i64")
print(" Parameter: *const libc::time_t = *const i32 ✗ INCORRECT")
print()
print("ISSUE: If 32-bit Windows is supported, the binding is UNSOUND")
EOFRepository: RustPython/RustPython
Length of output: 400
Add a 64-bit Windows guard to the platform module declarations.
The extern bindings for _gmtime64_s, _localtime64_s, and _mktime64 are correct for 64-bit Windows where libc::time_t is i64, matching the required __time64_t. However, the Windows platform module lacks any target_pointer_width guard. On 32-bit Windows, libc::time_t is i32, making these bindings unsound—the parameter type *const libc::time_t would become *const i32 instead of the required *const i64. While 32-bit Windows is not currently tested in CI (the i686-pc-windows-msvc target is commented out), the code could still compile and cause undefined behavior if someone targets it.
Add #[cfg(target_pointer_width = "64")] to the Windows platform module to prevent unsound compilation on 32-bit architectures, or replace the use of libc::time_t with explicit i64 types for these 64-bit CRT functions.
🤖 Prompt for AI Agents
In `@crates/vm/src/stdlib/time.rs` around lines 1329 - 1334, The Windows-specific
extern declarations for _gmtime64_s, _localtime64_s and c_mktime currently use
libc::time_t and lack an architecture guard, which is unsound on 32-bit Windows;
add #[cfg(target_pointer_width = "64")] to the Windows platform module
containing the extern "C" block (the functions named _gmtime64_s,
_localtime64_s, and the linked c_mktime/_mktime64) so these 64-bit CRT bindings
are only compiled on 64-bit targets, or alternatively change the function
signatures to use explicit i64 types for the time parameters to ensure
correctness on all targets.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@crates/vm/src/stdlib/time.rs`:
- Around line 634-649: The format-scanner currently only detects the literal
sequence "%y" and thus misses modified forms like "%#y" when tm.tm_year < 0;
update the loop that iterates over format (the code using fmt.as_str(),
chars.next(), and checking tm.tm_year) to skip valid strftime
flag/width/precision characters (e.g., '#', '-', '_', '0', field width digits
and optional '.' precision) between '%' and the conversion specifier, and then
treat the final conversion letter as the thing to check for 'y'; if that
conversion letter is 'y' and tm.tm_year < 0, return the same
vm.new_value_error("format %y requires year >= 1900 on Windows".to_owned()).
Ensure the change is applied in the same block where tm.tm_year is inspected so
all modified forms like "%#y" are rejected.
🧹 Nitpick comments (3)
crates/vm/src/stdlib/time.rs (3)
1424-1481:win_mktimeandunix_mktimeare near-identical; extract shared logic.Both functions parse the same
StructTimeDatafields into alibc::tm, differing only in the finalmktimecall (libc::mktimevsc_mktimewithsuppress_iph!). As per coding guidelines: "When branches differ only in a value but share common logic, extract the differing value first, then call the common logic once to avoid duplicate code."Consider extracting the common
StructTimeData → libc::tmparsing into a shared helper (e.g.,tm_from_struct_time_for_mktime), then have each platform function call it and dispatch to its respectivemktime.Also applies to: 1096-1155
956-971: Lossyi64 → f64 → time_tround-trip for integer timestamps.Line 965 converts
i64tof64, losing precision for values > 2⁵³. The subsequentsecs as libc::time_ton line 970 then converts back, potentially yielding a different timestamp than the user passed in. For the integer case, you could short-circuit directly:Suggested fix
fn pyobj_to_time_t(value: Either<f64, i64>, vm: &VirtualMachine) -> PyResult<libc::time_t> { - let secs = match value { + match value { Either::A(float) => { if !float.is_finite() { return Err(vm.new_value_error("Invalid value for timestamp")); } - float.floor() + let secs = float.floor(); + if secs < libc::time_t::MIN as f64 || secs > libc::time_t::MAX as f64 { + return Err( + vm.new_overflow_error("timestamp out of range for platform time_t"), + ); + } + Ok(secs as libc::time_t) } - Either::B(int) => int as f64, - }; - if secs < libc::time_t::MIN as f64 || secs > libc::time_t::MAX as f64 { - return Err(vm.new_overflow_error("timestamp out of range for platform time_t")); + Either::B(int) => Ok(int as libc::time_t), } - Ok(secs as libc::time_t) }
470-486:asctime_from_tmindexes arrays without bounds checks — panics on invalidtm_wday/tm_mon.While current callers go through
checked_tm_from_struct_timewhich validates ranges,asctime_from_tmitself ispub-accessible within the module and will panic with an index-out-of-bounds if given atmwithtm_wday >= 7ortm_mon >= 12. A defensive.get().unwrap_or(...)or debug assertion would harden this against future misuse.
crates/vm/src/stdlib/time.rs
Outdated
| // %y requires year >= 1900 on Windows | ||
| if tm.tm_year < 0 { | ||
| let fmt = format.as_str(); | ||
| let mut chars = fmt.chars(); | ||
| while let Some(ch) = chars.next() { | ||
| if ch == '%' { | ||
| if let Some(next) = chars.next() { | ||
| if next == 'y' { | ||
| return Err(vm.new_value_error( | ||
| "format %y requires year >= 1900 on Windows".to_owned(), | ||
| )); | ||
| } | ||
| } | ||
| } | ||
| size = size.saturating_mul(2); | ||
| } | ||
| } |
There was a problem hiding this comment.
%y check doesn't account for Windows modifier flags like %#y.
The scan rejects %y when year < 1900, but Windows MSVC strftime also supports %#y (and potentially other modified forms). The current check only looks for the exact sequence %y, so %#y would slip through and could cause MSVC's strftime to produce incorrect output or crash for years before 1900.
Consider extending the scan to skip over flag characters (#, -, 0, etc.) between % and the conversion specifier:
Suggested approach
if ch == '%' {
- if let Some(next) = chars.next() {
- if next == 'y' {
+ // Skip optional modifier flags (e.g., %#y)
+ let mut next = chars.next();
+ while let Some(c) = next {
+ if c == '#' || c == '-' || c == '0' {
+ next = chars.next();
+ } else {
+ break;
+ }
+ }
+ if let Some(spec) = next {
+ if spec == 'y' {
return Err(vm.new_value_error(
"format %y requires year >= 1900 on Windows".to_owned(),
));
}
}🤖 Prompt for AI Agents
In `@crates/vm/src/stdlib/time.rs` around lines 634 - 649, The format-scanner
currently only detects the literal sequence "%y" and thus misses modified forms
like "%#y" when tm.tm_year < 0; update the loop that iterates over format (the
code using fmt.as_str(), chars.next(), and checking tm.tm_year) to skip valid
strftime flag/width/precision characters (e.g., '#', '-', '_', '0', field width
digits and optional '.' precision) between '%' and the conversion specifier, and
then treat the final conversion letter as the thing to check for 'y'; if that
conversion letter is 'y' and tm.tm_year < 0, return the same
vm.new_value_error("format %y requires year >= 1900 on Windows".to_owned()).
Ensure the change is applied in the same block where tm.tm_year is inspected so
all modified forms like "%#y" are rejected.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@crates/vm/src/stdlib/time.rs`:
- Around line 1357-1363: The current_time_t function silently returns 0 on clock
errors because duration_since(UNIX_EPOCH).unwrap_or_default() masks failures;
update current_time_t to mirror the Unix behavior by returning an error sentinel
instead of 0 on failure: either (A) on Windows use libc::time(null) under
cfg(target_os = "windows") (call libc::time with a null pointer and return its
result cast to libc::time_t) or (B) keep the SystemTime approach but detect Err
from duration_since and return -1 as a libc::time_t on error; change
current_time_t to perform that branching so callers receive the same -1-on-error
semantics as the Unix implementation.
- Around line 940-955: pyobj_to_time_t currently maps the integer branch
(Either::B) through an f64 which can lose precision for i64 values beyond 2^53;
change the Either::B(int) handling in pyobj_to_time_t to perform a direct
integer range check against libc::time_t::MIN and libc::time_t::MAX (by casting
those bounds to i64 or comparing using i128 if needed) and then cast int ->
libc::time_t directly, keeping the float branch behavior (floor + finite check)
unchanged; update error paths to return vm.new_overflow_error("timestamp out of
range for platform time_t") when the integer is outside time_t bounds.
🧹 Nitpick comments (1)
crates/vm/src/stdlib/time.rs (1)
1408-1465:win_mktimelargely duplicatesunix_mktime— consider extracting shared logic.The field extraction and error handling in
win_mktime(lines 1408–1465) mirrorsunix_mktime(lines 1080–1139) almost identically. As per coding guidelines, shared logic should be extracted to avoid duplication. The platform-specific parts (extratm_gmtoff/tm_zonefields on Unix,suppress_iph!on Windows) could be handled in small platform-conditional blocks after a shared field-extraction helper.
- Replace chrono with CRT functions (wcsftime, _gmtime64_s, _localtime64_s, _mktime64) on Windows for correct behavior - Fix timezone name corruption: use take_while instead of filter for null-terminated wide strings in TIME_ZONE_INFORMATION - Fix wcsftime symbol name (_wcsftime -> wcsftime) - Fix CString import cfg to unix-only - Remove 13 expectedFailure markers from test_time for Windows
📦 Library DependenciesThe following Lib/ modules were modified. Here are their dependencies: [x] test: cpython/Lib/test/test_time.py (TODO: 2) dependencies: dependent tests: (134 tests)
Legend:
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@crates/vm/src/stdlib/time.rs`:
- Around line 1327-1329: The Windows platform module is annotated with plain
#[pymodule] but should be a submodule like the Unix one; change the attribute on
the Windows module declaration from #[pymodule] to #[pymodule(sub)] for the
module named platform so its items are merged into the parent module (i.e.,
update the module declaration "mod platform" to use #[pymodule(sub)]).
🧹 Nitpick comments (1)
crates/vm/src/stdlib/time.rs (1)
940-997: Consider extracting shared field-extraction logic fromtm_from_struct_timeandchecked_tm_from_struct_time.Both functions independently extract the same ~8 fields from
StructTimeDataintolibc::tmusing the identicalclone().try_into_value(vm).map_err(...)pattern. A shared helper that populates alibc::tmfromStructTimeData(with a configurable error-message prefix) could be called by both, withchecked_tm_from_struct_timeadding its range validation on top.As per coding guidelines: "When branches differ only in a value but share common logic, extract the differing value first, then call the common logic once to avoid duplicate code."
| #[cfg(windows)] | ||
| #[pymodule] | ||
| mod platform { |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, check the current line count and find all #[pymodule] attributes
echo "=== All #[pymodule] attributes in time.rs ==="
rg -n '#\[pymodule' crates/vm/src/stdlib/time.rs
echo ""
echo "=== Lines around 1043 (Unix platform module) ==="
sed -n '1040,1050p' crates/vm/src/stdlib/time.rs
echo ""
echo "=== Lines around 1328 (Windows platform module) ==="
sed -n '1325,1335p' crates/vm/src/stdlib/time.rs
echo ""
echo "=== Check the main module definition to understand module structure ==="
sed -n '20,35p' crates/vm/src/stdlib/time.rsRepository: RustPython/RustPython
Length of output: 1533
🏁 Script executed:
# Search for other #[pymodule(sub)] usage to understand the pattern
echo "=== Other uses of #[pymodule(sub)] in the codebase ==="
rg -n '#\[pymodule\(sub\)' crates/ | head -20
echo ""
echo "=== Check if there are Windows-specific pyfunction declarations ==="
sed -n '1328,1450p' crates/vm/src/stdlib/time.rs | grep -n "#\[pyfunction\|fn "
echo ""
echo "=== Check how Unix platform module is structured ==="
sed -n '1043,1150p' crates/vm/src/stdlib/time.rs | grep -n "#\[pyfunction\|fn " | head -15Repository: RustPython/RustPython
Length of output: 2025
🏁 Script executed:
# Check if Windows platform module functions are being properly registered
echo "=== Looking for #[pyfunction] in Windows platform module ==="
sed -n '1328,1500p' crates/vm/src/stdlib/time.rs | grep -n "pyfunction"
echo ""
echo "=== Check module initialization or expose patterns ==="
grep -n "pub fn\|pub async fn" crates/vm/src/stdlib/time.rs | grep -i "clock\|perf"
echo ""
echo "=== Verify test files reference time.perf_counter on Windows ==="
rg "time\.perf_counter|time\.get_clock_info" --type py | head -10Repository: RustPython/RustPython
Length of output: 985
🏁 Script executed:
# Get full Windows platform module to see structure
echo "=== Full Windows platform module ==="
sed -n '1328,1410p' crates/vm/src/stdlib/time.rs
echo ""
echo "=== Look for perf_counter and get_clock_info implementation ==="
rg -n "fn perf_counter|fn get_clock_info" crates/vm/src/stdlib/time.rs -A 3Repository: RustPython/RustPython
Length of output: 4226
🏁 Script executed:
# Check if Windows platform module functions are exported as #[pyfunction]
echo "=== Windows platform module functions (1328-1600 range) ==="
sed -n '1328,1600p' crates/vm/src/stdlib/time.rs | grep -E "#\[pyfunction\]|^[[:space:]]*(pub )?fn " | head -30Repository: RustPython/RustPython
Length of output: 754
🏁 Script executed:
# Check the complete context of Windows get_clock_info function
echo "=== Windows platform module get_clock_info and surrounding context ==="
sed -n '1500,1530p' crates/vm/src/stdlib/time.rs
echo ""
echo "=== Compare with Unix platform module structure ==="
sed -n '1043,1060p' crates/vm/src/stdlib/time.rsRepository: RustPython/RustPython
Length of output: 1843
Add sub attribute to Windows platform module: #[pymodule(sub)].
The Windows platform module uses plain #[pymodule] while the Unix platform module uses #[pymodule(sub)]. This is inconsistent with every other sub-module in the codebase, which all use #[pymodule(sub)] to merge their items into the parent module.
Suggested fix:
#[cfg(windows)]
-#[pymodule]
+#[pymodule(sub)]
mod platform {🤖 Prompt for AI Agents
In `@crates/vm/src/stdlib/time.rs` around lines 1327 - 1329, The Windows platform
module is annotated with plain #[pymodule] but should be a submodule like the
Unix one; change the attribute on the Windows module declaration from
#[pymodule] to #[pymodule(sub)] for the module named platform so its items are
merged into the parent module (i.e., update the module declaration "mod
platform" to use #[pymodule(sub)]).
Summary by CodeRabbit
Refactor
Bug Fixes