Skip to content

Commit

Permalink
Vendor pyo3-ffi, pyo3-build-config
Browse files Browse the repository at this point in the history
This is pyo3 at 57735540e85e496b448a2fc438e6343b6533ae7a.

To maintain compatibility when _Py APIs are removed from pyo3-ffi.
  • Loading branch information
ijl committed Feb 3, 2024
1 parent ede428a commit 221ca19
Show file tree
Hide file tree
Showing 94 changed files with 13,134 additions and 8 deletions.
8 changes: 2 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ encoding_rs = { version = "0.8", default_features = false }
itoa = { version = "1", default_features = false }
itoap = { version = "1", features = ["std", "simd"] }
once_cell = { version = "1", default_features = false, features = ["race"] }
pyo3-ffi = { version = "^0.20.2", default_features = false, features = ["extension-module"]}
pyo3-ffi = { path = "include/pyo3-ffi", default_features = false, features = ["extension-module"]}
ryu = { version = "1", default_features = false }
serde = { version = "1", default_features = false }
serde_json = { version = "1", default_features = false, features = ["std", "float_roundtrip"] }
Expand All @@ -65,7 +65,7 @@ smallvec = { version = "^1.11", default_features = false, features = ["union", "

[build-dependencies]
cc = { version = "1" }
pyo3-build-config = { version = "^0.20.2" }
pyo3-build-config = { path = "include/pyo3-build-config" }
version_check = { version = "0.9" }

[profile.dev]
Expand Down
40 changes: 40 additions & 0 deletions include/pyo3-build-config/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
[package]
name = "pyo3-build-config"
version = "0.21.0-dev"
description = "Build configuration for the PyO3 ecosystem"
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
keywords = ["pyo3", "python", "cpython", "ffi"]
homepage = "https://github.com/pyo3/pyo3"
repository = "https://github.com/pyo3/pyo3"
categories = ["api-bindings", "development-tools::ffi"]
license = "MIT OR Apache-2.0"
edition = "2021"

[dependencies]
once_cell = "1"
target-lexicon = "^0.12"

[build-dependencies]
target-lexicon = "^0.12"

[features]
default = []

# Attempt to resolve a Python interpreter config for building in the build
# script. If this feature isn't enabled, the build script no-ops.
resolve-config = []

# This feature is enabled by pyo3 when building an extension module.
extension-module = []

# These features are enabled by pyo3 when building Stable ABI extension modules.
abi3 = []
abi3-py37 = ["abi3-py38"]
abi3-py38 = ["abi3-py39"]
abi3-py39 = ["abi3-py310"]
abi3-py310 = ["abi3-py311"]
abi3-py311 = ["abi3-py312"]
abi3-py312 = ["abi3"]

[package.metadata.docs.rs]
features = ["resolve-config"]
74 changes: 74 additions & 0 deletions include/pyo3-build-config/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Import some modules from this crate inline to generate the build config.
// Allow dead code because not all code in the modules is used in this build script.

#[path = "src/impl_.rs"]
#[allow(dead_code)]
mod impl_;

#[path = "src/errors.rs"]
#[allow(dead_code)]
mod errors;

use std::{env, path::Path};

use errors::{Context, Result};
use impl_::{env_var, make_interpreter_config, InterpreterConfig};

fn configure(interpreter_config: Option<InterpreterConfig>, name: &str) -> Result<bool> {
let target = Path::new(&env::var_os("OUT_DIR").unwrap()).join(name);
if let Some(config) = interpreter_config {
config
.to_writer(&mut std::fs::File::create(&target).with_context(|| {
format!("failed to write config file at {}", target.display())
})?)?;
Ok(true)
} else {
std::fs::File::create(&target)
.with_context(|| format!("failed to create new file at {}", target.display()))?;
Ok(false)
}
}

/// If PYO3_CONFIG_FILE is set, copy it into the crate.
fn config_file() -> Result<Option<InterpreterConfig>> {
if let Some(path) = env_var("PYO3_CONFIG_FILE") {
let path = Path::new(&path);
println!("cargo:rerun-if-changed={}", path.display());
// Absolute path is necessary because this build script is run with a cwd different to the
// original `cargo build` instruction.
ensure!(
path.is_absolute(),
"PYO3_CONFIG_FILE must be an absolute path"
);

let interpreter_config = InterpreterConfig::from_path(path)
.context("failed to parse contents of PYO3_CONFIG_FILE")?;
Ok(Some(interpreter_config))
} else {
Ok(None)
}
}

fn generate_build_configs() -> Result<()> {
let configured = configure(config_file()?, "pyo3-build-config-file.txt")?;

if configured {
// Don't bother trying to find an interpreter on the host system
// if the user-provided config file is present.
configure(None, "pyo3-build-config.txt")?;
} else {
configure(Some(make_interpreter_config()?), "pyo3-build-config.txt")?;
}
Ok(())
}

fn main() {
if std::env::var("CARGO_FEATURE_RESOLVE_CONFIG").is_ok() {
if let Err(e) = generate_build_configs() {
eprintln!("error: {}", e.report());
std::process::exit(1)
}
} else {
eprintln!("resolve-config feature not enabled; build script in no-op mode");
}
}
153 changes: 153 additions & 0 deletions include/pyo3-build-config/src/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/// A simple macro for returning an error. Resembles anyhow::bail.
#[macro_export]
#[doc(hidden)]
macro_rules! bail {
($($args: tt)+) => { return Err(format!($($args)+).into()) };
}

/// A simple macro for checking a condition. Resembles anyhow::ensure.
#[macro_export]
#[doc(hidden)]
macro_rules! ensure {
($condition:expr, $($args: tt)+) => { if !($condition) { bail!($($args)+) } };
}

/// Show warning.
#[macro_export]
#[doc(hidden)]
macro_rules! warn {
($($args: tt)+) => {
println!("{}", $crate::format_warn!($($args)+))
};
}

/// Format warning into string.
#[macro_export]
#[doc(hidden)]
macro_rules! format_warn {
($($args: tt)+) => {
format!("cargo:warning={}", format_args!($($args)+))
};
}

/// A simple error implementation which allows chaining of errors, inspired somewhat by anyhow.
#[derive(Debug)]
pub struct Error {
value: String,
source: Option<Box<dyn std::error::Error>>,
}

/// Error report inspired by
/// <https://blog.rust-lang.org/inside-rust/2021/07/01/What-the-error-handling-project-group-is-working-towards.html#2-error-reporter>
pub struct ErrorReport<'a>(&'a Error);

impl Error {
pub fn report(&self) -> ErrorReport<'_> {
ErrorReport(self)
}
}

impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.value)
}
}

impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.source.as_deref()
}
}

impl std::fmt::Display for ErrorReport<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use std::error::Error;
self.0.fmt(f)?;
let mut source = self.0.source();
if source.is_some() {
writeln!(f, "\ncaused by:")?;
let mut index = 0;
while let Some(some_source) = source {
writeln!(f, " - {}: {}", index, some_source)?;
source = some_source.source();
index += 1;
}
}
Ok(())
}
}

impl From<String> for Error {
fn from(value: String) -> Self {
Self {
value,
source: None,
}
}
}

impl From<&'_ str> for Error {
fn from(value: &str) -> Self {
value.to_string().into()
}
}

impl From<std::convert::Infallible> for Error {
fn from(value: std::convert::Infallible) -> Self {
match value {}
}
}

pub type Result<T, E = Error> = std::result::Result<T, E>;

pub trait Context<T> {
fn context(self, message: impl Into<String>) -> Result<T>;
fn with_context(self, message: impl FnOnce() -> String) -> Result<T>;
}

impl<T, E> Context<T> for Result<T, E>
where
E: std::error::Error + 'static,
{
fn context(self, message: impl Into<String>) -> Result<T> {
self.map_err(|error| Error {
value: message.into(),
source: Some(Box::new(error)),
})
}

fn with_context(self, message: impl FnOnce() -> String) -> Result<T> {
self.map_err(|error| Error {
value: message(),
source: Some(Box::new(error)),
})
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn error_report() {
let error: Result<()> = Err(Error::from("there was an internal error"))
.with_context(|| format!("failed to do {}", "something difficult"))
.context("things went wrong");

assert_eq!(
error
.unwrap_err()
.report()
.to_string()
.split('\n')
.collect::<Vec<&str>>(),
vec![
"things went wrong",
"caused by:",
" - 0: failed to do something difficult",
" - 1: there was an internal error",
""
]
);
}
}
Loading

0 comments on commit 221ca19

Please sign in to comment.