Fix sys.implementation (#7793)

`sys.version_info` and `sys.implementation` are two different versioning
schemes. `version_info` refers to the target Python version while
`implementation` refers to the implementation version.

For RustPython, `version_info` should be 3.14 because it currently
targets Python 3.14. This was correct and remains unchanged.

`implementation`, however, should be set to RustPython's version. I
updated the code to correctly get RustPython's version from the
environment. I also ensured values are hard coded at build time which
reduces the overall code that needs to be run at runtime as well as
removes some unneeded allocations.

Closes: #7770
This commit is contained in:
Joshua Megnauth authored and GitHub committed 2026-05-10 00:25:14 +09:00
1 parent 10581d3412
commit d72a726d63
3 files changed
+88 -52

No files matched your search

+39 -14
View File
@@ -4,7 +4,13 @@
)]
use itertools::Itertools;
use std::{env, io::prelude::*, path::PathBuf, process::Command};
use std::{
env,
io::{self, prelude::*},
path::PathBuf,
process::Command,
time::{SystemTime, UNIX_EPOCH},
};
fn main() {
let frozen_libs = if cfg!(feature = "freeze-stdlib") {
@@ -27,6 +33,11 @@ fn main() {
println!("cargo:rustc-env=RUSTPYTHON_GIT_BRANCH={}", git_branch());
println!("cargo:rustc-env=RUSTC_VERSION={}", rustc_version());
let release_level = option_env!("RUSTPYTHON_RELEASE_LEVEL").unwrap_or("alpha");
println!("cargo:rustc-env=RUSTPYTHON_RELEASE_LEVEL={release_level}");
let release_serial = option_env!("RUSTPYTHON_RELEASE_SERIAL").unwrap_or("0");
println!("cargo:rustc-env=RUSTPYTHON_RELEASE_SERIAL={release_serial}");
println!(
"cargo:rustc-env=RUSTPYTHON_TARGET_TRIPLE={}",
env::var("TARGET").unwrap()
@@ -44,36 +55,50 @@ fn main() {
}
fn git_hash() -> String {
git(&["rev-parse", "--short", "HEAD"])
git(&["rev-parse", "--short", "HEAD"]).unwrap_or_else(|_| "0000000".into())
}
fn git_timestamp() -> String {
git(&["log", "-1", "--format=%ct"])
git(&["log", "-1", "--format=%ct"]).unwrap_or_else(|_| {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs()
.to_string()
})
}
fn git_tag() -> String {
git(&["describe", "--all", "--always", "--dirty"])
.unwrap_or_else(|_| "heads/unknown-branch".into())
}
fn git_branch() -> String {
git(&["name-rev", "--name-only", "HEAD"])
git(&["name-rev", "--name-only", "HEAD"]).unwrap_or_else(|_| "unknown-branch".into())
}
fn git(args: &[&str]) -> String {
fn git(args: &[&str]) -> io::Result<String> {
command("git", args)
}
fn rustc_version() -> String {
let rustc = env::var_os("RUSTC").unwrap_or_else(|| "rustc".into());
command(rustc, &["-V"])
command(rustc, &["-V"]).unwrap_or_else(|_| "rustc [unknown]".into())
}
fn command(cmd: impl AsRef<std::ffi::OsStr>, args: &[&str]) -> String {
match Command::new(cmd).args(args).output() {
Ok(output) => match String::from_utf8(output.stdout) {
Ok(s) => s,
Err(err) => format!("(output error: {err})"),
},
Err(err) => format!("(command error: {err})"),
}
fn command(cmd: impl AsRef<std::ffi::OsStr>, args: &[&str]) -> io::Result<String> {
Command::new(&cmd).args(args).output().and_then(|output| {
// TODO: Switch to exit_ok()? when stable.
if !output.status.success() {
Err(io::Error::other(format!(
"command '{}' exited with status {}",
cmd.as_ref().to_string_lossy(),
output.status
)))?
}
str::from_utf8(&output.stdout)
.map(|s| s.trim().to_owned())
.map_err(io::Error::other)
})
}
+12 -4
View File
@@ -660,7 +660,7 @@ pub mod sys {
vm.new_tuple((
ascii!("RustPython"),
version::get_git_identifier(),
version::get_git_revision(),
version::GIT_REVISION,
))
}
@@ -668,14 +668,14 @@ pub mod sys {
fn implementation(vm: &VirtualMachine) -> PyRef<PyNamespace> {
const NAME: &str = "rustpython";
let cache_tag = format!("{NAME}-{}{}", version::MAJOR, version::MINOR);
let cache_tag = format!("{NAME}-{}_{}", version::MAJOR_IMPL, version::MINOR_IMPL);
let ctx = &vm.ctx;
py_namespace!(vm, {
"name" => ctx.new_str(NAME),
"cache_tag" => ctx.new_str(cache_tag),
"_multiarch" => ctx.new_str(multiarch()),
"version" => version_info(vm),
"hexversion" => ctx.new_int(version::VERSION_HEX),
"version" => PyVersionInfo::from_data(VersionInfoData::IMPLEMENTATION, vm),
"hexversion" => ctx.new_int(version::VERSION_HEX_IMPL),
"supports_isolated_interpreters" => ctx.new_bool(false),
})
}
@@ -1758,6 +1758,14 @@ pub mod sys {
releaselevel: version::RELEASELEVEL,
serial: version::SERIAL,
};
pub const IMPLEMENTATION: Self = Self {
major: version::MAJOR_IMPL,
minor: version::MINOR_IMPL,
micro: version::MICRO_IMPL,
releaselevel: version::RELEASELEVEL_IMPL,
serial: version::SERIAL_IMPL,
};
}
#[pystruct_sequence(name = "version_info", data = "VersionInfoData", no_attr)]
+37 -34
View File
@@ -11,10 +11,37 @@ pub const MICRO: usize = 0;
pub const RELEASELEVEL: &str = "alpha";
pub const RELEASELEVEL_N: usize = 0xA;
pub const SERIAL: usize = 0;
pub const VERSION_HEX: usize =
(MAJOR << 24) | (MINOR << 16) | (MICRO << 8) | (RELEASELEVEL_N << 4) | SERIAL;
pub const GIT_REVISION: &str = env!("RUSTPYTHON_GIT_HASH");
const GIT_TAG: &str = env!("RUSTPYTHON_GIT_TAG");
const GIT_BRANCH: &str = env!("RUSTPYTHON_GIT_BRANCH");
// RustPython version
pub const MAJOR_IMPL: usize = match usize::from_str_radix(env!("CARGO_PKG_VERSION_MAJOR"), 10) {
Ok(v) => v,
Err(_) => panic!("Compile with Cargo to get 'CARGO_PKG_VERSION_MAJOR'"),
};
pub const MINOR_IMPL: usize = match usize::from_str_radix(env!("CARGO_PKG_VERSION_MINOR"), 10) {
Ok(v) => v,
Err(_) => panic!("Compile with Cargo to get 'CARGO_PKG_VERSION_MINOR'"),
};
pub const MICRO_IMPL: usize = match usize::from_str_radix(env!("CARGO_PKG_VERSION_PATCH"), 10) {
Ok(v) => v,
Err(_) => panic!("Compile with Cargo to get 'CARGO_PKG_VERSION_PATCH'"),
};
pub const RELEASELEVEL_IMPL: &str = env!("RUSTPYTHON_RELEASE_LEVEL");
pub const SERIAL_IMPL: usize = match usize::from_str_radix(env!("RUSTPYTHON_RELEASE_SERIAL"), 10) {
Ok(v) => v,
Err(_) => panic!("Compile with Cargo to get 'RUSTPYTHON_RELEASE_SERIAL'"),
};
pub const VERSION_HEX_IMPL: usize = (MAJOR_IMPL << 24)
| (MINOR_IMPL << 16)
| (MICRO_IMPL << 8)
| (RELEASELEVEL_N << 4)
| SERIAL_IMPL;
#[must_use]
pub fn get_version() -> String {
// Windows: include MSC v. for compatibility with ctypes.util.find_library
@@ -57,59 +84,35 @@ const COMPILER: &str = env!("RUSTC_VERSION");
#[must_use]
pub fn get_build_info() -> String {
// See: https://reproducible-builds.org/docs/timestamps/
let git_revision = get_git_revision();
let separator = if git_revision.is_empty() { "" } else { ":" };
let separator = if GIT_REVISION.is_empty() { "" } else { ":" };
let git_identifier = get_git_identifier();
format!(
"{id}{sep}{revision}, {date:.20}, {time:.9}",
id = if git_identifier.is_empty() {
"default".to_owned()
"default"
} else {
git_identifier
},
sep = separator,
revision = git_revision,
revision = GIT_REVISION,
date = get_git_date(),
time = get_git_time(),
)
}
#[must_use]
pub fn get_git_revision() -> String {
option_env!("RUSTPYTHON_GIT_HASH").unwrap_or("").to_owned()
}
#[must_use]
pub fn get_git_tag() -> String {
option_env!("RUSTPYTHON_GIT_TAG").unwrap_or("").to_owned()
}
#[must_use]
pub fn get_git_branch() -> String {
option_env!("RUSTPYTHON_GIT_BRANCH")
.unwrap_or("")
.to_owned()
}
#[must_use]
pub fn get_git_identifier() -> String {
let git_tag = get_git_tag();
let git_branch = get_git_branch();
if git_tag.is_empty() || git_tag == "undefined" {
git_branch
pub const fn get_git_identifier() -> &'static str {
if GIT_TAG.is_empty() || GIT_TAG.eq_ignore_ascii_case("undefined") {
GIT_BRANCH
} else {
git_tag
GIT_TAG
}
}
fn get_git_timestamp_datetime() -> DateTime<Local> {
let timestamp = option_env!("RUSTPYTHON_GIT_TIMESTAMP")
.unwrap_or("")
.to_owned();
let timestamp = timestamp.parse::<u64>().unwrap_or(0);
let timestamp = option_env!("RUSTPYTHON_GIT_TIMESTAMP").unwrap_or_default();
let timestamp = timestamp.parse::<u64>().unwrap_or_default();
let datetime = UNIX_EPOCH + Duration::from_secs(timestamp);