Merge branch 'main' into immutable
This commit is contained in:
commit
7118e94947
|
@ -278,7 +278,7 @@ jobs:
|
|||
run: |
|
||||
sudo apt-get install -y mingw-w64 llvm
|
||||
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 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
|
||||
|
|
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
|
||||
|
||||
## [0.16.4] - 2022-04-14
|
||||
|
||||
### 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 FFI definitions for `PyDateTime_BaseTime` and `PyDateTime_BaseDateTime`. [#2294](https://github.com/PyO3/pyo3/pull/2294)
|
||||
|
||||
### 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)
|
||||
- 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
|
||||
|
||||
- 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 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
|
||||
|
||||
|
@ -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 `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
|
||||
|
||||
### Added
|
||||
|
@ -1145,11 +1157,13 @@ Yanked
|
|||
|
||||
- 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.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.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.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
|
||||
|
|
10
Cargo.toml
10
Cargo.toml
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "pyo3"
|
||||
version = "0.16.3"
|
||||
version = "0.16.4"
|
||||
description = "Bindings to Python interpreter"
|
||||
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
|
||||
readme = "README.md"
|
||||
|
@ -19,10 +19,10 @@ libc = "0.2.62"
|
|||
parking_lot = ">= 0.11, < 0.13"
|
||||
|
||||
# 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
|
||||
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 }
|
||||
unindent = { version = "0.1.4", optional = true }
|
||||
|
||||
|
@ -50,7 +50,7 @@ serde = { version = "1.0", features = ["derive"] }
|
|||
serde_json = "1.0.61"
|
||||
|
||||
[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]
|
||||
default = ["macros", "pyproto"]
|
||||
|
@ -67,7 +67,7 @@ pyproto = ["pyo3-macros/pyproto"]
|
|||
# Use this feature when building an extension module.
|
||||
# It tells the linker to keep the python symbols unresolved,
|
||||
# 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.
|
||||
abi3 = ["pyo3-build-config/abi3", "pyo3-ffi/abi3", "pyo3-macros/abi3"]
|
||||
|
|
|
@ -68,7 +68,7 @@ name = "string_sum"
|
|||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
pyo3 = { version = "0.16.3", features = ["extension-module"] }
|
||||
pyo3 = { version = "0.16.4", features = ["extension-module"] }
|
||||
```
|
||||
|
||||
**`src/lib.rs`**
|
||||
|
@ -132,7 +132,7 @@ Start a new project with `cargo new` and add `pyo3` to the `Cargo.toml` like th
|
|||
|
||||
```toml
|
||||
[dependencies.pyo3]
|
||||
version = "0.16.3"
|
||||
version = "0.16.4"
|
||||
features = ["auto-initialize"]
|
||||
```
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ publish = false
|
|||
edition = "2018"
|
||||
|
||||
[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]]
|
||||
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/pyproject.toml", "pyproject.toml");
|
||||
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/pyproject.toml", "pyproject.toml");
|
||||
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/setup.cfg", "setup.cfg");
|
||||
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/tox.ini", "tox.ini");
|
||||
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
|
||||
|
||||
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
|
||||
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:
|
||||
break
|
||||
|
||||
authors = sorted(list(authors))
|
||||
authors = sorted(list(authors), key=lambda author: author.lower())
|
||||
|
||||
for author in authors:
|
||||
print(f"@{author}")
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "pyo3-build-config"
|
||||
version = "0.16.3"
|
||||
version = "0.16.4"
|
||||
description = "Build configuration for the PyO3 ecosystem"
|
||||
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
|
||||
keywords = ["pyo3", "python", "cpython", "ffi"]
|
||||
|
@ -26,6 +26,10 @@ default = []
|
|||
# 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"]
|
||||
|
|
|
@ -233,6 +233,13 @@ print("mingw", get_platform().startswith("mingw"))
|
|||
"#;
|
||||
let output = run_python_script(interpreter.as_ref(), SCRIPT)?;
|
||||
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 version = PythonVersion {
|
||||
|
@ -366,7 +373,7 @@ print("mingw", get_platform().startswith("mingw"))
|
|||
#[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()))
|
||||
.map(|buf| InterpreterConfig::from_reader(&*unescape(&buf)))
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
|
@ -464,11 +471,7 @@ print("mingw", get_platform().startswith("mingw"))
|
|||
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");
|
||||
}
|
||||
println!("cargo:PYO3_CONFIG={}", escape(&buf));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -682,14 +685,14 @@ pub fn is_extension_module() -> bool {
|
|||
|
||||
/// 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 {
|
||||
is_linking_libpython_for_target(&target_triple_from_env())
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
target.operating_system == OperatingSystem::Windows
|
||||
|| target.environment == Environment::Android
|
||||
|
@ -697,6 +700,18 @@ fn is_linking_libpython_for_target(target: &Triple) -> bool {
|
|||
|| !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.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// 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.
|
||||
#[allow(dead_code, unused_mut)]
|
||||
pub fn make_interpreter_config() -> Result<InterpreterConfig> {
|
||||
let host = Triple::host();
|
||||
let abi3_version = get_abi3_version();
|
||||
|
||||
if have_python_interpreter() {
|
||||
let mut interpreter_config = InterpreterConfig::from_interpreter(find_interpreter()?)?;
|
||||
interpreter_config.fixup_for_abi3_version(abi3_version)?;
|
||||
Ok(interpreter_config)
|
||||
} else if let Some(version) = abi3_version {
|
||||
let host = Triple::host();
|
||||
let mut interpreter_config = default_abi3_config(&host, version);
|
||||
// See if we can safely skip the Python interpreter configuration detection.
|
||||
// Unix "abi3" extension modules can usually be built without any interpreter.
|
||||
let need_interpreter = abi3_version.is_none() || require_libdir_for_target(&host);
|
||||
|
||||
// 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)?;
|
||||
if have_python_interpreter() {
|
||||
match get_host_interpreter(abi3_version) {
|
||||
Ok(interpreter_config) => return Ok(interpreter_config),
|
||||
// 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)
|
||||
} else {
|
||||
bail!("An abi3-py3* feature must be specified when compiling without a Python interpreter.")
|
||||
bytes.push(unhex(chunk[0]) << 4 | unhex(chunk[1]));
|
||||
}
|
||||
|
||||
bytes
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{io::Cursor, iter::FromIterator};
|
||||
use std::iter::FromIterator;
|
||||
use target_lexicon::triple;
|
||||
|
||||
use super::*;
|
||||
|
@ -1713,10 +1787,7 @@ mod tests {
|
|||
let mut buf: Vec<u8> = Vec::new();
|
||||
config.to_writer(&mut buf).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
config,
|
||||
InterpreterConfig::from_reader(Cursor::new(buf)).unwrap()
|
||||
);
|
||||
assert_eq!(config, InterpreterConfig::from_reader(&*buf).unwrap());
|
||||
|
||||
// And some different options, for variety
|
||||
|
||||
|
@ -1744,17 +1815,37 @@ mod tests {
|
|||
let mut buf: Vec<u8> = Vec::new();
|
||||
config.to_writer(&mut buf).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
config,
|
||||
InterpreterConfig::from_reader(Cursor::new(buf)).unwrap()
|
||||
);
|
||||
assert_eq!(config, InterpreterConfig::from_reader(&*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]
|
||||
fn test_config_file_defaults() {
|
||||
// Only version is required
|
||||
assert_eq!(
|
||||
InterpreterConfig::from_reader(Cursor::new("version=3.7")).unwrap(),
|
||||
InterpreterConfig::from_reader("version=3.7".as_bytes()).unwrap(),
|
||||
InterpreterConfig {
|
||||
version: PythonVersion { major: 3, minor: 7 },
|
||||
implementation: PythonImplementation::CPython,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "pyo3-ffi"
|
||||
version = "0.16.3"
|
||||
version = "0.16.4"
|
||||
description = "Python-API bindings for the PyO3 ecosystem"
|
||||
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
|
||||
keywords = ["pyo3", "python", "cpython", "ffi"]
|
||||
|
@ -35,4 +35,4 @@ abi3-py310 = ["abi3", "pyo3-build-config/abi3-py310"]
|
|||
|
||||
|
||||
[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]
|
||||
name = "pyo3-macros-backend"
|
||||
version = "0.16.3"
|
||||
version = "0.16.4"
|
||||
description = "Code generation for PyO3 package"
|
||||
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
|
||||
keywords = ["pyo3", "python", "cpython", "ffi"]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "pyo3-macros"
|
||||
version = "0.16.3"
|
||||
version = "0.16.4"
|
||||
description = "Proc macros for PyO3 package"
|
||||
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
|
||||
keywords = ["pyo3", "python", "cpython", "ffi"]
|
||||
|
@ -23,4 +23,4 @@ abi3 = ["pyo3-macros-backend/abi3"]
|
|||
proc-macro2 = { version = "1", default-features = false }
|
||||
quote = "1"
|
||||
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::types::{
|
||||
PyDate, PyDateAccess, PyDateTime, PyDelta, PyDeltaAccess, PyTime, PyTimeAccess, PyTuple,
|
||||
PyTzInfo,
|
||||
PyTzInfo, PyTzInfoAccess,
|
||||
};
|
||||
|
||||
#[pyfunction]
|
||||
|
@ -179,6 +179,16 @@ fn datetime_from_timestamp<'p>(
|
|||
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)]
|
||||
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!(get_datetime_tuple, 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
|
||||
#[cfg(not(PyPy))]
|
||||
|
|
|
@ -114,6 +114,7 @@ def test_time(args, kwargs):
|
|||
|
||||
assert act == exp
|
||||
assert act.tzinfo is exp.tzinfo
|
||||
assert rdt.get_time_tzinfo(act) == exp.tzinfo
|
||||
|
||||
|
||||
@given(t=st.times())
|
||||
|
@ -194,6 +195,7 @@ def test_datetime(args, kwargs):
|
|||
|
||||
assert act == exp
|
||||
assert act.tzinfo is exp.tzinfo
|
||||
assert rdt.get_datetime_tzinfo(act) == exp.tzinfo
|
||||
|
||||
|
||||
@given(dt=st.datetimes())
|
||||
|
|
|
@ -4,9 +4,8 @@
|
|||
//! documentation](https://docs.python.org/3/library/datetime.html)
|
||||
|
||||
use crate::err::PyResult;
|
||||
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))]
|
||||
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_SECOND,
|
||||
};
|
||||
use crate::instance::PyNativeType;
|
||||
use crate::types::PyTuple;
|
||||
use crate::{AsPyPointer, PyAny, PyObject, Python, ToPyObject};
|
||||
use std::os::raw::c_int;
|
||||
|
@ -160,6 +160,16 @@ pub trait PyTimeAccess {
|
|||
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`
|
||||
#[repr(transparent)]
|
||||
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`
|
||||
#[repr(transparent)]
|
||||
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`
|
||||
///
|
||||
/// This is an abstract base class and should not be constructed directly.
|
||||
|
@ -524,4 +560,33 @@ mod tests {
|
|||
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))]
|
||||
pub use self::datetime::{
|
||||
PyDate, PyDateAccess, PyDateTime, PyDelta, PyDeltaAccess, PyTime, PyTimeAccess, PyTzInfo,
|
||||
PyTzInfoAccess,
|
||||
};
|
||||
pub use self::dict::{IntoPyDict, PyDict};
|
||||
pub use self::floatob::PyFloat;
|
||||
|
|
Loading…
Reference in New Issue