Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions crates/capi/src/pylifecycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::pystate::ensure_thread_has_vm_attached;
use alloc::ffi::CString;
use core::ffi::{c_char, c_int, c_ulong};
use rustpython_vm::common::rc::PyRc;
use rustpython_vm::stdlib::sys;
use rustpython_vm::version::{MAJOR, MICRO, MINOR, VERSION_HEX};
use rustpython_vm::vm::thread::ThreadedVirtualMachine;
use rustpython_vm::{Context, Interpreter};
Expand Down Expand Up @@ -80,6 +81,38 @@ pub extern "C" fn Py_GetVersion() -> *const c_char {
VERSION.as_ptr()
}

#[unsafe(no_mangle)]
pub extern "C" fn Py_GetBuildInfo() -> *const c_char {
static BUILD_INFO: LazyLock<CString> = LazyLock::new(|| {
CString::new(sys::BUILD_INFO).expect("build info must not contain interior NULs")
});
BUILD_INFO.as_ptr()
}

#[unsafe(no_mangle)]
pub extern "C" fn Py_GetCompiler() -> *const c_char {
static COMPILER: LazyLock<CString> = LazyLock::new(|| {
CString::new(sys::COMPILER).expect("compiler must not contain interior NULs")
});
COMPILER.as_ptr()
}

#[unsafe(no_mangle)]
pub extern "C" fn Py_GetCopyright() -> *const c_char {
static COPYRIGHT: LazyLock<CString> = LazyLock::new(|| {
CString::new(sys::COPYRIGHT).expect("copyright must not contain interior NULs")
});
COPYRIGHT.as_ptr()
}

#[unsafe(no_mangle)]
pub extern "C" fn Py_GetPlatform() -> *const c_char {
static PLATFORM: LazyLock<CString> = LazyLock::new(|| {
CString::new(sys::PLATFORM).expect("platform must not contain interior NULs")
});
PLATFORM.as_ptr()
}

Comment on lines +84 to +115

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Deduplicate the four near-identical build-metadata functions with a macro.

Py_GetBuildInfo, Py_GetCompiler, Py_GetCopyright, and Py_GetPlatform differ only in the source constant, static name, and panic message — everything else is copy-pasted.

♻️ Proposed macro-based refactor
+macro_rules! build_metadata_getter {
+    ($fn_name:ident, $const_name:path, $what:literal) => {
+        #[unsafe(no_mangle)]
+        pub extern "C" fn $fn_name() -> *const c_char {
+            static VALUE: LazyLock<CString> = LazyLock::new(|| {
+                CString::new($const_name).expect(concat!($what, " must not contain interior NULs"))
+            });
+            VALUE.as_ptr()
+        }
+    };
+}
+
+build_metadata_getter!(Py_GetBuildInfo, sys::BUILD_INFO, "build info");
+build_metadata_getter!(Py_GetCompiler, sys::COMPILER, "compiler");
+build_metadata_getter!(Py_GetCopyright, sys::COPYRIGHT, "copyright");
+build_metadata_getter!(Py_GetPlatform, sys::PLATFORM, "platform");
-#[unsafe(no_mangle)]
-pub extern "C" fn Py_GetBuildInfo() -> *const c_char {
-    static BUILD_INFO: LazyLock<CString> = LazyLock::new(|| {
-        CString::new(sys::BUILD_INFO).expect("build info must not contain interior NULs")
-    });
-    BUILD_INFO.as_ptr()
-}
-
-#[unsafe(no_mangle)]
-pub extern "C" fn Py_GetCompiler() -> *const c_char {
-    static COMPILER: LazyLock<CString> = LazyLock::new(|| {
-        CString::new(sys::COMPILER).expect("compiler must not contain interior NULs")
-    });
-    COMPILER.as_ptr()
-}
-
-#[unsafe(no_mangle)]
-pub extern "C" fn Py_GetCopyright() -> *const c_char {
-    static COPYRIGHT: LazyLock<CString> = LazyLock::new(|| {
-        CString::new(sys::COPYRIGHT).expect("copyright must not contain interior NULs")
-    });
-    COPYRIGHT.as_ptr()
-}
-
-#[unsafe(no_mangle)]
-pub extern "C" fn Py_GetPlatform() -> *const c_char {
-    static PLATFORM: LazyLock<CString> = LazyLock::new(|| {
-        CString::new(sys::PLATFORM).expect("platform must not contain interior NULs")
-    });
-    PLATFORM.as_ptr()
-}

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."

#!/bin/bash
# Check whether Py_GetVersion (pre-existing) follows the same repeated pattern,
# and whether existing pyo3-based tests cover similar getters.
ast-grep run --pattern 'pub extern "C" fn Py_GetVersion() -> *const c_char { $$$ }' --lang rust crates/capi/src/pylifecycle.rs
rg -n -A5 -B2 '"Py_Get' crates/capi/src/pylifecycle.rs
rg -n 'fn test' -A10 crates/capi/src/pylifecycle.rs | rg -n 'Py_Get' -B5
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/capi/src/pylifecycle.rs` around lines 84 - 115, The four getter
functions are duplicated and should be refactored into a macro-based helper.
Extract the shared CString/LazyLock/as_ptr logic into a single reusable macro or
helper and parameterize only the differing sys constant, static identifier, and
panic text for Py_GetBuildInfo, Py_GetCompiler, Py_GetCopyright, and
Py_GetPlatform. Keep the public extern "C" function names unchanged and apply
the same pattern consistently if Py_GetVersion or similar getters in
pylifecycle.rs use the same structure.

Source: Coding guidelines

#[cfg(test)]
mod tests {
use pyo3::prelude::*;
Expand Down
9 changes: 7 additions & 2 deletions crates/vm/src/stdlib/sys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::{Py, PyPayload, PyResult, VirtualMachine, builtins::PyModule, convert

#[cfg(all(not(feature = "host_env"), feature = "stdio"))]
pub(crate) use sys::SandboxStdio;
pub use sys::{BUILD_INFO, COMPILER, COPYRIGHT, PLATFORM, VERSION};
pub(crate) use sys::{DOC, MAXSIZE, RUST_MULTIARCH, UnraisableHookArgsData, module_def, multiarch};

#[pymodule(name = "_jit")]
Expand Down Expand Up @@ -222,7 +223,7 @@ pub mod sys {
#[pyattr(name = "api_version")]
const API_VERSION: u32 = 0x0; // what C api?
#[pyattr(name = "copyright")]
const COPYRIGHT: &str = "Copyright (c) 2019 RustPython Team";
pub const COPYRIGHT: &str = "Copyright (c) 2019 RustPython Team";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, another suggestion.
let's define this as

Suggested change
pub const COPYRIGHT: &str = "Copyright (c) 2019 RustPython Team";
pub const COPYRIGHT: &CStr = c"Copyright (c) 2019 RustPython Team";

Then add a IntoPyObject conversion from &CStr to String to ignore the tail null char.
This will not hurt any RustPython use case and also will make C api happier without LazyLock

#[pyattr(name = "float_repr_style")]
const FLOAT_REPR_STYLE: &str = "short";
#[pyattr(name = "_framework")]
Expand Down Expand Up @@ -708,7 +709,11 @@ pub mod sys {
}

#[pyattr(name = "version")]
const VERSION: &str = version::RUSTPYTHON_VERSION;
pub const VERSION: &str = version::RUSTPYTHON_VERSION;

pub const BUILD_INFO: &str = version::RUSTPYTHON_BUILD_INFO;

pub const COMPILER: &str = "[Rust]";

// Note: This is Python DLL version in CPython, but we arbitrary fill it for compatibility
#[cfg(windows)]
Expand Down
Loading