Merge pull request #2092 from aganders3/export-conf
Add export-config feature to pyo3-build-config
This commit is contained in:
commit
2813c87eaf
|
@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Allow dependent crates to access config values from `pyo3-build-config` via cargo link dep env vars. [#2092](https://github.com/PyO3/pyo3/pull/2092)
|
||||||
|
- Added methods on `InterpreterConfig` to run Python scripts using the configured executable. [#2092](https://github.com/PyO3/pyo3/pull/2092)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Allow `#[pyo3(crate = "...", text_signature = "...")]` options to be used directly in `#[pyclass(crate = "...", text_signature = "...")]`. [#2234](https://github.com/PyO3/pyo3/pull/2234)
|
- Allow `#[pyo3(crate = "...", text_signature = "...")]` options to be used directly in `#[pyclass(crate = "...", text_signature = "...")]`. [#2234](https://github.com/PyO3/pyo3/pull/2234)
|
||||||
|
|
13
Cargo.toml
13
Cargo.toml
|
@ -87,7 +87,18 @@ nightly = []
|
||||||
|
|
||||||
# Activates all additional features
|
# Activates all additional features
|
||||||
# This is mostly intended for testing purposes - activating *all* of these isn't particularly useful.
|
# This is mostly intended for testing purposes - activating *all* of these isn't particularly useful.
|
||||||
full = ["macros", "pyproto", "multiple-pymethods", "num-bigint", "num-complex", "hashbrown", "serde", "indexmap", "eyre", "anyhow"]
|
full = [
|
||||||
|
"macros",
|
||||||
|
"pyproto",
|
||||||
|
"multiple-pymethods",
|
||||||
|
"num-bigint",
|
||||||
|
"num-complex",
|
||||||
|
"hashbrown",
|
||||||
|
"serde",
|
||||||
|
"indexmap",
|
||||||
|
"eyre",
|
||||||
|
"anyhow",
|
||||||
|
]
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "bench_call"
|
name = "bench_call"
|
||||||
|
|
|
@ -8,6 +8,7 @@ use std::{
|
||||||
io::{BufRead, BufReader, Read, Write},
|
io::{BufRead, BufReader, Read, Write},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
process::{Command, Stdio},
|
process::{Command, Stdio},
|
||||||
|
str,
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -154,7 +155,7 @@ impl InterpreterConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
for flag in &self.build_flags.0 {
|
for flag in &self.build_flags.0 {
|
||||||
println!("cargo:rustc-cfg=py_sys_config=\"{}\"", flag)
|
println!("cargo:rustc-cfg=py_sys_config=\"{}\"", flag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,6 +341,12 @@ print("mingw", get_platform().startswith("mingw"))
|
||||||
InterpreterConfig::from_reader(reader)
|
InterpreterConfig::from_reader(reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn from_cargo_dep_env() -> Option<Result<Self>> {
|
||||||
|
cargo_env_var("DEP_PYTHON_PYO3_CONFIG")
|
||||||
|
.map(|buf| InterpreterConfig::from_reader(buf.replace("\\n", "\n").as_bytes()))
|
||||||
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn from_reader(reader: impl Read) -> Result<Self> {
|
pub fn from_reader(reader: impl Read) -> Result<Self> {
|
||||||
let reader = BufReader::new(reader);
|
let reader = BufReader::new(reader);
|
||||||
|
@ -420,6 +427,29 @@ print("mingw", get_platform().startswith("mingw"))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
/// Serialize the `InterpreterConfig` and print it to the environment for Cargo to pass along
|
||||||
|
/// to dependent packages during build time.
|
||||||
|
///
|
||||||
|
/// NB: writing to the cargo environment requires the
|
||||||
|
/// [`links`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key)
|
||||||
|
/// manifest key to be set. In this case that means this is called by the `pyo3-ffi` crate and
|
||||||
|
/// available for dependent package build scripts in `DEP_PYTHON_PYO3_CONFIG`. See
|
||||||
|
/// documentation for the
|
||||||
|
/// [`DEP_<name>_<key>`](https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts)
|
||||||
|
/// environment variable.
|
||||||
|
pub fn to_cargo_dep_env(&self) -> Result<()> {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
self.to_writer(&mut buf)?;
|
||||||
|
// escape newlines in env var
|
||||||
|
if let Ok(config) = str::from_utf8(&buf) {
|
||||||
|
println!("cargo:PYO3_CONFIG={}", config.replace('\n', "\\n"));
|
||||||
|
} else {
|
||||||
|
bail!("unable to emit interpreter config to link env for downstream use");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn to_writer(&self, mut writer: impl Write) -> Result<()> {
|
pub fn to_writer(&self, mut writer: impl Write) -> Result<()> {
|
||||||
macro_rules! write_line {
|
macro_rules! write_line {
|
||||||
|
@ -462,6 +492,38 @@ print("mingw", get_platform().startswith("mingw"))
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Run a python script using the [`InterpreterConfig::executable`].
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// This function will panic if the [`executable`](InterpreterConfig::executable) is `None`.
|
||||||
|
pub fn run_python_script(&self, script: &str) -> Result<String> {
|
||||||
|
run_python_script_with_envs(
|
||||||
|
Path::new(self.executable.as_ref().expect("no interpreter executable")),
|
||||||
|
script,
|
||||||
|
std::iter::empty::<(&str, &str)>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run a python script using the [`InterpreterConfig::executable`] with additional
|
||||||
|
/// environment variables (e.g. PYTHONPATH) set.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// This function will panic if the [`executable`](InterpreterConfig::executable) is `None`.
|
||||||
|
pub fn run_python_script_with_envs<I, K, V>(&self, script: &str, envs: I) -> Result<String>
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = (K, V)>,
|
||||||
|
K: AsRef<OsStr>,
|
||||||
|
V: AsRef<OsStr>,
|
||||||
|
{
|
||||||
|
run_python_script_with_envs(
|
||||||
|
Path::new(self.executable.as_ref().expect("no interpreter executable")),
|
||||||
|
script,
|
||||||
|
envs,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
@ -1184,8 +1246,20 @@ fn default_lib_name_unix(
|
||||||
|
|
||||||
/// Run a python script using the specified interpreter binary.
|
/// Run a python script using the specified interpreter binary.
|
||||||
fn run_python_script(interpreter: &Path, script: &str) -> Result<String> {
|
fn run_python_script(interpreter: &Path, script: &str) -> Result<String> {
|
||||||
|
run_python_script_with_envs(interpreter, script, std::iter::empty::<(&str, &str)>())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run a python script using the specified interpreter binary with additional environment
|
||||||
|
/// variables (e.g. PYTHONPATH) set.
|
||||||
|
fn run_python_script_with_envs<I, K, V>(interpreter: &Path, script: &str, envs: I) -> Result<String>
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = (K, V)>,
|
||||||
|
K: AsRef<OsStr>,
|
||||||
|
V: AsRef<OsStr>,
|
||||||
|
{
|
||||||
let out = Command::new(interpreter)
|
let out = Command::new(interpreter)
|
||||||
.env("PYTHONIOENCODING", "utf-8")
|
.env("PYTHONIOENCODING", "utf-8")
|
||||||
|
.envs(envs)
|
||||||
.stdin(Stdio::piped())
|
.stdin(Stdio::piped())
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.stderr(Stdio::inherit())
|
.stderr(Stdio::inherit())
|
||||||
|
@ -1849,4 +1923,29 @@ mod tests {
|
||||||
.is_none()
|
.is_none()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_run_python_script() {
|
||||||
|
// as above, this should be okay in CI where Python is presumed installed
|
||||||
|
let interpreter = make_interpreter_config()
|
||||||
|
.expect("could not get InterpreterConfig from installed interpreter");
|
||||||
|
let out = interpreter
|
||||||
|
.run_python_script("print(2 + 2)")
|
||||||
|
.expect("failed to run Python script");
|
||||||
|
assert_eq!(out.trim_end(), "4");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_run_python_script_with_envs() {
|
||||||
|
// as above, this should be okay in CI where Python is presumed installed
|
||||||
|
let interpreter = make_interpreter_config()
|
||||||
|
.expect("could not get InterpreterConfig from installed interpreter");
|
||||||
|
let out = interpreter
|
||||||
|
.run_python_script_with_envs(
|
||||||
|
"import os; print(os.getenv('PYO3_TEST'))",
|
||||||
|
vec![("PYO3_TEST", "42")],
|
||||||
|
)
|
||||||
|
.expect("failed to run Python script");
|
||||||
|
assert_eq!(out.trim_end(), "42");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,12 +61,13 @@ fn _add_extension_module_link_args(target_os: &str, mut writer: impl std::io::Wr
|
||||||
/// Loads the configuration determined from the build environment.
|
/// Loads the configuration determined from the build environment.
|
||||||
///
|
///
|
||||||
/// Because this will never change in a given compilation run, this is cached in a `once_cell`.
|
/// Because this will never change in a given compilation run, this is cached in a `once_cell`.
|
||||||
#[doc(hidden)]
|
|
||||||
#[cfg(feature = "resolve-config")]
|
#[cfg(feature = "resolve-config")]
|
||||||
pub fn get() -> &'static InterpreterConfig {
|
pub fn get() -> &'static InterpreterConfig {
|
||||||
static CONFIG: OnceCell<InterpreterConfig> = OnceCell::new();
|
static CONFIG: OnceCell<InterpreterConfig> = OnceCell::new();
|
||||||
CONFIG.get_or_init(|| {
|
CONFIG.get_or_init(|| {
|
||||||
if !CONFIG_FILE.is_empty() {
|
if let Some(interpreter_config) = InterpreterConfig::from_cargo_dep_env() {
|
||||||
|
interpreter_config
|
||||||
|
} else if !CONFIG_FILE.is_empty() {
|
||||||
InterpreterConfig::from_reader(Cursor::new(CONFIG_FILE))
|
InterpreterConfig::from_reader(Cursor::new(CONFIG_FILE))
|
||||||
} else if !ABI3_CONFIG.is_empty() {
|
} else if !ABI3_CONFIG.is_empty() {
|
||||||
Ok(abi3_config())
|
Ok(abi3_config())
|
||||||
|
@ -75,7 +76,7 @@ pub fn get() -> &'static InterpreterConfig {
|
||||||
} else {
|
} else {
|
||||||
InterpreterConfig::from_reader(Cursor::new(HOST_CONFIG))
|
InterpreterConfig::from_reader(Cursor::new(HOST_CONFIG))
|
||||||
}
|
}
|
||||||
.expect("failed to parse PyO3 config file")
|
.expect("failed to parse PyO3 config")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,6 +69,9 @@ fn emit_link_config(interpreter_config: &InterpreterConfig) -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// serialize the whole interpreter config in DEP_PYTHON_PYO3_CONFIG
|
||||||
|
interpreter_config.to_cargo_dep_env()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue