Merge branch 'main' into immutable
This commit is contained in:
commit
7118e94947
|
@ -278,7 +278,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get install -y mingw-w64 llvm
|
sudo apt-get install -y mingw-w64 llvm
|
||||||
rustup target add x86_64-pc-windows-gnu x86_64-pc-windows-msvc
|
rustup target add x86_64-pc-windows-gnu x86_64-pc-windows-msvc
|
||||||
which cargo-xwin > /dev/null || cargo install cargo-xwin
|
python -m pip install cargo-xwin
|
||||||
cargo build --manifest-path examples/maturin-starter/Cargo.toml --features abi3 --target x86_64-pc-windows-gnu
|
cargo build --manifest-path examples/maturin-starter/Cargo.toml --features abi3 --target x86_64-pc-windows-gnu
|
||||||
cargo xwin build --manifest-path examples/maturin-starter/Cargo.toml --features abi3 --target x86_64-pc-windows-msvc
|
cargo xwin build --manifest-path examples/maturin-starter/Cargo.toml --features abi3 --target x86_64-pc-windows-msvc
|
||||||
- name: Test cross compile to Windows with maturin
|
- name: Test cross compile to Windows with maturin
|
||||||
|
|
18
CHANGELOG.md
18
CHANGELOG.md
|
@ -8,19 +8,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
## [0.16.4] - 2022-04-14
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Add `PyTzInfoAccess` trait for safe access to time zone information. [#2263](https://github.com/PyO3/pyo3/pull/2263)
|
||||||
- Add an experimental `generate-abi3-import-lib` feature to auto-generate `python3.dll` import libraries for Windows. [#2282](https://github.com/PyO3/pyo3/pull/2282)
|
- Add an experimental `generate-abi3-import-lib` feature to auto-generate `python3.dll` import libraries for Windows. [#2282](https://github.com/PyO3/pyo3/pull/2282)
|
||||||
- Add FFI definitions for `PyDateTime_BaseTime` and `PyDateTime_BaseDateTime`. [#2294](https://github.com/PyO3/pyo3/pull/2294)
|
- Add FFI definitions for `PyDateTime_BaseTime` and `PyDateTime_BaseDateTime`. [#2294](https://github.com/PyO3/pyo3/pull/2294)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Default to "m" ABI tag when choosing `libpython` link name for CPython 3.7 on Unix. [#2288](https://github.com/PyO3/pyo3/pull/2288)
|
|
||||||
- Improved performance of failing calls to `FromPyObject::extract` which is common when functions accept multiple distinct types. [#2279](https://github.com/PyO3/pyo3/pull/2279)
|
- Improved performance of failing calls to `FromPyObject::extract` which is common when functions accept multiple distinct types. [#2279](https://github.com/PyO3/pyo3/pull/2279)
|
||||||
|
- Default to "m" ABI tag when choosing `libpython` link name for CPython 3.7 on Unix. [#2288](https://github.com/PyO3/pyo3/pull/2288)
|
||||||
|
- Allow to compile "abi3" extensions without a working build host Python interpreter. [#2293](https://github.com/PyO3/pyo3/pull/2293)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
- Crates depending on PyO3 can collect code coverage via LLVM instrumentation using stable Rust. [#2286](https://github.com/PyO3/pyo3/pull/2286)
|
||||||
- Fix segfault when calling FFI methods `PyDateTime_DATE_GET_TZINFO` or `PyDateTime_TIME_GET_TZINFO` on `datetime` or `time` without a tzinfo. [#2289](https://github.com/PyO3/pyo3/pull/2289)
|
- Fix segfault when calling FFI methods `PyDateTime_DATE_GET_TZINFO` or `PyDateTime_TIME_GET_TZINFO` on `datetime` or `time` without a tzinfo. [#2289](https://github.com/PyO3/pyo3/pull/2289)
|
||||||
|
- Fix directory names starting with the letter `n` breaking serialization of the interpreter configuration on Windows since PyO3 0.16.3. [#2299](https://github.com/PyO3/pyo3/pull/2299)
|
||||||
|
|
||||||
## [0.16.3] - 2022-04-05
|
## [0.16.3] - 2022-04-05
|
||||||
|
|
||||||
|
@ -164,6 +170,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- Fix FFI definition of `_PyLong_NumBits` to return `size_t` instead of `c_int`. [#2161](https://github.com/PyO3/pyo3/pull/2161)
|
- Fix FFI definition of `_PyLong_NumBits` to return `size_t` instead of `c_int`. [#2161](https://github.com/PyO3/pyo3/pull/2161)
|
||||||
- Fix `TypeError` thrown when argument parsing failed missing the originating causes. [2177](https://github.com/PyO3/pyo3/pull/2178)
|
- Fix `TypeError` thrown when argument parsing failed missing the originating causes. [2177](https://github.com/PyO3/pyo3/pull/2178)
|
||||||
|
|
||||||
|
## [0.15.2] - 2022-04-14
|
||||||
|
|
||||||
|
### Packaging
|
||||||
|
|
||||||
|
- Backport of PyPy 3.9 support from PyO3 0.16. [#2262](https://github.com/PyO3/pyo3/pull/2262)
|
||||||
|
|
||||||
## [0.15.1] - 2021-11-19
|
## [0.15.1] - 2021-11-19
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@ -1145,11 +1157,13 @@ Yanked
|
||||||
|
|
||||||
- Initial release
|
- Initial release
|
||||||
|
|
||||||
[Unreleased]: https://github.com/pyo3/pyo3/compare/v0.16.3...HEAD
|
[Unreleased]: https://github.com/pyo3/pyo3/compare/v0.16.4...HEAD
|
||||||
|
[0.16.3]: https://github.com/pyo3/pyo3/compare/v0.16.3...v0.16.4
|
||||||
[0.16.3]: https://github.com/pyo3/pyo3/compare/v0.16.2...v0.16.3
|
[0.16.3]: https://github.com/pyo3/pyo3/compare/v0.16.2...v0.16.3
|
||||||
[0.16.2]: https://github.com/pyo3/pyo3/compare/v0.16.1...v0.16.2
|
[0.16.2]: https://github.com/pyo3/pyo3/compare/v0.16.1...v0.16.2
|
||||||
[0.16.1]: https://github.com/pyo3/pyo3/compare/v0.16.0...v0.16.1
|
[0.16.1]: https://github.com/pyo3/pyo3/compare/v0.16.0...v0.16.1
|
||||||
[0.16.0]: https://github.com/pyo3/pyo3/compare/v0.15.1...v0.16.0
|
[0.16.0]: https://github.com/pyo3/pyo3/compare/v0.15.1...v0.16.0
|
||||||
|
[0.15.2]: https://github.com/pyo3/pyo3/compare/v0.15.1...v0.15.2
|
||||||
[0.15.1]: https://github.com/pyo3/pyo3/compare/v0.15.0...v0.15.1
|
[0.15.1]: https://github.com/pyo3/pyo3/compare/v0.15.0...v0.15.1
|
||||||
[0.15.0]: https://github.com/pyo3/pyo3/compare/v0.14.5...v0.15.0
|
[0.15.0]: https://github.com/pyo3/pyo3/compare/v0.14.5...v0.15.0
|
||||||
[0.14.5]: https://github.com/pyo3/pyo3/compare/v0.14.4...v0.14.5
|
[0.14.5]: https://github.com/pyo3/pyo3/compare/v0.14.4...v0.14.5
|
||||||
|
|
10
Cargo.toml
10
Cargo.toml
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "pyo3"
|
name = "pyo3"
|
||||||
version = "0.16.3"
|
version = "0.16.4"
|
||||||
description = "Bindings to Python interpreter"
|
description = "Bindings to Python interpreter"
|
||||||
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
|
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
@ -19,10 +19,10 @@ libc = "0.2.62"
|
||||||
parking_lot = ">= 0.11, < 0.13"
|
parking_lot = ">= 0.11, < 0.13"
|
||||||
|
|
||||||
# ffi bindings to the python interpreter, split into a seperate crate so they can be used independently
|
# ffi bindings to the python interpreter, split into a seperate crate so they can be used independently
|
||||||
pyo3-ffi = { path = "pyo3-ffi", version = "=0.16.3" }
|
pyo3-ffi = { path = "pyo3-ffi", version = "=0.16.4" }
|
||||||
|
|
||||||
# support crates for macros feature
|
# support crates for macros feature
|
||||||
pyo3-macros = { path = "pyo3-macros", version = "=0.16.3", optional = true }
|
pyo3-macros = { path = "pyo3-macros", version = "=0.16.4", optional = true }
|
||||||
indoc = { version = "1.0.3", optional = true }
|
indoc = { version = "1.0.3", optional = true }
|
||||||
unindent = { version = "0.1.4", optional = true }
|
unindent = { version = "0.1.4", optional = true }
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0.61"
|
serde_json = "1.0.61"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
pyo3-build-config = { path = "pyo3-build-config", version = "0.16.3", features = ["resolve-config"] }
|
pyo3-build-config = { path = "pyo3-build-config", version = "0.16.4", features = ["resolve-config"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["macros", "pyproto"]
|
default = ["macros", "pyproto"]
|
||||||
|
@ -67,7 +67,7 @@ pyproto = ["pyo3-macros/pyproto"]
|
||||||
# Use this feature when building an extension module.
|
# Use this feature when building an extension module.
|
||||||
# It tells the linker to keep the python symbols unresolved,
|
# It tells the linker to keep the python symbols unresolved,
|
||||||
# so that the module can also be used with statically linked python interpreters.
|
# so that the module can also be used with statically linked python interpreters.
|
||||||
extension-module = ["pyo3-ffi/extension-module"]
|
extension-module = ["pyo3-build-config/extension-module", "pyo3-ffi/extension-module"]
|
||||||
|
|
||||||
# Use the Python limited API. See https://www.python.org/dev/peps/pep-0384/ for more.
|
# Use the Python limited API. See https://www.python.org/dev/peps/pep-0384/ for more.
|
||||||
abi3 = ["pyo3-build-config/abi3", "pyo3-ffi/abi3", "pyo3-macros/abi3"]
|
abi3 = ["pyo3-build-config/abi3", "pyo3-ffi/abi3", "pyo3-macros/abi3"]
|
||||||
|
|
|
@ -68,7 +68,7 @@ name = "string_sum"
|
||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
pyo3 = { version = "0.16.3", features = ["extension-module"] }
|
pyo3 = { version = "0.16.4", features = ["extension-module"] }
|
||||||
```
|
```
|
||||||
|
|
||||||
**`src/lib.rs`**
|
**`src/lib.rs`**
|
||||||
|
@ -132,7 +132,7 @@ Start a new project with `cargo new` and add `pyo3` to the `Cargo.toml` like th
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies.pyo3]
|
[dependencies.pyo3]
|
||||||
version = "0.16.3"
|
version = "0.16.4"
|
||||||
features = ["auto-initialize"]
|
features = ["auto-initialize"]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ publish = false
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pyo3 = { version = "0.16.3", path = "..", features = ["auto-initialize", "extension-module"] }
|
pyo3 = { version = "0.16.4", path = "..", features = ["auto-initialize", "extension-module"] }
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "decorator"
|
name = "decorator"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
variable::set("PYO3_VERSION", "0.16.3");
|
variable::set("PYO3_VERSION", "0.16.4");
|
||||||
file::rename(".template/Cargo.toml", "Cargo.toml");
|
file::rename(".template/Cargo.toml", "Cargo.toml");
|
||||||
file::rename(".template/pyproject.toml", "pyproject.toml");
|
file::rename(".template/pyproject.toml", "pyproject.toml");
|
||||||
file::rename(".template/tox.ini", "tox.ini");
|
file::rename(".template/tox.ini", "tox.ini");
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
variable::set("PYO3_VERSION", "0.16.3");
|
variable::set("PYO3_VERSION", "0.16.4");
|
||||||
file::rename(".template/Cargo.toml", "Cargo.toml");
|
file::rename(".template/Cargo.toml", "Cargo.toml");
|
||||||
file::rename(".template/pyproject.toml", "pyproject.toml");
|
file::rename(".template/pyproject.toml", "pyproject.toml");
|
||||||
file::rename(".template/tox.ini", "tox.ini");
|
file::rename(".template/tox.ini", "tox.ini");
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
variable::set("PYO3_VERSION", "0.16.3");
|
variable::set("PYO3_VERSION", "0.16.4");
|
||||||
file::rename(".template/Cargo.toml", "Cargo.toml");
|
file::rename(".template/Cargo.toml", "Cargo.toml");
|
||||||
file::rename(".template/setup.cfg", "setup.cfg");
|
file::rename(".template/setup.cfg", "setup.cfg");
|
||||||
file::rename(".template/tox.ini", "tox.ini");
|
file::rename(".template/tox.ini", "tox.ini");
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
variable::set("PYO3_VERSION", "0.16.3");
|
variable::set("PYO3_VERSION", "0.16.4");
|
||||||
file::rename(".template/Cargo.toml", "Cargo.toml");
|
file::rename(".template/Cargo.toml", "Cargo.toml");
|
||||||
file::rename(".template/tox.ini", "tox.ini");
|
file::rename(".template/tox.ini", "tox.ini");
|
||||||
file::delete(".template");
|
file::delete(".template");
|
||||||
|
|
|
@ -157,6 +157,8 @@ PyO3 is only able to link your extension module to api3 version up to and includ
|
||||||
#### Building `abi3` extensions without a Python interpreter
|
#### Building `abi3` extensions without a Python interpreter
|
||||||
|
|
||||||
As an advanced feature, you can build PyO3 wheel without calling Python interpreter with the environment variable `PYO3_NO_PYTHON` set.
|
As an advanced feature, you can build PyO3 wheel without calling Python interpreter with the environment variable `PYO3_NO_PYTHON` set.
|
||||||
|
Also, if the build host Python interpreter is not found or is too old or otherwise unusable,
|
||||||
|
PyO3 will still attempt to compile `abi3` extension modules after displaying a warning message.
|
||||||
On Unix-like systems this works unconditionally; on Windows you must also set the `RUSTFLAGS` environment variable
|
On Unix-like systems this works unconditionally; on Windows you must also set the `RUSTFLAGS` environment variable
|
||||||
to contain `-L native=/path/to/python/libs` so that the linker can find `python3.lib`.
|
to contain `-L native=/path/to/python/libs` so that the linker can find `python3.lib`.
|
||||||
|
|
||||||
|
|
|
@ -124,7 +124,7 @@ def contributors(session: nox.Session) -> None:
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
authors = sorted(list(authors))
|
authors = sorted(list(authors), key=lambda author: author.lower())
|
||||||
|
|
||||||
for author in authors:
|
for author in authors:
|
||||||
print(f"@{author}")
|
print(f"@{author}")
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "pyo3-build-config"
|
name = "pyo3-build-config"
|
||||||
version = "0.16.3"
|
version = "0.16.4"
|
||||||
description = "Build configuration for the PyO3 ecosystem"
|
description = "Build configuration for the PyO3 ecosystem"
|
||||||
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
|
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
|
||||||
keywords = ["pyo3", "python", "cpython", "ffi"]
|
keywords = ["pyo3", "python", "cpython", "ffi"]
|
||||||
|
@ -26,6 +26,10 @@ default = []
|
||||||
# script. If this feature isn't enabled, the build script no-ops.
|
# script. If this feature isn't enabled, the build script no-ops.
|
||||||
resolve-config = []
|
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 = []
|
||||||
abi3-py37 = ["abi3-py38"]
|
abi3-py37 = ["abi3-py38"]
|
||||||
abi3-py38 = ["abi3-py39"]
|
abi3-py38 = ["abi3-py39"]
|
||||||
|
|
|
@ -233,6 +233,13 @@ print("mingw", get_platform().startswith("mingw"))
|
||||||
"#;
|
"#;
|
||||||
let output = run_python_script(interpreter.as_ref(), SCRIPT)?;
|
let output = run_python_script(interpreter.as_ref(), SCRIPT)?;
|
||||||
let map: HashMap<String, String> = parse_script_output(&output);
|
let map: HashMap<String, String> = parse_script_output(&output);
|
||||||
|
|
||||||
|
ensure!(
|
||||||
|
!map.is_empty(),
|
||||||
|
"broken Python interpreter: {}",
|
||||||
|
interpreter.as_ref().display()
|
||||||
|
);
|
||||||
|
|
||||||
let shared = map["shared"].as_str() == "True";
|
let shared = map["shared"].as_str() == "True";
|
||||||
|
|
||||||
let version = PythonVersion {
|
let version = PythonVersion {
|
||||||
|
@ -366,7 +373,7 @@ print("mingw", get_platform().startswith("mingw"))
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn from_cargo_dep_env() -> Option<Result<Self>> {
|
pub fn from_cargo_dep_env() -> Option<Result<Self>> {
|
||||||
cargo_env_var("DEP_PYTHON_PYO3_CONFIG")
|
cargo_env_var("DEP_PYTHON_PYO3_CONFIG")
|
||||||
.map(|buf| InterpreterConfig::from_reader(buf.replace("\\n", "\n").as_bytes()))
|
.map(|buf| InterpreterConfig::from_reader(&*unescape(&buf)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
@ -464,11 +471,7 @@ print("mingw", get_platform().startswith("mingw"))
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
self.to_writer(&mut buf)?;
|
self.to_writer(&mut buf)?;
|
||||||
// escape newlines in env var
|
// escape newlines in env var
|
||||||
if let Ok(config) = str::from_utf8(&buf) {
|
println!("cargo:PYO3_CONFIG={}", escape(&buf));
|
||||||
println!("cargo:PYO3_CONFIG={}", config.replace('\n', "\\n"));
|
|
||||||
} else {
|
|
||||||
bail!("unable to emit interpreter config to link env for downstream use");
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -682,14 +685,14 @@ pub fn is_extension_module() -> bool {
|
||||||
|
|
||||||
/// Checks if we need to link to `libpython` for the current build target.
|
/// Checks if we need to link to `libpython` for the current build target.
|
||||||
///
|
///
|
||||||
/// Must be called from a crate PyO3 build script.
|
/// Must be called from a PyO3 crate build script.
|
||||||
pub fn is_linking_libpython() -> bool {
|
pub fn is_linking_libpython() -> bool {
|
||||||
is_linking_libpython_for_target(&target_triple_from_env())
|
is_linking_libpython_for_target(&target_triple_from_env())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if we need to link to `libpython` for the target.
|
/// Checks if we need to link to `libpython` for the target.
|
||||||
///
|
///
|
||||||
/// Must be called from a crate PyO3 build script.
|
/// Must be called from a PyO3 crate build script.
|
||||||
fn is_linking_libpython_for_target(target: &Triple) -> bool {
|
fn is_linking_libpython_for_target(target: &Triple) -> bool {
|
||||||
target.operating_system == OperatingSystem::Windows
|
target.operating_system == OperatingSystem::Windows
|
||||||
|| target.environment == Environment::Android
|
|| target.environment == Environment::Android
|
||||||
|
@ -697,6 +700,18 @@ fn is_linking_libpython_for_target(target: &Triple) -> bool {
|
||||||
|| !is_extension_module()
|
|| !is_extension_module()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if we need to discover the Python library directory
|
||||||
|
/// to link the extension module binary.
|
||||||
|
///
|
||||||
|
/// Must be called from a PyO3 crate build script.
|
||||||
|
fn require_libdir_for_target(target: &Triple) -> bool {
|
||||||
|
let is_generating_libpython = cfg!(feature = "python3-dll-a")
|
||||||
|
&& target.operating_system == OperatingSystem::Windows
|
||||||
|
&& is_abi3();
|
||||||
|
|
||||||
|
is_linking_libpython_for_target(target) && !is_generating_libpython
|
||||||
|
}
|
||||||
|
|
||||||
/// Configuration needed by PyO3 to cross-compile for a target platform.
|
/// Configuration needed by PyO3 to cross-compile for a target platform.
|
||||||
///
|
///
|
||||||
/// Usually this is collected from the environment (i.e. `PYO3_CROSS_*` and `CARGO_CFG_TARGET_*`)
|
/// Usually this is collected from the environment (i.e. `PYO3_CROSS_*` and `CARGO_CFG_TARGET_*`)
|
||||||
|
@ -1646,6 +1661,18 @@ pub fn find_interpreter() -> Result<PathBuf> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Locates and extracts the build host Python interpreter configuration.
|
||||||
|
///
|
||||||
|
/// Lowers the configured Python version to `abi3_version` if required.
|
||||||
|
fn get_host_interpreter(abi3_version: Option<PythonVersion>) -> Result<InterpreterConfig> {
|
||||||
|
let interpreter_path = find_interpreter()?;
|
||||||
|
|
||||||
|
let mut interpreter_config = InterpreterConfig::from_interpreter(interpreter_path)?;
|
||||||
|
interpreter_config.fixup_for_abi3_version(abi3_version)?;
|
||||||
|
|
||||||
|
Ok(interpreter_config)
|
||||||
|
}
|
||||||
|
|
||||||
/// Generates an interpreter config suitable for cross-compilation.
|
/// Generates an interpreter config suitable for cross-compilation.
|
||||||
///
|
///
|
||||||
/// This must be called from PyO3's build script, because it relies on environment variables such as
|
/// This must be called from PyO3's build script, because it relies on environment variables such as
|
||||||
|
@ -1666,31 +1693,78 @@ pub fn make_cross_compile_config() -> Result<Option<InterpreterConfig>> {
|
||||||
/// Only used by `pyo3-build-config` build script.
|
/// Only used by `pyo3-build-config` build script.
|
||||||
#[allow(dead_code, unused_mut)]
|
#[allow(dead_code, unused_mut)]
|
||||||
pub fn make_interpreter_config() -> Result<InterpreterConfig> {
|
pub fn make_interpreter_config() -> Result<InterpreterConfig> {
|
||||||
|
let host = Triple::host();
|
||||||
let abi3_version = get_abi3_version();
|
let abi3_version = get_abi3_version();
|
||||||
|
|
||||||
if have_python_interpreter() {
|
// See if we can safely skip the Python interpreter configuration detection.
|
||||||
let mut interpreter_config = InterpreterConfig::from_interpreter(find_interpreter()?)?;
|
// Unix "abi3" extension modules can usually be built without any interpreter.
|
||||||
interpreter_config.fixup_for_abi3_version(abi3_version)?;
|
let need_interpreter = abi3_version.is_none() || require_libdir_for_target(&host);
|
||||||
Ok(interpreter_config)
|
|
||||||
} else if let Some(version) = abi3_version {
|
|
||||||
let host = Triple::host();
|
|
||||||
let mut interpreter_config = default_abi3_config(&host, version);
|
|
||||||
|
|
||||||
// Auto generate python3.dll import libraries for Windows targets.
|
if have_python_interpreter() {
|
||||||
#[cfg(feature = "python3-dll-a")]
|
match get_host_interpreter(abi3_version) {
|
||||||
{
|
Ok(interpreter_config) => return Ok(interpreter_config),
|
||||||
interpreter_config.lib_dir = self::abi3_import_lib::generate_abi3_import_lib(&host)?;
|
// Bail if the interpreter configuration is required to build.
|
||||||
|
Err(e) if need_interpreter => return Err(e),
|
||||||
|
_ => {
|
||||||
|
// Fall back to the "abi3" defaults just as if `PYO3_NO_PYTHON`
|
||||||
|
// environment variable was set.
|
||||||
|
warn!("Compiling without a working Python interpreter.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ensure!(
|
||||||
|
abi3_version.is_some(),
|
||||||
|
"An abi3-py3* feature must be specified when compiling without a Python interpreter."
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut interpreter_config = default_abi3_config(&host, abi3_version.unwrap());
|
||||||
|
|
||||||
|
// Auto generate python3.dll import libraries for Windows targets.
|
||||||
|
#[cfg(feature = "python3-dll-a")]
|
||||||
|
{
|
||||||
|
interpreter_config.lib_dir = self::abi3_import_lib::generate_abi3_import_lib(&host)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(interpreter_config)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn escape(bytes: &[u8]) -> String {
|
||||||
|
let mut escaped = String::with_capacity(2 * bytes.len());
|
||||||
|
|
||||||
|
for byte in bytes {
|
||||||
|
const LUT: &[u8; 16] = b"0123456789abcdef";
|
||||||
|
|
||||||
|
escaped.push(LUT[(byte >> 4) as usize] as char);
|
||||||
|
escaped.push(LUT[(byte & 0x0F) as usize] as char);
|
||||||
|
}
|
||||||
|
|
||||||
|
escaped
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unescape(escaped: &str) -> Vec<u8> {
|
||||||
|
assert!(escaped.len() % 2 == 0, "invalid hex encoding");
|
||||||
|
|
||||||
|
let mut bytes = Vec::with_capacity(escaped.len() / 2);
|
||||||
|
|
||||||
|
for chunk in escaped.as_bytes().chunks_exact(2) {
|
||||||
|
fn unhex(hex: u8) -> u8 {
|
||||||
|
match hex {
|
||||||
|
b'a'..=b'f' => hex - b'a' + 10,
|
||||||
|
b'0'..=b'9' => hex - b'0',
|
||||||
|
_ => panic!("invalid hex encoding"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(interpreter_config)
|
bytes.push(unhex(chunk[0]) << 4 | unhex(chunk[1]));
|
||||||
} else {
|
|
||||||
bail!("An abi3-py3* feature must be specified when compiling without a Python interpreter.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::{io::Cursor, iter::FromIterator};
|
use std::iter::FromIterator;
|
||||||
use target_lexicon::triple;
|
use target_lexicon::triple;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -1713,10 +1787,7 @@ mod tests {
|
||||||
let mut buf: Vec<u8> = Vec::new();
|
let mut buf: Vec<u8> = Vec::new();
|
||||||
config.to_writer(&mut buf).unwrap();
|
config.to_writer(&mut buf).unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(config, InterpreterConfig::from_reader(&*buf).unwrap());
|
||||||
config,
|
|
||||||
InterpreterConfig::from_reader(Cursor::new(buf)).unwrap()
|
|
||||||
);
|
|
||||||
|
|
||||||
// And some different options, for variety
|
// And some different options, for variety
|
||||||
|
|
||||||
|
@ -1744,17 +1815,37 @@ mod tests {
|
||||||
let mut buf: Vec<u8> = Vec::new();
|
let mut buf: Vec<u8> = Vec::new();
|
||||||
config.to_writer(&mut buf).unwrap();
|
config.to_writer(&mut buf).unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(config, InterpreterConfig::from_reader(&*buf).unwrap());
|
||||||
config,
|
}
|
||||||
InterpreterConfig::from_reader(Cursor::new(buf)).unwrap()
|
|
||||||
);
|
#[test]
|
||||||
|
fn test_config_file_roundtrip_with_escaping() {
|
||||||
|
let config = InterpreterConfig {
|
||||||
|
abi3: true,
|
||||||
|
build_flags: BuildFlags::default(),
|
||||||
|
pointer_width: Some(32),
|
||||||
|
executable: Some("executable".into()),
|
||||||
|
implementation: PythonImplementation::CPython,
|
||||||
|
lib_name: Some("lib_name".into()),
|
||||||
|
lib_dir: Some("lib_dir\\n".into()),
|
||||||
|
shared: true,
|
||||||
|
version: MINIMUM_SUPPORTED_VERSION,
|
||||||
|
suppress_build_script_link_lines: true,
|
||||||
|
extra_build_script_lines: vec!["cargo:test1".to_string(), "cargo:test2".to_string()],
|
||||||
|
};
|
||||||
|
let mut buf: Vec<u8> = Vec::new();
|
||||||
|
config.to_writer(&mut buf).unwrap();
|
||||||
|
|
||||||
|
let buf = unescape(&escape(&buf));
|
||||||
|
|
||||||
|
assert_eq!(config, InterpreterConfig::from_reader(&*buf).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_config_file_defaults() {
|
fn test_config_file_defaults() {
|
||||||
// Only version is required
|
// Only version is required
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
InterpreterConfig::from_reader(Cursor::new("version=3.7")).unwrap(),
|
InterpreterConfig::from_reader("version=3.7".as_bytes()).unwrap(),
|
||||||
InterpreterConfig {
|
InterpreterConfig {
|
||||||
version: PythonVersion { major: 3, minor: 7 },
|
version: PythonVersion { major: 3, minor: 7 },
|
||||||
implementation: PythonImplementation::CPython,
|
implementation: PythonImplementation::CPython,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "pyo3-ffi"
|
name = "pyo3-ffi"
|
||||||
version = "0.16.3"
|
version = "0.16.4"
|
||||||
description = "Python-API bindings for the PyO3 ecosystem"
|
description = "Python-API bindings for the PyO3 ecosystem"
|
||||||
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
|
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
|
||||||
keywords = ["pyo3", "python", "cpython", "ffi"]
|
keywords = ["pyo3", "python", "cpython", "ffi"]
|
||||||
|
@ -35,4 +35,4 @@ abi3-py310 = ["abi3", "pyo3-build-config/abi3-py310"]
|
||||||
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
pyo3-build-config = { path = "../pyo3-build-config", version = "0.16.3", features = ["resolve-config"] }
|
pyo3-build-config = { path = "../pyo3-build-config", version = "0.16.4", features = ["resolve-config"] }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "pyo3-macros-backend"
|
name = "pyo3-macros-backend"
|
||||||
version = "0.16.3"
|
version = "0.16.4"
|
||||||
description = "Code generation for PyO3 package"
|
description = "Code generation for PyO3 package"
|
||||||
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
|
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
|
||||||
keywords = ["pyo3", "python", "cpython", "ffi"]
|
keywords = ["pyo3", "python", "cpython", "ffi"]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "pyo3-macros"
|
name = "pyo3-macros"
|
||||||
version = "0.16.3"
|
version = "0.16.4"
|
||||||
description = "Proc macros for PyO3 package"
|
description = "Proc macros for PyO3 package"
|
||||||
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
|
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
|
||||||
keywords = ["pyo3", "python", "cpython", "ffi"]
|
keywords = ["pyo3", "python", "cpython", "ffi"]
|
||||||
|
@ -23,4 +23,4 @@ abi3 = ["pyo3-macros-backend/abi3"]
|
||||||
proc-macro2 = { version = "1", default-features = false }
|
proc-macro2 = { version = "1", default-features = false }
|
||||||
quote = "1"
|
quote = "1"
|
||||||
syn = { version = "1.0.56", features = ["full", "extra-traits"] }
|
syn = { version = "1.0.56", features = ["full", "extra-traits"] }
|
||||||
pyo3-macros-backend = { path = "../pyo3-macros-backend", version = "=0.16.3" }
|
pyo3-macros-backend = { path = "../pyo3-macros-backend", version = "=0.16.4" }
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
use pyo3::types::{
|
use pyo3::types::{
|
||||||
PyDate, PyDateAccess, PyDateTime, PyDelta, PyDeltaAccess, PyTime, PyTimeAccess, PyTuple,
|
PyDate, PyDateAccess, PyDateTime, PyDelta, PyDeltaAccess, PyTime, PyTimeAccess, PyTuple,
|
||||||
PyTzInfo,
|
PyTzInfo, PyTzInfoAccess,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
|
@ -179,6 +179,16 @@ fn datetime_from_timestamp<'p>(
|
||||||
PyDateTime::from_timestamp(py, ts, tz)
|
PyDateTime::from_timestamp(py, ts, tz)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[pyfunction]
|
||||||
|
fn get_datetime_tzinfo(dt: &PyDateTime) -> Option<&PyTzInfo> {
|
||||||
|
dt.get_tzinfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyfunction]
|
||||||
|
fn get_time_tzinfo(dt: &PyTime) -> Option<&PyTzInfo> {
|
||||||
|
dt.get_tzinfo()
|
||||||
|
}
|
||||||
|
|
||||||
#[pyclass(extends=PyTzInfo)]
|
#[pyclass(extends=PyTzInfo)]
|
||||||
pub struct TzClass {}
|
pub struct TzClass {}
|
||||||
|
|
||||||
|
@ -214,6 +224,8 @@ pub fn datetime(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
|
||||||
m.add_function(wrap_pyfunction!(make_datetime, m)?)?;
|
m.add_function(wrap_pyfunction!(make_datetime, m)?)?;
|
||||||
m.add_function(wrap_pyfunction!(get_datetime_tuple, m)?)?;
|
m.add_function(wrap_pyfunction!(get_datetime_tuple, m)?)?;
|
||||||
m.add_function(wrap_pyfunction!(datetime_from_timestamp, m)?)?;
|
m.add_function(wrap_pyfunction!(datetime_from_timestamp, m)?)?;
|
||||||
|
m.add_function(wrap_pyfunction!(get_datetime_tzinfo, m)?)?;
|
||||||
|
m.add_function(wrap_pyfunction!(get_time_tzinfo, m)?)?;
|
||||||
|
|
||||||
// Functions not supported by PyPy
|
// Functions not supported by PyPy
|
||||||
#[cfg(not(PyPy))]
|
#[cfg(not(PyPy))]
|
||||||
|
|
|
@ -114,6 +114,7 @@ def test_time(args, kwargs):
|
||||||
|
|
||||||
assert act == exp
|
assert act == exp
|
||||||
assert act.tzinfo is exp.tzinfo
|
assert act.tzinfo is exp.tzinfo
|
||||||
|
assert rdt.get_time_tzinfo(act) == exp.tzinfo
|
||||||
|
|
||||||
|
|
||||||
@given(t=st.times())
|
@given(t=st.times())
|
||||||
|
@ -194,6 +195,7 @@ def test_datetime(args, kwargs):
|
||||||
|
|
||||||
assert act == exp
|
assert act == exp
|
||||||
assert act.tzinfo is exp.tzinfo
|
assert act.tzinfo is exp.tzinfo
|
||||||
|
assert rdt.get_datetime_tzinfo(act) == exp.tzinfo
|
||||||
|
|
||||||
|
|
||||||
@given(dt=st.datetimes())
|
@given(dt=st.datetimes())
|
||||||
|
|
|
@ -4,9 +4,8 @@
|
||||||
//! documentation](https://docs.python.org/3/library/datetime.html)
|
//! documentation](https://docs.python.org/3/library/datetime.html)
|
||||||
|
|
||||||
use crate::err::PyResult;
|
use crate::err::PyResult;
|
||||||
use crate::ffi;
|
|
||||||
use crate::ffi::{
|
use crate::ffi::{
|
||||||
PyDateTime_CAPI, PyDateTime_FromTimestamp, PyDateTime_IMPORT, PyDate_FromTimestamp,
|
self, PyDateTime_CAPI, PyDateTime_FromTimestamp, PyDateTime_IMPORT, PyDate_FromTimestamp,
|
||||||
};
|
};
|
||||||
#[cfg(not(PyPy))]
|
#[cfg(not(PyPy))]
|
||||||
use crate::ffi::{PyDateTime_DATE_GET_FOLD, PyDateTime_TIME_GET_FOLD};
|
use crate::ffi::{PyDateTime_DATE_GET_FOLD, PyDateTime_TIME_GET_FOLD};
|
||||||
|
@ -22,6 +21,7 @@ use crate::ffi::{
|
||||||
PyDateTime_TIME_GET_HOUR, PyDateTime_TIME_GET_MICROSECOND, PyDateTime_TIME_GET_MINUTE,
|
PyDateTime_TIME_GET_HOUR, PyDateTime_TIME_GET_MICROSECOND, PyDateTime_TIME_GET_MINUTE,
|
||||||
PyDateTime_TIME_GET_SECOND,
|
PyDateTime_TIME_GET_SECOND,
|
||||||
};
|
};
|
||||||
|
use crate::instance::PyNativeType;
|
||||||
use crate::types::PyTuple;
|
use crate::types::PyTuple;
|
||||||
use crate::{AsPyPointer, PyAny, PyObject, Python, ToPyObject};
|
use crate::{AsPyPointer, PyAny, PyObject, Python, ToPyObject};
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
@ -160,6 +160,16 @@ pub trait PyTimeAccess {
|
||||||
fn get_fold(&self) -> bool;
|
fn get_fold(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Trait for accessing the components of a struct containing a tzinfo.
|
||||||
|
pub trait PyTzInfoAccess {
|
||||||
|
/// Returns the tzinfo (which may be None).
|
||||||
|
///
|
||||||
|
/// Implementations should conform to the upstream documentation:
|
||||||
|
/// <https://docs.python.org/3/c-api/datetime.html#c.PyDateTime_DATE_GET_TZINFO>
|
||||||
|
/// <https://docs.python.org/3/c-api/datetime.html#c.PyDateTime_TIME_GET_TZINFO>
|
||||||
|
fn get_tzinfo(&self) -> Option<&PyTzInfo>;
|
||||||
|
}
|
||||||
|
|
||||||
/// Bindings around `datetime.date`
|
/// Bindings around `datetime.date`
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct PyDate(PyAny);
|
pub struct PyDate(PyAny);
|
||||||
|
@ -354,6 +364,19 @@ impl PyTimeAccess for PyDateTime {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PyTzInfoAccess for PyDateTime {
|
||||||
|
fn get_tzinfo(&self) -> Option<&PyTzInfo> {
|
||||||
|
let ptr = self.as_ptr() as *mut ffi::PyDateTime_DateTime;
|
||||||
|
unsafe {
|
||||||
|
if (*ptr).hastzinfo != 0 {
|
||||||
|
Some(self.py().from_borrowed_ptr((*ptr).tzinfo))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Bindings for `datetime.time`
|
/// Bindings for `datetime.time`
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct PyTime(PyAny);
|
pub struct PyTime(PyAny);
|
||||||
|
@ -439,6 +462,19 @@ impl PyTimeAccess for PyTime {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PyTzInfoAccess for PyTime {
|
||||||
|
fn get_tzinfo(&self) -> Option<&PyTzInfo> {
|
||||||
|
let ptr = self.as_ptr() as *mut ffi::PyDateTime_Time;
|
||||||
|
unsafe {
|
||||||
|
if (*ptr).hastzinfo != 0 {
|
||||||
|
Some(self.py().from_borrowed_ptr((*ptr).tzinfo))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Bindings for `datetime.tzinfo`
|
/// Bindings for `datetime.tzinfo`
|
||||||
///
|
///
|
||||||
/// This is an abstract base class and should not be constructed directly.
|
/// This is an abstract base class and should not be constructed directly.
|
||||||
|
@ -524,4 +560,33 @@ mod tests {
|
||||||
assert!(b.unwrap().get_fold());
|
assert!(b.unwrap().get_fold());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(PyPy))]
|
||||||
|
#[test]
|
||||||
|
fn test_get_tzinfo() {
|
||||||
|
crate::Python::with_gil(|py| {
|
||||||
|
use crate::conversion::ToPyObject;
|
||||||
|
use crate::types::{PyDateTime, PyTime, PyTzInfoAccess};
|
||||||
|
|
||||||
|
let datetime = py.import("datetime").map_err(|e| e.print(py)).unwrap();
|
||||||
|
let timezone = datetime.getattr("timezone").unwrap();
|
||||||
|
let utc = timezone.getattr("utc").unwrap().to_object(py);
|
||||||
|
|
||||||
|
let dt = PyDateTime::new(py, 2018, 1, 1, 0, 0, 0, 0, Some(&utc)).unwrap();
|
||||||
|
|
||||||
|
assert!(dt.get_tzinfo().unwrap().eq(&utc).unwrap());
|
||||||
|
|
||||||
|
let dt = PyDateTime::new(py, 2018, 1, 1, 0, 0, 0, 0, None).unwrap();
|
||||||
|
|
||||||
|
assert!(dt.get_tzinfo().is_none());
|
||||||
|
|
||||||
|
let t = PyTime::new(py, 0, 0, 0, 0, Some(&utc)).unwrap();
|
||||||
|
|
||||||
|
assert!(t.get_tzinfo().unwrap().eq(&utc).unwrap());
|
||||||
|
|
||||||
|
let t = PyTime::new(py, 0, 0, 0, 0, None).unwrap();
|
||||||
|
|
||||||
|
assert!(t.get_tzinfo().is_none());
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ pub use self::complex::PyComplex;
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
pub use self::datetime::{
|
pub use self::datetime::{
|
||||||
PyDate, PyDateAccess, PyDateTime, PyDelta, PyDeltaAccess, PyTime, PyTimeAccess, PyTzInfo,
|
PyDate, PyDateAccess, PyDateTime, PyDelta, PyDeltaAccess, PyTime, PyTimeAccess, PyTzInfo,
|
||||||
|
PyTzInfoAccess,
|
||||||
};
|
};
|
||||||
pub use self::dict::{IntoPyDict, PyDict};
|
pub use self::dict::{IntoPyDict, PyDict};
|
||||||
pub use self::floatob::PyFloat;
|
pub use self::floatob::PyFloat;
|
||||||
|
|
Loading…
Reference in New Issue