move ffi module to separate crate
This commit is contained in:
parent
6b95118c23
commit
6a9a9ba38a
|
@ -19,7 +19,7 @@ Since implementing `PyClass` requires lots of boilerplate, we have a proc-macro
|
||||||
To summarize, there are six main parts to the PyO3 codebase.
|
To summarize, there are six main parts to the PyO3 codebase.
|
||||||
|
|
||||||
1. [Low-level bindings of Python/C API.](#1-low-level-bindings-of-python-capi)
|
1. [Low-level bindings of Python/C API.](#1-low-level-bindings-of-python-capi)
|
||||||
- [`src/ffi`]
|
- [`pyo3-ffi`] and [`src/ffi`]
|
||||||
2. [Bindings to Python objects.](#2-bindings-to-python-objects)
|
2. [Bindings to Python objects.](#2-bindings-to-python-objects)
|
||||||
- [`src/instance.rs`] and [`src/types`]
|
- [`src/instance.rs`] and [`src/types`]
|
||||||
3. [`PyClass` and related functionalities.](#3-pyclass-and-related-functionalities)
|
3. [`PyClass` and related functionalities.](#3-pyclass-and-related-functionalities)
|
||||||
|
@ -34,7 +34,7 @@ To summarize, there are six main parts to the PyO3 codebase.
|
||||||
|
|
||||||
## 1. Low-level bindings of Python/C API
|
## 1. Low-level bindings of Python/C API
|
||||||
|
|
||||||
[`src/ffi`] contains wrappers of [Python/C API].
|
[`pyo3-ffi`] contains wrappers of [Python/C API].
|
||||||
|
|
||||||
We aim to provide straight-forward Rust wrappers resembling the file structure of
|
We aim to provide straight-forward Rust wrappers resembling the file structure of
|
||||||
[`cpython/Include`](https://github.com/python/cpython/tree/v3.9.2/Include).
|
[`cpython/Include`](https://github.com/python/cpython/tree/v3.9.2/Include).
|
||||||
|
@ -43,7 +43,7 @@ However, we still lack some APIs and are continuously updating the module to mat
|
||||||
the file contents upstream in CPython.
|
the file contents upstream in CPython.
|
||||||
The tracking issue is [#1289](https://github.com/PyO3/pyo3/issues/1289), and contribution is welcome.
|
The tracking issue is [#1289](https://github.com/PyO3/pyo3/issues/1289), and contribution is welcome.
|
||||||
|
|
||||||
In the [`src/ffi`] module, there is lots of conditional compilation such as `#[cfg(Py_LIMITED_API)]`,
|
In the [`pyo3-ffi`] crate, there is lots of conditional compilation such as `#[cfg(Py_LIMITED_API)]`,
|
||||||
`#[cfg(Py_37)]`, and `#[cfg(PyPy)]`.
|
`#[cfg(Py_37)]`, and `#[cfg(PyPy)]`.
|
||||||
`Py_LIMITED_API` corresponds to `#define Py_LIMITED_API` macro in Python/C API.
|
`Py_LIMITED_API` corresponds to `#define Py_LIMITED_API` macro in Python/C API.
|
||||||
With `Py_LIMITED_API`, we can build a Python-version-agnostic binary called an
|
With `Py_LIMITED_API`, we can build a Python-version-agnostic binary called an
|
||||||
|
@ -208,6 +208,7 @@ Some of the functionality of `pyo3-build-config`:
|
||||||
[`pyo3-macros`]: https://github.com/PyO3/pyo3/tree/main/pyo3-macros
|
[`pyo3-macros`]: https://github.com/PyO3/pyo3/tree/main/pyo3-macros
|
||||||
[`pyo3-macros-backend`]: https://github.com/PyO3/pyo3/tree/main/pyo3-macros-backend
|
[`pyo3-macros-backend`]: https://github.com/PyO3/pyo3/tree/main/pyo3-macros-backend
|
||||||
[`pyo3-build-config`]: https://github.com/PyO3/pyo3/tree/main/pyo3-build-config
|
[`pyo3-build-config`]: https://github.com/PyO3/pyo3/tree/main/pyo3-build-config
|
||||||
|
[`pyo3-ffi`]: https://github.com/PyO3/pyo3/tree/main/pyo3-ffi
|
||||||
|
|
||||||
<!-- Directories -->
|
<!-- Directories -->
|
||||||
|
|
||||||
|
|
17
CHANGELOG.md
17
CHANGELOG.md
|
@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- `pyo3-build-config` no longer enables the `resolve-config` feature by default. [#2008](https://github.com/PyO3/pyo3/pull/2008)
|
- `pyo3-build-config` no longer enables the `resolve-config` feature by default. [#2008](https://github.com/PyO3/pyo3/pull/2008)
|
||||||
- Update `inventory` optional dependency to 0.2. [#2019](https://github.com/PyO3/pyo3/pull/2019)
|
- Update `inventory` optional dependency to 0.2. [#2019](https://github.com/PyO3/pyo3/pull/2019)
|
||||||
- Drop `paste` dependency. [#2081](https://github.com/PyO3/pyo3/pull/2081)
|
- Drop `paste` dependency. [#2081](https://github.com/PyO3/pyo3/pull/2081)
|
||||||
|
- The bindings found `pyo3::ffi` are now a re-export of the new `pyo3-ffi` crate. [#2126](https://github.com/PyO3/pyo3/pull/2126)
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
@ -50,6 +51,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- `#[pyclass]` type object creation [#2076](https://github.com/PyO3/pyo3/pull/2076) [#2081](https://github.com/PyO3/pyo3/pull/2081)
|
- `#[pyclass]` type object creation [#2076](https://github.com/PyO3/pyo3/pull/2076) [#2081](https://github.com/PyO3/pyo3/pull/2081)
|
||||||
- `__ipow__` now supports modulo argument on Python 3.8+. [#2083](https://github.com/PyO3/pyo3/pull/2083)
|
- `__ipow__` now supports modulo argument on Python 3.8+. [#2083](https://github.com/PyO3/pyo3/pull/2083)
|
||||||
- `pyo3-macros-backend` is now compiled with PyO3 cfgs to enable different magic method definitions based on version. [#2083](https://github.com/PyO3/pyo3/pull/2083)
|
- `pyo3-macros-backend` is now compiled with PyO3 cfgs to enable different magic method definitions based on version. [#2083](https://github.com/PyO3/pyo3/pull/2083)
|
||||||
|
- `_PyCFunctionFast` now correctly reflects the signature defined in the [Python docs](https://docs.python.org/3/c-api/structures.html#c._PyCFunctionFast). [#2126](https://github.com/PyO3/pyo3/pull/2126)
|
||||||
|
- `PyDateTimeAPI` and `PyDateTime_TimeZone_UTC` are are now unsafe functions instead of statics. [#2126](https://github.com/PyO3/pyo3/pull/2126)
|
||||||
|
- `PyDateTimeAPI` does not implicitly call `PyDateTime_IMPORT` anymore to reflect the original Python API more closely. Before the first call to `PyDateTime_IMPORT` a null pointer is returned. Therefore before calling any of the following FFI functions `PyDateTime_IMPORT` must be called to avoid undefined behaviour: [#2126](https://github.com/PyO3/pyo3/pull/2126)
|
||||||
|
- `PyDateTime_TimeZone_UTC`
|
||||||
|
- `PyDate_Check`
|
||||||
|
- `PyDate_CheckExact`
|
||||||
|
- `PyDateTime_Check`
|
||||||
|
- `PyDateTime_CheckExact`
|
||||||
|
- `PyTime_Check`
|
||||||
|
- `PyTime_CheckExact`
|
||||||
|
- `PyDelta_Check`
|
||||||
|
- `PyDelta_CheckExact`
|
||||||
|
- `PyTZInfo_Check`
|
||||||
|
- `PyTZInfo_CheckExact`
|
||||||
|
- `PyDateTime_FromTimestamp`
|
||||||
|
- `PyDate_FromTimestamp`
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
|
|
16
Cargo.toml
16
Cargo.toml
|
@ -19,6 +19,9 @@ cfg-if = "1.0"
|
||||||
libc = "0.2.62"
|
libc = "0.2.62"
|
||||||
parking_lot = "0.11.0"
|
parking_lot = "0.11.0"
|
||||||
|
|
||||||
|
# ffi bindings to the python interpreter, split into a seperate crate so they can be used independently
|
||||||
|
pyo3-ffi = { path = "pyo3-ffi", version = "=0.15.1" }
|
||||||
|
|
||||||
# support crates for macros feature
|
# support crates for macros feature
|
||||||
pyo3-macros = { path = "pyo3-macros", version = "=0.15.1", optional = true }
|
pyo3-macros = { path = "pyo3-macros", version = "=0.15.1", optional = true }
|
||||||
indoc = { version = "1.0.3", optional = true }
|
indoc = { version = "1.0.3", optional = true }
|
||||||
|
@ -60,16 +63,16 @@ multiple-pymethods = ["inventory", "pyo3-macros/multiple-pymethods"]
|
||||||
# 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 = []
|
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"]
|
abi3 = ["pyo3-build-config/abi3", "pyo3-ffi/abi3"]
|
||||||
|
|
||||||
# With abi3, we can manually set the minimum Python version.
|
# With abi3, we can manually set the minimum Python version.
|
||||||
abi3-py37 = ["abi3-py38", "pyo3-build-config/abi3-py37"]
|
abi3-py37 = ["abi3-py38", "pyo3-build-config/abi3-py37", "pyo3-ffi/abi3-py37"]
|
||||||
abi3-py38 = ["abi3-py39", "pyo3-build-config/abi3-py38"]
|
abi3-py38 = ["abi3-py39", "pyo3-build-config/abi3-py38", "pyo3-ffi/abi3-py38"]
|
||||||
abi3-py39 = ["abi3-py310", "pyo3-build-config/abi3-py39"]
|
abi3-py39 = ["abi3-py310", "pyo3-build-config/abi3-py39", "pyo3-ffi/abi3-py39"]
|
||||||
abi3-py310 = ["abi3", "pyo3-build-config/abi3-py310"]
|
abi3-py310 = ["abi3", "pyo3-build-config/abi3-py310", "pyo3-ffi/abi3-py310"]
|
||||||
|
|
||||||
# Changes `Python::with_gil` and `Python::acquire_gil` to automatically initialize the
|
# Changes `Python::with_gil` and `Python::acquire_gil` to automatically initialize the
|
||||||
# Python interpreter if needed.
|
# Python interpreter if needed.
|
||||||
|
@ -126,6 +129,7 @@ harness = false
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
|
"pyo3-ffi",
|
||||||
"pyo3-macros",
|
"pyo3-macros",
|
||||||
"pyo3-macros-backend",
|
"pyo3-macros-backend",
|
||||||
"pytests/pyo3-benchmarks",
|
"pytests/pyo3-benchmarks",
|
||||||
|
|
103
build.rs
103
build.rs
|
@ -1,48 +1,7 @@
|
||||||
use std::{env, process::Command};
|
use std::{env, process::Command};
|
||||||
|
|
||||||
use pyo3_build_config::{
|
use pyo3_build_config::pyo3_build_script_impl::{cargo_env_var, errors::Result};
|
||||||
bail, ensure,
|
use pyo3_build_config::{bail, InterpreterConfig};
|
||||||
pyo3_build_script_impl::{
|
|
||||||
cargo_env_var, env_var, errors::Result, resolve_interpreter_config, InterpreterConfig,
|
|
||||||
PythonVersion,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Minimum Python version PyO3 supports.
|
|
||||||
const MINIMUM_SUPPORTED_VERSION: PythonVersion = PythonVersion { major: 3, minor: 6 };
|
|
||||||
|
|
||||||
fn ensure_python_version(interpreter_config: &InterpreterConfig) -> Result<()> {
|
|
||||||
ensure!(
|
|
||||||
interpreter_config.version >= MINIMUM_SUPPORTED_VERSION,
|
|
||||||
"the configured Python interpreter version ({}) is lower than PyO3's minimum supported version ({})",
|
|
||||||
interpreter_config.version,
|
|
||||||
MINIMUM_SUPPORTED_VERSION,
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ensure_target_pointer_width(interpreter_config: &InterpreterConfig) -> Result<()> {
|
|
||||||
if let Some(pointer_width) = interpreter_config.pointer_width {
|
|
||||||
// Try to check whether the target architecture matches the python library
|
|
||||||
let rust_target = match cargo_env_var("CARGO_CFG_TARGET_POINTER_WIDTH")
|
|
||||||
.unwrap()
|
|
||||||
.as_str()
|
|
||||||
{
|
|
||||||
"64" => 64,
|
|
||||||
"32" => 32,
|
|
||||||
x => bail!("unexpected Rust target pointer width: {}", x),
|
|
||||||
};
|
|
||||||
|
|
||||||
ensure!(
|
|
||||||
rust_target == pointer_width,
|
|
||||||
"your Rust target architecture ({}-bit) does not match your python interpreter ({}-bit)",
|
|
||||||
rust_target,
|
|
||||||
pointer_width
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ensure_auto_initialize_ok(interpreter_config: &InterpreterConfig) -> Result<()> {
|
fn ensure_auto_initialize_ok(interpreter_config: &InterpreterConfig) -> Result<()> {
|
||||||
if cargo_env_var("CARGO_FEATURE_AUTO_INITIALIZE").is_some() {
|
if cargo_env_var("CARGO_FEATURE_AUTO_INITIALIZE").is_some() {
|
||||||
|
@ -84,36 +43,6 @@ fn rustc_minor_version() -> Option<u32> {
|
||||||
pieces.next()?.parse().ok()
|
pieces.next()?.parse().ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_link_config(interpreter_config: &InterpreterConfig) -> Result<()> {
|
|
||||||
let target_os = cargo_env_var("CARGO_CFG_TARGET_OS").unwrap();
|
|
||||||
let is_extension_module = cargo_env_var("CARGO_FEATURE_EXTENSION_MODULE").is_some();
|
|
||||||
if target_os == "windows" || target_os == "android" || !is_extension_module {
|
|
||||||
// windows and android - always link
|
|
||||||
// other systems - only link if not extension module
|
|
||||||
println!(
|
|
||||||
"cargo:rustc-link-lib={link_model}{alias}{lib_name}",
|
|
||||||
link_model = if interpreter_config.shared {
|
|
||||||
""
|
|
||||||
} else {
|
|
||||||
"static="
|
|
||||||
},
|
|
||||||
alias = if target_os == "windows" {
|
|
||||||
"pythonXY:"
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
},
|
|
||||||
lib_name = interpreter_config.lib_name.as_ref().ok_or(
|
|
||||||
"attempted to link to Python shared library but config does not contain lib_name"
|
|
||||||
)?,
|
|
||||||
);
|
|
||||||
if let Some(lib_dir) = &interpreter_config.lib_dir {
|
|
||||||
println!("cargo:rustc-link-search=native={}", lib_dir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Prepares the PyO3 crate for compilation.
|
/// Prepares the PyO3 crate for compilation.
|
||||||
///
|
///
|
||||||
/// This loads the config from pyo3-build-config and then makes some additional checks to improve UX
|
/// This loads the config from pyo3-build-config and then makes some additional checks to improve UX
|
||||||
|
@ -122,23 +51,12 @@ fn emit_link_config(interpreter_config: &InterpreterConfig) -> Result<()> {
|
||||||
/// Emits the cargo configuration based on this config as well as a few checks of the Rust compiler
|
/// Emits the cargo configuration based on this config as well as a few checks of the Rust compiler
|
||||||
/// version to enable features which aren't supported on MSRV.
|
/// version to enable features which aren't supported on MSRV.
|
||||||
fn configure_pyo3() -> Result<()> {
|
fn configure_pyo3() -> Result<()> {
|
||||||
let interpreter_config = resolve_interpreter_config()?;
|
let interpreter_config = pyo3_build_config::get();
|
||||||
|
|
||||||
if env_var("PYO3_PRINT_CONFIG").map_or(false, |os_str| os_str == "1") {
|
|
||||||
print_config_and_exit(&interpreter_config);
|
|
||||||
}
|
|
||||||
|
|
||||||
ensure_python_version(&interpreter_config)?;
|
|
||||||
ensure_target_pointer_width(&interpreter_config)?;
|
|
||||||
ensure_auto_initialize_ok(&interpreter_config)?;
|
|
||||||
|
|
||||||
if !interpreter_config.suppress_build_script_link_lines {
|
|
||||||
emit_link_config(&interpreter_config)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
interpreter_config.emit_pyo3_cfgs();
|
interpreter_config.emit_pyo3_cfgs();
|
||||||
|
|
||||||
let rustc_minor_version = rustc_minor_version().unwrap_or(0);
|
let rustc_minor_version = rustc_minor_version().unwrap_or(0);
|
||||||
|
ensure_auto_initialize_ok(interpreter_config)?;
|
||||||
|
|
||||||
// Enable use of const generics on Rust 1.51 and greater
|
// Enable use of const generics on Rust 1.51 and greater
|
||||||
if rustc_minor_version >= 51 {
|
if rustc_minor_version >= 51 {
|
||||||
|
@ -150,22 +68,9 @@ fn configure_pyo3() -> Result<()> {
|
||||||
println!("cargo:rustc-cfg=addr_of");
|
println!("cargo:rustc-cfg=addr_of");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extra lines come last, to support last write wins.
|
|
||||||
for line in &interpreter_config.extra_build_script_lines {
|
|
||||||
println!("{}", line);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_config_and_exit(config: &InterpreterConfig) {
|
|
||||||
println!("\n-- PYO3_PRINT_CONFIG=1 is set, printing configuration and halting compile --");
|
|
||||||
config
|
|
||||||
.to_writer(&mut std::io::stdout())
|
|
||||||
.expect("failed to print config to stdout");
|
|
||||||
std::process::exit(101);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
if let Err(e) = configure_pyo3() {
|
if let Err(e) = configure_pyo3() {
|
||||||
eprintln!("error: {}", e.report());
|
eprintln!("error: {}", e.report());
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
[package]
|
||||||
|
name = "pyo3-ffi"
|
||||||
|
version = "0.15.1"
|
||||||
|
description = "Python-API bindings 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 = "Apache-2.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libc = "0.2.62"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
|
||||||
|
default = []
|
||||||
|
|
||||||
|
# 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 = []
|
||||||
|
|
||||||
|
# Use the Python limited API. See https://www.python.org/dev/peps/pep-0384/ for more.
|
||||||
|
abi3 = ["pyo3-build-config/abi3"]
|
||||||
|
|
||||||
|
# With abi3, we can manually set the minimum Python version.
|
||||||
|
abi3-py37 = ["abi3-py38", "pyo3-build-config/abi3-py37"]
|
||||||
|
abi3-py38 = ["abi3-py39", "pyo3-build-config/abi3-py38"]
|
||||||
|
abi3-py39 = ["abi3-py310", "pyo3-build-config/abi3-py39"]
|
||||||
|
abi3-py310 = ["abi3", "pyo3-build-config/abi3-py310"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
pyo3-build-config = { path = "../pyo3-build-config", version = "0.15.1", features = ["resolve-config"] }
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,201 @@
|
||||||
|
# pyo3-ffi
|
||||||
|
|
||||||
|
This crate provides [Rust](https://www.rust-lang.org/) FFI declarations for Python 3.
|
||||||
|
It supports both the stable and the unstable component of the ABI through the use of cfg flags.
|
||||||
|
Python Versions 3.7+ are supported.
|
||||||
|
It is meant for advanced users only - regular PyO3 users shouldn't
|
||||||
|
need to interact with this crate at all.
|
||||||
|
|
||||||
|
The contents of this crate are not documented here, as it would entail
|
||||||
|
basically copying the documentation from CPython. Consult the [Python/C API Reference
|
||||||
|
Manual][capi] for up-to-date documentation.
|
||||||
|
|
||||||
|
# Minimum supported Rust and Python versions
|
||||||
|
|
||||||
|
PyO3 supports the following software versions:
|
||||||
|
- Python 3.7 and up (CPython and PyPy)
|
||||||
|
- Rust 1.48 and up
|
||||||
|
|
||||||
|
# Example: Building Python Native modules
|
||||||
|
|
||||||
|
PyO3 can be used to generate a native Python module. The easiest way to try this out for the
|
||||||
|
first time is to use [`maturin`]. `maturin` is a tool for building and publishing Rust-based
|
||||||
|
Python packages with minimal configuration. The following steps set up some files for an example
|
||||||
|
Python module, install `maturin`, and then show how to build and import the Python module.
|
||||||
|
|
||||||
|
First, create a new folder (let's call it `string_sum`) containing the following two files:
|
||||||
|
|
||||||
|
**`Cargo.toml`**
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[lib]
|
||||||
|
name = "string_sum"
|
||||||
|
# "cdylib" is necessary to produce a shared library for Python to import from.
|
||||||
|
#
|
||||||
|
# Downstream Rust code (including code in `bin/`, `examples/`, and `tests/`) will not be able
|
||||||
|
# to `use string_sum;` unless the "rlib" or "lib" crate type is also included, e.g.:
|
||||||
|
# crate-type = ["cdylib", "rlib"]
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
|
[dependencies.pyo3-ffi]
|
||||||
|
version = "*"
|
||||||
|
features = ["extension-module"]
|
||||||
|
```
|
||||||
|
|
||||||
|
**`src/lib.rs`**
|
||||||
|
```rust
|
||||||
|
use std::mem::transmute;
|
||||||
|
use std::os::raw::c_char;
|
||||||
|
|
||||||
|
use pyo3_ffi::*;
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PyInit_string_sum() -> *mut PyObject {
|
||||||
|
let init = PyModuleDef {
|
||||||
|
m_base: PyModuleDef_HEAD_INIT,
|
||||||
|
m_name: "string_sum\0".as_ptr() as *const c_char,
|
||||||
|
m_doc: std::ptr::null(),
|
||||||
|
m_size: 0,
|
||||||
|
m_methods: std::ptr::null_mut(),
|
||||||
|
m_slots: std::ptr::null_mut(),
|
||||||
|
m_traverse: None,
|
||||||
|
m_clear: None,
|
||||||
|
m_free: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mptr = PyModule_Create(Box::into_raw(Box::new(init)));
|
||||||
|
let version = env!("CARGO_PKG_VERSION");
|
||||||
|
PyModule_AddObject(
|
||||||
|
mptr,
|
||||||
|
"__version__\0".as_ptr() as *const c_char,
|
||||||
|
PyUnicode_FromStringAndSize(version.as_ptr() as *const c_char, version.len() as isize),
|
||||||
|
);
|
||||||
|
|
||||||
|
// It's necessary to transmute `sum_as_string` here because functions marked with `METH_FASTCALL`
|
||||||
|
// have a different signature. However the `PyMethodDef` struct currently represents all
|
||||||
|
// functions as a `PyCFunction`. The python interpreter will cast the function pointer back
|
||||||
|
// to `_PyCFunctionFast`.
|
||||||
|
let wrapped_sum_as_string = PyMethodDef {
|
||||||
|
ml_name: "sum_as_string\0".as_ptr() as *const c_char,
|
||||||
|
ml_meth: Some(transmute::<_PyCFunctionFast, PyCFunction>(sum_as_string)),
|
||||||
|
ml_flags: METH_FASTCALL,
|
||||||
|
ml_doc: "returns the sum of two integers as a string\0".as_ptr() as *const c_char,
|
||||||
|
};
|
||||||
|
|
||||||
|
// PyModule_AddObject can technically fail.
|
||||||
|
// For more involved applications error checking may be necessary
|
||||||
|
PyModule_AddObject(
|
||||||
|
mptr,
|
||||||
|
"sum_as_string\0".as_ptr() as *const c_char,
|
||||||
|
PyCFunction_NewEx(
|
||||||
|
Box::into_raw(Box::new(wrapped_sum_as_string)),
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
PyUnicode_InternFromString("string_sum\0".as_ptr() as *const c_char),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
let all = ["__all__\0", "__version__\0", "sum_as_string\0"];
|
||||||
|
|
||||||
|
let pyall = PyTuple_New(all.len() as isize);
|
||||||
|
for (i, obj) in all.iter().enumerate() {
|
||||||
|
PyTuple_SET_ITEM(
|
||||||
|
pyall,
|
||||||
|
i as isize,
|
||||||
|
PyUnicode_InternFromString(obj.as_ptr() as *const c_char),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
PyModule_AddObject(mptr, "__all__\0".as_ptr() as *const c_char, pyall);
|
||||||
|
|
||||||
|
mptr
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn sum_as_string(
|
||||||
|
_self: *mut PyObject,
|
||||||
|
args: *mut *mut PyObject,
|
||||||
|
nargs: Py_ssize_t,
|
||||||
|
) -> *mut PyObject {
|
||||||
|
if nargs != 2 {
|
||||||
|
return raise_type_error("sum_as_string() expected 2 positional arguments");
|
||||||
|
}
|
||||||
|
|
||||||
|
let arg1 = *args;
|
||||||
|
if PyLong_Check(arg1) == 0 {
|
||||||
|
return raise_type_error("sum_as_string() expected an int for positional argument 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
let arg1 = PyLong_AsLong(arg1);
|
||||||
|
if !PyErr_Occurred().is_null() {
|
||||||
|
return ptr::null()
|
||||||
|
}
|
||||||
|
|
||||||
|
let arg2 = *args.add(1);
|
||||||
|
if PyLong_Check(arg2) == 0 {
|
||||||
|
return raise_type_error("sum_as_string() expected an int for positional argument 2");
|
||||||
|
}
|
||||||
|
|
||||||
|
let arg2 = PyLong_AsLong(arg2);
|
||||||
|
if !PyErr_Occurred().is_null() {
|
||||||
|
return ptr::null()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let res = (arg1 + arg2).to_string();
|
||||||
|
PyUnicode_FromStringAndSize(res.as_ptr() as *const c_char, res.len() as isize)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cold]
|
||||||
|
#[inline(never)]
|
||||||
|
fn raise_type_error(msg: &str) -> *mut PyObject {
|
||||||
|
unsafe {
|
||||||
|
let err_msg =
|
||||||
|
PyUnicode_FromStringAndSize(msg.as_ptr() as *const c_char, msg.len() as isize);
|
||||||
|
PyErr_SetObject(PyExc_TypeError, err_msg);
|
||||||
|
Py_DECREF(err_msg);
|
||||||
|
};
|
||||||
|
std::ptr::null_mut()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
With those two files in place, now `maturin` needs to be installed. This can be done using
|
||||||
|
Python's package manager `pip`. First, load up a new Python `virtualenv`, and install `maturin`
|
||||||
|
into it:
|
||||||
|
```bash
|
||||||
|
$ cd string_sum
|
||||||
|
$ python -m venv .env
|
||||||
|
$ source .env/bin/activate
|
||||||
|
$ pip install maturin
|
||||||
|
```
|
||||||
|
|
||||||
|
Now build and execute the module:
|
||||||
|
```bash
|
||||||
|
$ maturin develop
|
||||||
|
# lots of progress output as maturin runs the compilation...
|
||||||
|
$ python
|
||||||
|
>>> import string_sum
|
||||||
|
>>> string_sum.sum_as_string(5, 20)
|
||||||
|
'25'
|
||||||
|
```
|
||||||
|
|
||||||
|
As well as with `maturin`, it is possible to build using [setuptools-rust] or
|
||||||
|
[manually][manual_builds]. Both offer more flexibility than `maturin` but require further
|
||||||
|
configuration.
|
||||||
|
|
||||||
|
|
||||||
|
While most projects use the safe wrapper provided by PyO3,
|
||||||
|
you can take a look at the [`orjson`] library as an example on how to use `pyo3-ffi` directly.
|
||||||
|
For those well versed in C and Rust the [tutorials] from the CPython documentation
|
||||||
|
can be easily converted to rust as well.
|
||||||
|
|
||||||
|
[tutorials]: https://docs.python.org/3/extending/
|
||||||
|
[`orjson`]: https://github.com/ijl/orjson
|
||||||
|
[capi]: https://docs.python.org/3/c-api/index.html
|
||||||
|
[`maturin`]: https://github.com/PyO3/maturin "Build and publish crates with pyo3, rust-cpython and cffi bindings as well as rust binaries as python packages"
|
||||||
|
[`pyo3-build-config`]: https://docs.rs/pyo3-build-config
|
||||||
|
[feature flags]: https://doc.rust-lang.org/cargo/reference/features.html "Features - The Cargo Book"
|
||||||
|
[manual_builds]: https://pyo3.rs/latest/building_and_distribution.html#manual-builds "Manual builds - Building and Distribution - PyO3 user guide"
|
||||||
|
[setuptools-rust]: https://github.com/PyO3/setuptools-rust "Setuptools plugin for Rust extensions"
|
||||||
|
[PEP 384]: https://www.python.org/dev/peps/pep-0384 "PEP 384 -- Defining a Stable ABI"
|
||||||
|
[Features chapter of the guide]: https://pyo3.rs/latest/features.html#features-reference "Features Reference - PyO3 user guide"
|
|
@ -0,0 +1,119 @@
|
||||||
|
use pyo3_build_config::{
|
||||||
|
bail, ensure,
|
||||||
|
pyo3_build_script_impl::{
|
||||||
|
cargo_env_var, env_var, errors::Result, resolve_interpreter_config, InterpreterConfig,
|
||||||
|
PythonVersion,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Minimum Python version PyO3 supports.
|
||||||
|
const MINIMUM_SUPPORTED_VERSION: PythonVersion = PythonVersion { major: 3, minor: 7 };
|
||||||
|
|
||||||
|
fn ensure_python_version(interpreter_config: &InterpreterConfig) -> Result<()> {
|
||||||
|
ensure!(
|
||||||
|
interpreter_config.version >= MINIMUM_SUPPORTED_VERSION,
|
||||||
|
"the configured Python interpreter version ({}) is lower than PyO3's minimum supported version ({})",
|
||||||
|
interpreter_config.version,
|
||||||
|
MINIMUM_SUPPORTED_VERSION,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ensure_target_pointer_width(interpreter_config: &InterpreterConfig) -> Result<()> {
|
||||||
|
if let Some(pointer_width) = interpreter_config.pointer_width {
|
||||||
|
// Try to check whether the target architecture matches the python library
|
||||||
|
let rust_target = match cargo_env_var("CARGO_CFG_TARGET_POINTER_WIDTH")
|
||||||
|
.unwrap()
|
||||||
|
.as_str()
|
||||||
|
{
|
||||||
|
"64" => 64,
|
||||||
|
"32" => 32,
|
||||||
|
x => bail!("unexpected Rust target pointer width: {}", x),
|
||||||
|
};
|
||||||
|
|
||||||
|
ensure!(
|
||||||
|
rust_target == pointer_width,
|
||||||
|
"your Rust target architecture ({}-bit) does not match your python interpreter ({}-bit)",
|
||||||
|
rust_target,
|
||||||
|
pointer_width
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_link_config(interpreter_config: &InterpreterConfig) -> Result<()> {
|
||||||
|
let target_os = cargo_env_var("CARGO_CFG_TARGET_OS").unwrap();
|
||||||
|
let is_extension_module = cargo_env_var("CARGO_FEATURE_EXTENSION_MODULE").is_some();
|
||||||
|
if target_os == "windows" || target_os == "android" || !is_extension_module {
|
||||||
|
// windows and android - always link
|
||||||
|
// other systems - only link if not extension module
|
||||||
|
println!(
|
||||||
|
"cargo:rustc-link-lib={link_model}{alias}{lib_name}",
|
||||||
|
link_model = if interpreter_config.shared {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
"static="
|
||||||
|
},
|
||||||
|
alias = if target_os == "windows" {
|
||||||
|
"pythonXY:"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
},
|
||||||
|
lib_name = interpreter_config.lib_name.as_ref().ok_or(
|
||||||
|
"attempted to link to Python shared library but config does not contain lib_name"
|
||||||
|
)?,
|
||||||
|
);
|
||||||
|
if let Some(lib_dir) = &interpreter_config.lib_dir {
|
||||||
|
println!("cargo:rustc-link-search=native={}", lib_dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prepares the PyO3 crate for compilation.
|
||||||
|
///
|
||||||
|
/// This loads the config from pyo3-build-config and then makes some additional checks to improve UX
|
||||||
|
/// for users.
|
||||||
|
///
|
||||||
|
/// Emits the cargo configuration based on this config as well as a few checks of the Rust compiler
|
||||||
|
/// version to enable features which aren't supported on MSRV.
|
||||||
|
fn configure_pyo3() -> Result<()> {
|
||||||
|
let interpreter_config = resolve_interpreter_config()?;
|
||||||
|
|
||||||
|
if env_var("PYO3_PRINT_CONFIG").map_or(false, |os_str| os_str == "1") {
|
||||||
|
print_config_and_exit(&interpreter_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_python_version(&interpreter_config)?;
|
||||||
|
ensure_target_pointer_width(&interpreter_config)?;
|
||||||
|
|
||||||
|
if !interpreter_config.suppress_build_script_link_lines {
|
||||||
|
emit_link_config(&interpreter_config)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
interpreter_config.emit_pyo3_cfgs();
|
||||||
|
|
||||||
|
// Extra lines come last, to support last write wins.
|
||||||
|
for line in &interpreter_config.extra_build_script_lines {
|
||||||
|
println!("{}", line);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_config_and_exit(config: &InterpreterConfig) {
|
||||||
|
println!("\n-- PYO3_PRINT_CONFIG=1 is set, printing configuration and halting compile --");
|
||||||
|
config
|
||||||
|
.to_writer(&mut std::io::stdout())
|
||||||
|
.expect("failed to print config to stdout");
|
||||||
|
std::process::exit(101);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if let Err(e) = configure_pyo3() {
|
||||||
|
eprintln!("error: {}", e.report());
|
||||||
|
std::process::exit(1)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
use crate::ffi::pyport::Py_ssize_t;
|
use crate::pyport::Py_ssize_t;
|
||||||
use std::os::raw::{c_char, c_int};
|
use std::os::raw::{c_char, c_int};
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
|
@ -96,10 +96,9 @@ extern "C" {
|
||||||
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn PyIter_Check(o: *mut PyObject) -> c_int {
|
pub unsafe fn PyIter_Check(o: *mut PyObject) -> c_int {
|
||||||
(match (*crate::ffi::Py_TYPE(o)).tp_iternext {
|
(match (*crate::Py_TYPE(o)).tp_iternext {
|
||||||
Some(tp_iternext) => {
|
Some(tp_iternext) => {
|
||||||
tp_iternext as *const std::os::raw::c_void
|
tp_iternext as *const std::os::raw::c_void != crate::_PyObject_NextNotImplemented as _
|
||||||
!= crate::ffi::_PyObject_NextNotImplemented as _
|
|
||||||
}
|
}
|
||||||
None => false,
|
None => false,
|
||||||
}) as c_int
|
}) as c_int
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::ffi::object::PyTypeObject;
|
use crate::object::PyTypeObject;
|
||||||
|
|
||||||
#[cfg_attr(windows, link(name = "pythonXY"))]
|
#[cfg_attr(windows, link(name = "pythonXY"))]
|
||||||
extern "C" {
|
extern "C" {
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::ffi::longobject::PyLongObject;
|
use crate::longobject::PyLongObject;
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
use std::os::raw::{c_int, c_long};
|
use std::os::raw::{c_int, c_long};
|
||||||
|
|
||||||
#[cfg_attr(windows, link(name = "pythonXY"))]
|
#[cfg_attr(windows, link(name = "pythonXY"))]
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
use crate::ffi::pyport::Py_ssize_t;
|
use crate::pyport::Py_ssize_t;
|
||||||
use std::os::raw::{c_char, c_int};
|
use std::os::raw::{c_char, c_int};
|
||||||
|
|
||||||
#[cfg_attr(windows, link(name = "pythonXY"))]
|
#[cfg_attr(windows, link(name = "pythonXY"))]
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
use crate::ffi::pyport::Py_ssize_t;
|
use crate::pyport::Py_ssize_t;
|
||||||
use std::os::raw::{c_char, c_int};
|
use std::os::raw::{c_char, c_int};
|
||||||
|
|
||||||
#[cfg_attr(windows, link(name = "pythonXY"))]
|
#[cfg_attr(windows, link(name = "pythonXY"))]
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::ffi::object::PyObject;
|
use crate::object::PyObject;
|
||||||
use crate::ffi::pystate::PyThreadState;
|
use crate::pystate::PyThreadState;
|
||||||
use std::os::raw::{c_char, c_int, c_void};
|
use std::os::raw::{c_char, c_int, c_void};
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -37,7 +37,7 @@ extern "C" {
|
||||||
pub fn PyEval_GetGlobals() -> *mut PyObject;
|
pub fn PyEval_GetGlobals() -> *mut PyObject;
|
||||||
#[cfg_attr(PyPy, link_name = "PyPyEval_GetLocals")]
|
#[cfg_attr(PyPy, link_name = "PyPyEval_GetLocals")]
|
||||||
pub fn PyEval_GetLocals() -> *mut PyObject;
|
pub fn PyEval_GetLocals() -> *mut PyObject;
|
||||||
pub fn PyEval_GetFrame() -> *mut crate::ffi::PyFrameObject;
|
pub fn PyEval_GetFrame() -> *mut crate::PyFrameObject;
|
||||||
#[cfg_attr(PyPy, link_name = "PyPy_AddPendingCall")]
|
#[cfg_attr(PyPy, link_name = "PyPy_AddPendingCall")]
|
||||||
pub fn Py_AddPendingCall(
|
pub fn Py_AddPendingCall(
|
||||||
func: Option<extern "C" fn(arg1: *mut c_void) -> c_int>,
|
func: Option<extern "C" fn(arg1: *mut c_void) -> c_int>,
|
||||||
|
@ -59,8 +59,8 @@ extern "C" {
|
||||||
pub fn PyEval_GetFuncName(arg1: *mut PyObject) -> *const c_char;
|
pub fn PyEval_GetFuncName(arg1: *mut PyObject) -> *const c_char;
|
||||||
pub fn PyEval_GetFuncDesc(arg1: *mut PyObject) -> *const c_char;
|
pub fn PyEval_GetFuncDesc(arg1: *mut PyObject) -> *const c_char;
|
||||||
pub fn PyEval_GetCallStats(arg1: *mut PyObject) -> *mut PyObject;
|
pub fn PyEval_GetCallStats(arg1: *mut PyObject) -> *mut PyObject;
|
||||||
pub fn PyEval_EvalFrame(arg1: *mut crate::ffi::PyFrameObject) -> *mut PyObject;
|
pub fn PyEval_EvalFrame(arg1: *mut crate::PyFrameObject) -> *mut PyObject;
|
||||||
pub fn PyEval_EvalFrameEx(f: *mut crate::ffi::PyFrameObject, exc: c_int) -> *mut PyObject;
|
pub fn PyEval_EvalFrameEx(f: *mut crate::PyFrameObject, exc: c_int) -> *mut PyObject;
|
||||||
#[cfg_attr(PyPy, link_name = "PyPyEval_SaveThread")]
|
#[cfg_attr(PyPy, link_name = "PyPyEval_SaveThread")]
|
||||||
pub fn PyEval_SaveThread() -> *mut PyThreadState;
|
pub fn PyEval_SaveThread() -> *mut PyThreadState;
|
||||||
#[cfg_attr(PyPy, link_name = "PyPyEval_RestoreThread")]
|
#[cfg_attr(PyPy, link_name = "PyPyEval_RestoreThread")]
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::ffi::object::PyObject;
|
use crate::object::PyObject;
|
||||||
use std::os::raw::{c_char, c_int};
|
use std::os::raw::{c_char, c_int};
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
use std::os::raw::{c_double, c_int};
|
use std::os::raw::{c_double, c_int};
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::ffi::object::{PyObject, PyTypeObject, Py_TYPE};
|
use crate::object::{PyObject, PyTypeObject, Py_TYPE};
|
||||||
use std::os::raw::{c_char, c_int};
|
use std::os::raw::{c_char, c_int};
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::ffi::{PyObject, Py_buffer, Py_ssize_t};
|
use crate::{PyObject, Py_buffer, Py_ssize_t};
|
||||||
use std::os::raw::{c_char, c_int, c_void};
|
use std::os::raw::{c_char, c_int, c_void};
|
||||||
|
|
||||||
#[cfg(all(Py_3_8, not(PyPy)))]
|
#[cfg(all(Py_3_8, not(PyPy)))]
|
||||||
use crate::ffi::{
|
use crate::{
|
||||||
pyport::PY_SSIZE_T_MAX, vectorcallfunc, PyCallable_Check, PyThreadState, PyThreadState_GET,
|
pyport::PY_SSIZE_T_MAX, vectorcallfunc, PyCallable_Check, PyThreadState, PyThreadState_GET,
|
||||||
PyTuple_Check, PyType_HasFeature, Py_TPFLAGS_HAVE_VECTORCALL,
|
PyTuple_Check, PyType_HasFeature, Py_TPFLAGS_HAVE_VECTORCALL,
|
||||||
};
|
};
|
||||||
|
@ -51,7 +51,7 @@ pub unsafe fn PyVectorcall_NARGS(n: size_t) -> Py_ssize_t {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn PyVectorcall_Function(callable: *mut PyObject) -> Option<vectorcallfunc> {
|
pub unsafe fn PyVectorcall_Function(callable: *mut PyObject) -> Option<vectorcallfunc> {
|
||||||
assert!(!callable.is_null());
|
assert!(!callable.is_null());
|
||||||
let tp = crate::ffi::Py_TYPE(callable);
|
let tp = crate::Py_TYPE(callable);
|
||||||
if PyType_HasFeature(tp, Py_TPFLAGS_HAVE_VECTORCALL) == 0 {
|
if PyType_HasFeature(tp, Py_TPFLAGS_HAVE_VECTORCALL) == 0 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -218,7 +218,7 @@ extern "C" {
|
||||||
#[cfg(not(any(Py_3_9, PyPy)))]
|
#[cfg(not(any(Py_3_9, PyPy)))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn PyObject_CheckBuffer(o: *mut PyObject) -> c_int {
|
pub unsafe fn PyObject_CheckBuffer(o: *mut PyObject) -> c_int {
|
||||||
let tp_as_buffer = (*crate::ffi::Py_TYPE(o)).tp_as_buffer;
|
let tp_as_buffer = (*crate::Py_TYPE(o)).tp_as_buffer;
|
||||||
(!tp_as_buffer.is_null() && (*tp_as_buffer).bf_getbuffer.is_some()) as c_int
|
(!tp_as_buffer.is_null() && (*tp_as_buffer).bf_getbuffer.is_some()) as c_int
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::ffi::object::PyObject;
|
use crate::object::PyObject;
|
||||||
use crate::ffi::pyport::Py_ssize_t;
|
use crate::pyport::Py_ssize_t;
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
|
@ -1,12 +1,9 @@
|
||||||
use crate::ffi::cpython::pystate::Py_tracefunc;
|
use crate::cpython::pystate::Py_tracefunc;
|
||||||
use crate::ffi::object::{freefunc, PyObject};
|
use crate::object::{freefunc, PyObject};
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn _PyEval_EvalFrameDefault(
|
pub fn _PyEval_EvalFrameDefault(arg1: *mut crate::PyFrameObject, exc: c_int) -> *mut PyObject;
|
||||||
arg1: *mut crate::ffi::PyFrameObject,
|
|
||||||
exc: c_int,
|
|
||||||
) -> *mut PyObject;
|
|
||||||
pub fn _PyEval_RequestCodeExtraIndex(func: freefunc) -> c_int;
|
pub fn _PyEval_RequestCodeExtraIndex(func: freefunc) -> c_int;
|
||||||
pub fn PyEval_SetProfile(trace_func: Option<Py_tracefunc>, arg1: *mut PyObject);
|
pub fn PyEval_SetProfile(trace_func: Option<Py_tracefunc>, arg1: *mut PyObject);
|
||||||
pub fn PyEval_SetTrace(trace_func: Option<Py_tracefunc>, arg1: *mut PyObject);
|
pub fn PyEval_SetTrace(trace_func: Option<Py_tracefunc>, arg1: *mut PyObject);
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
use crate::ffi::pyport::Py_ssize_t;
|
use crate::pyport::Py_ssize_t;
|
||||||
use std::os::raw::{c_char, c_int, c_uchar, c_void};
|
use std::os::raw::{c_char, c_int, c_uchar, c_void};
|
||||||
|
|
||||||
// skipped _Py_CODEUNIT
|
// skipped _Py_CODEUNIT
|
||||||
|
@ -90,7 +90,7 @@ pub unsafe fn PyCode_Check(op: *mut PyObject) -> c_int {
|
||||||
#[inline]
|
#[inline]
|
||||||
#[cfg(not(PyPy))]
|
#[cfg(not(PyPy))]
|
||||||
pub unsafe fn PyCode_GetNumFree(op: *mut PyCodeObject) -> Py_ssize_t {
|
pub unsafe fn PyCode_GetNumFree(op: *mut PyCodeObject) -> Py_ssize_t {
|
||||||
crate::ffi::PyTuple_GET_SIZE((*op).co_freevars)
|
crate::PyTuple_GET_SIZE((*op).co_freevars)
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
|
@ -1,11 +1,11 @@
|
||||||
#[cfg(not(Py_3_10))]
|
#[cfg(not(Py_3_10))]
|
||||||
use crate::ffi::object::PyObject;
|
use crate::object::PyObject;
|
||||||
#[cfg(not(Py_3_10))]
|
#[cfg(not(Py_3_10))]
|
||||||
use crate::ffi::pyarena::*;
|
use crate::pyarena::*;
|
||||||
#[cfg(not(Py_3_10))]
|
#[cfg(not(Py_3_10))]
|
||||||
use crate::ffi::pythonrun::*;
|
use crate::pythonrun::*;
|
||||||
#[cfg(not(Py_3_10))]
|
#[cfg(not(Py_3_10))]
|
||||||
use crate::ffi::PyCodeObject;
|
use crate::PyCodeObject;
|
||||||
#[cfg(not(Py_3_10))]
|
#[cfg(not(Py_3_10))]
|
||||||
use std::os::raw::c_char;
|
use std::os::raw::c_char;
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
use crate::ffi::pyport::Py_ssize_t;
|
use crate::pyport::Py_ssize_t;
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
|
||||||
opaque_struct!(PyDictKeysObject);
|
opaque_struct!(PyDictKeysObject);
|
||||||
|
@ -24,7 +24,7 @@ extern "C" {
|
||||||
mp: *mut PyObject,
|
mp: *mut PyObject,
|
||||||
key: *mut PyObject,
|
key: *mut PyObject,
|
||||||
item: *mut PyObject,
|
item: *mut PyObject,
|
||||||
hash: crate::ffi::Py_hash_t,
|
hash: crate::Py_hash_t,
|
||||||
) -> c_int;
|
) -> c_int;
|
||||||
// skipped _PyDict_DelItem_KnownHash
|
// skipped _PyDict_DelItem_KnownHash
|
||||||
// skipped _PyDict_DelItemIf
|
// skipped _PyDict_DelItemIf
|
||||||
|
@ -34,7 +34,7 @@ extern "C" {
|
||||||
pos: *mut Py_ssize_t,
|
pos: *mut Py_ssize_t,
|
||||||
key: *mut *mut PyObject,
|
key: *mut *mut PyObject,
|
||||||
value: *mut *mut PyObject,
|
value: *mut *mut PyObject,
|
||||||
hash: *mut crate::ffi::Py_hash_t,
|
hash: *mut crate::Py_hash_t,
|
||||||
) -> c_int;
|
) -> c_int;
|
||||||
// skipped PyDict_GET_SIZE
|
// skipped PyDict_GET_SIZE
|
||||||
// skipped _PyDict_Contains_KnownHash
|
// skipped _PyDict_Contains_KnownHash
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::ffi::cpython::code::{PyCodeObject, CO_MAXBLOCKS};
|
use crate::cpython::code::{PyCodeObject, CO_MAXBLOCKS};
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
use crate::ffi::pystate::PyThreadState;
|
use crate::pystate::PyThreadState;
|
||||||
use std::os::raw::{c_char, c_int};
|
use std::os::raw::{c_char, c_int};
|
||||||
|
|
||||||
// skipped _framestate
|
// skipped _framestate
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::ffi::{PyInterpreterState, PyObject};
|
use crate::{PyInterpreterState, PyObject};
|
||||||
use std::os::raw::{c_char, c_int, c_uchar};
|
use std::os::raw::{c_char, c_int, c_uchar};
|
||||||
|
|
||||||
// skipped PyInit__imp
|
// skipped PyInit__imp
|
|
@ -1,6 +1,6 @@
|
||||||
/* --- PyStatus ----------------------------------------------- */
|
/* --- PyStatus ----------------------------------------------- */
|
||||||
|
|
||||||
use crate::ffi::Py_ssize_t;
|
use crate::Py_ssize_t;
|
||||||
use libc::wchar_t;
|
use libc::wchar_t;
|
||||||
use std::os::raw::{c_char, c_int, c_ulong};
|
use std::os::raw::{c_char, c_int, c_ulong};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
use crate::ffi::pyport::Py_ssize_t;
|
use crate::pyport::Py_ssize_t;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::ffi::PyObject;
|
use crate::PyObject;
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
|
||||||
// skipped _Py_NewReference
|
// skipped _Py_NewReference
|
||||||
|
@ -12,7 +12,7 @@ use std::os::raw::c_int;
|
||||||
// skipped _Py_IDENTIFIER
|
// skipped _Py_IDENTIFIER
|
||||||
|
|
||||||
mod bufferinfo {
|
mod bufferinfo {
|
||||||
use crate::ffi::Py_ssize_t;
|
use crate::Py_ssize_t;
|
||||||
use std::os::raw::{c_char, c_int, c_void};
|
use std::os::raw::{c_char, c_int, c_void};
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ mod bufferinfo {
|
||||||
pub struct Py_buffer {
|
pub struct Py_buffer {
|
||||||
pub buf: *mut c_void,
|
pub buf: *mut c_void,
|
||||||
/// Owned reference
|
/// Owned reference
|
||||||
pub obj: *mut crate::ffi::PyObject,
|
pub obj: *mut crate::PyObject,
|
||||||
pub len: Py_ssize_t,
|
pub len: Py_ssize_t,
|
||||||
pub itemsize: Py_ssize_t,
|
pub itemsize: Py_ssize_t,
|
||||||
pub readonly: c_int,
|
pub readonly: c_int,
|
||||||
|
@ -67,12 +67,12 @@ mod bufferinfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type getbufferproc = unsafe extern "C" fn(
|
pub type getbufferproc = unsafe extern "C" fn(
|
||||||
arg1: *mut crate::ffi::PyObject,
|
arg1: *mut crate::PyObject,
|
||||||
arg2: *mut Py_buffer,
|
arg2: *mut Py_buffer,
|
||||||
arg3: c_int,
|
arg3: c_int,
|
||||||
) -> c_int;
|
) -> c_int;
|
||||||
pub type releasebufferproc =
|
pub type releasebufferproc =
|
||||||
unsafe extern "C" fn(arg1: *mut crate::ffi::PyObject, arg2: *mut Py_buffer);
|
unsafe extern "C" fn(arg1: *mut crate::PyObject, arg2: *mut Py_buffer);
|
||||||
|
|
||||||
/// Maximum number of dimensions
|
/// Maximum number of dimensions
|
||||||
pub const PyBUF_MAX_NDIM: c_int = 64;
|
pub const PyBUF_MAX_NDIM: c_int = 64;
|
||||||
|
@ -117,8 +117,8 @@ pub type vectorcallfunc = unsafe extern "C" fn(
|
||||||
) -> *mut PyObject;
|
) -> *mut PyObject;
|
||||||
|
|
||||||
mod typeobject {
|
mod typeobject {
|
||||||
use crate::ffi::{self, object};
|
use crate::object;
|
||||||
use crate::ffi::{PyObject, Py_ssize_t};
|
use crate::{PyObject, Py_ssize_t};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::os::raw::{c_char, c_int, c_uint, c_ulong, c_void};
|
use std::os::raw::{c_char, c_int, c_uint, c_ulong, c_void};
|
||||||
|
|
||||||
|
@ -248,9 +248,9 @@ mod typeobject {
|
||||||
pub tp_weaklistoffset: Py_ssize_t,
|
pub tp_weaklistoffset: Py_ssize_t,
|
||||||
pub tp_iter: Option<object::getiterfunc>,
|
pub tp_iter: Option<object::getiterfunc>,
|
||||||
pub tp_iternext: Option<object::iternextfunc>,
|
pub tp_iternext: Option<object::iternextfunc>,
|
||||||
pub tp_methods: *mut ffi::methodobject::PyMethodDef,
|
pub tp_methods: *mut crate::methodobject::PyMethodDef,
|
||||||
pub tp_members: *mut ffi::structmember::PyMemberDef,
|
pub tp_members: *mut crate::structmember::PyMemberDef,
|
||||||
pub tp_getset: *mut ffi::descrobject::PyGetSetDef,
|
pub tp_getset: *mut crate::descrobject::PyGetSetDef,
|
||||||
pub tp_base: *mut PyTypeObject,
|
pub tp_base: *mut PyTypeObject,
|
||||||
pub tp_dict: *mut object::PyObject,
|
pub tp_dict: *mut object::PyObject,
|
||||||
pub tp_descr_get: Option<object::descrgetfunc>,
|
pub tp_descr_get: Option<object::descrgetfunc>,
|
||||||
|
@ -310,10 +310,10 @@ mod typeobject {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn PyHeapType_GET_MEMBERS(
|
pub unsafe fn PyHeapType_GET_MEMBERS(
|
||||||
etype: *mut PyHeapTypeObject,
|
etype: *mut PyHeapTypeObject,
|
||||||
) -> *mut ffi::structmember::PyMemberDef {
|
) -> *mut crate::structmember::PyMemberDef {
|
||||||
let py_type = object::Py_TYPE(etype as *mut object::PyObject);
|
let py_type = object::Py_TYPE(etype as *mut object::PyObject);
|
||||||
let ptr = etype.offset((*py_type).tp_basicsize);
|
let ptr = etype.offset((*py_type).tp_basicsize);
|
||||||
ptr as *mut ffi::structmember::PyMemberDef
|
ptr as *mut crate::structmember::PyMemberDef
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::ffi::{PyConfig, PyPreConfig, PyStatus, Py_ssize_t};
|
use crate::{PyConfig, PyPreConfig, PyStatus, Py_ssize_t};
|
||||||
use libc::wchar_t;
|
use libc::wchar_t;
|
||||||
use std::os::raw::{c_char, c_int};
|
use std::os::raw::{c_char, c_int};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#[cfg(not(PyPy))]
|
#[cfg(not(PyPy))]
|
||||||
use crate::ffi::PyThreadState;
|
use crate::PyThreadState;
|
||||||
use crate::ffi::{PyFrameObject, PyInterpreterState, PyObject};
|
use crate::{PyFrameObject, PyInterpreterState, PyObject};
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
|
||||||
// skipped _PyInterpreterState_RequiresIDRef
|
// skipped _PyInterpreterState_RequiresIDRef
|
||||||
|
@ -60,10 +60,10 @@ extern "C" {
|
||||||
|
|
||||||
#[cfg(Py_3_9)]
|
#[cfg(Py_3_9)]
|
||||||
pub type _PyFrameEvalFunction = extern "C" fn(
|
pub type _PyFrameEvalFunction = extern "C" fn(
|
||||||
*mut crate::ffi::PyThreadState,
|
*mut crate::PyThreadState,
|
||||||
*mut crate::ffi::PyFrameObject,
|
*mut crate::PyFrameObject,
|
||||||
c_int,
|
c_int,
|
||||||
) -> *mut crate::ffi::object::PyObject;
|
) -> *mut crate::object::PyObject;
|
||||||
|
|
||||||
#[cfg(Py_3_9)]
|
#[cfg(Py_3_9)]
|
||||||
extern "C" {
|
extern "C" {
|
|
@ -1,9 +1,9 @@
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
#[cfg(all(not(Py_LIMITED_API), not(Py_3_10)))]
|
#[cfg(all(not(Py_LIMITED_API), not(Py_3_10)))]
|
||||||
use crate::ffi::pyarena::PyArena;
|
use crate::pyarena::PyArena;
|
||||||
use crate::ffi::PyCompilerFlags;
|
use crate::PyCompilerFlags;
|
||||||
#[cfg(not(Py_3_10))]
|
#[cfg(not(Py_3_10))]
|
||||||
use crate::ffi::{_mod, _node};
|
use crate::{_mod, _node};
|
||||||
use libc::FILE;
|
use libc::FILE;
|
||||||
use std::os::raw::{c_char, c_int};
|
use std::os::raw::{c_char, c_int};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
#[cfg(not(PyPy))]
|
#[cfg(not(PyPy))]
|
||||||
use crate::ffi::pyport::Py_ssize_t;
|
use crate::pyport::Py_ssize_t;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct PyTupleObject {
|
pub struct PyTupleObject {
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::ffi::{PyObject, Py_UCS1, Py_UCS2, Py_UCS4, Py_UNICODE, Py_hash_t, Py_ssize_t};
|
use crate::{PyObject, Py_UCS1, Py_UCS2, Py_UCS4, Py_UNICODE, Py_hash_t, Py_ssize_t};
|
||||||
use libc::wchar_t;
|
use libc::wchar_t;
|
||||||
use std::os::raw::{c_char, c_int, c_uint, c_void};
|
use std::os::raw::{c_char, c_int, c_uint, c_void};
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@ pub const SSTATE_INTERNED_IMMORTAL: c_uint = 2;
|
||||||
#[inline]
|
#[inline]
|
||||||
#[cfg(target_endian = "little")]
|
#[cfg(target_endian = "little")]
|
||||||
pub unsafe fn PyUnicode_IS_ASCII(op: *mut PyObject) -> c_uint {
|
pub unsafe fn PyUnicode_IS_ASCII(op: *mut PyObject) -> c_uint {
|
||||||
debug_assert!(crate::ffi::PyUnicode_Check(op) != 0);
|
debug_assert!(crate::PyUnicode_Check(op) != 0);
|
||||||
debug_assert!(PyUnicode_IS_READY(op) != 0);
|
debug_assert!(PyUnicode_IS_READY(op) != 0);
|
||||||
|
|
||||||
(*(op as *mut PyASCIIObject)).ascii()
|
(*(op as *mut PyASCIIObject)).ascii()
|
||||||
|
@ -170,7 +170,7 @@ pub unsafe fn PyUnicode_4BYTE_DATA(op: *mut PyObject) -> *mut Py_UCS4 {
|
||||||
#[inline]
|
#[inline]
|
||||||
#[cfg(target_endian = "little")]
|
#[cfg(target_endian = "little")]
|
||||||
pub unsafe fn PyUnicode_KIND(op: *mut PyObject) -> c_uint {
|
pub unsafe fn PyUnicode_KIND(op: *mut PyObject) -> c_uint {
|
||||||
debug_assert!(crate::ffi::PyUnicode_Check(op) != 0);
|
debug_assert!(crate::PyUnicode_Check(op) != 0);
|
||||||
debug_assert!(PyUnicode_IS_READY(op) != 0);
|
debug_assert!(PyUnicode_IS_READY(op) != 0);
|
||||||
|
|
||||||
(*(op as *mut PyASCIIObject)).kind()
|
(*(op as *mut PyASCIIObject)).kind()
|
||||||
|
@ -197,7 +197,7 @@ pub unsafe fn _PyUnicode_NONCOMPACT_DATA(op: *mut PyObject) -> *mut c_void {
|
||||||
#[inline]
|
#[inline]
|
||||||
#[cfg(target_endian = "little")]
|
#[cfg(target_endian = "little")]
|
||||||
pub unsafe fn PyUnicode_DATA(op: *mut PyObject) -> *mut c_void {
|
pub unsafe fn PyUnicode_DATA(op: *mut PyObject) -> *mut c_void {
|
||||||
debug_assert!(crate::ffi::PyUnicode_Check(op) != 0);
|
debug_assert!(crate::PyUnicode_Check(op) != 0);
|
||||||
|
|
||||||
if PyUnicode_IS_COMPACT(op) != 0 {
|
if PyUnicode_IS_COMPACT(op) != 0 {
|
||||||
_PyUnicode_COMPACT_DATA(op)
|
_PyUnicode_COMPACT_DATA(op)
|
||||||
|
@ -213,7 +213,7 @@ pub unsafe fn PyUnicode_DATA(op: *mut PyObject) -> *mut c_void {
|
||||||
#[inline]
|
#[inline]
|
||||||
#[cfg(target_endian = "little")]
|
#[cfg(target_endian = "little")]
|
||||||
pub unsafe fn PyUnicode_GET_LENGTH(op: *mut PyObject) -> Py_ssize_t {
|
pub unsafe fn PyUnicode_GET_LENGTH(op: *mut PyObject) -> Py_ssize_t {
|
||||||
debug_assert!(crate::ffi::PyUnicode_Check(op) != 0);
|
debug_assert!(crate::PyUnicode_Check(op) != 0);
|
||||||
debug_assert!(PyUnicode_IS_READY(op) != 0);
|
debug_assert!(PyUnicode_IS_READY(op) != 0);
|
||||||
|
|
||||||
(*(op as *mut PyASCIIObject)).length
|
(*(op as *mut PyASCIIObject)).length
|
||||||
|
@ -230,7 +230,7 @@ pub unsafe fn PyUnicode_IS_READY(op: *mut PyObject) -> c_uint {
|
||||||
#[inline]
|
#[inline]
|
||||||
#[cfg(target_endian = "little")]
|
#[cfg(target_endian = "little")]
|
||||||
pub unsafe fn PyUnicode_READY(op: *mut PyObject) -> c_int {
|
pub unsafe fn PyUnicode_READY(op: *mut PyObject) -> c_int {
|
||||||
debug_assert!(crate::ffi::PyUnicode_Check(op) != 0);
|
debug_assert!(crate::PyUnicode_Check(op) != 0);
|
||||||
|
|
||||||
if PyUnicode_IS_READY(op) != 0 {
|
if PyUnicode_IS_READY(op) != 0 {
|
||||||
0
|
0
|
||||||
|
@ -480,130 +480,3 @@ extern "C" {
|
||||||
// skipped _PyUnicode_FromId
|
// skipped _PyUnicode_FromId
|
||||||
// skipped _PyUnicode_EQ
|
// skipped _PyUnicode_EQ
|
||||||
// skipped _PyUnicode_ScanIdentifier
|
// skipped _PyUnicode_ScanIdentifier
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
#[cfg(target_endian = "little")]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::types::PyString;
|
|
||||||
use crate::{AsPyPointer, Python};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ascii_object_bitfield() {
|
|
||||||
let ob_base: PyObject = unsafe { std::mem::zeroed() };
|
|
||||||
|
|
||||||
let mut o = PyASCIIObject {
|
|
||||||
ob_base,
|
|
||||||
length: 0,
|
|
||||||
hash: 0,
|
|
||||||
state: 0,
|
|
||||||
wstr: std::ptr::null_mut() as *mut wchar_t,
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
assert_eq!(o.interned(), 0);
|
|
||||||
assert_eq!(o.kind(), 0);
|
|
||||||
assert_eq!(o.compact(), 0);
|
|
||||||
assert_eq!(o.ascii(), 0);
|
|
||||||
assert_eq!(o.ready(), 0);
|
|
||||||
|
|
||||||
for i in 0..4 {
|
|
||||||
o.state = i;
|
|
||||||
assert_eq!(o.interned(), i);
|
|
||||||
}
|
|
||||||
|
|
||||||
for i in 0..8 {
|
|
||||||
o.state = i << 2;
|
|
||||||
assert_eq!(o.kind(), i);
|
|
||||||
}
|
|
||||||
|
|
||||||
o.state = 1 << 5;
|
|
||||||
assert_eq!(o.compact(), 1);
|
|
||||||
|
|
||||||
o.state = 1 << 6;
|
|
||||||
assert_eq!(o.ascii(), 1);
|
|
||||||
|
|
||||||
o.state = 1 << 7;
|
|
||||||
assert_eq!(o.ready(), 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg_attr(Py_3_10, allow(deprecated))]
|
|
||||||
fn ascii() {
|
|
||||||
Python::with_gil(|py| {
|
|
||||||
// This test relies on implementation details of PyString.
|
|
||||||
let s = PyString::new(py, "hello, world");
|
|
||||||
let ptr = s.as_ptr();
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let ascii_ptr = ptr as *mut PyASCIIObject;
|
|
||||||
let ascii = ascii_ptr.as_ref().unwrap();
|
|
||||||
|
|
||||||
assert_eq!(ascii.interned(), 0);
|
|
||||||
assert_eq!(ascii.kind(), PyUnicode_1BYTE_KIND);
|
|
||||||
assert_eq!(ascii.compact(), 1);
|
|
||||||
assert_eq!(ascii.ascii(), 1);
|
|
||||||
assert_eq!(ascii.ready(), 1);
|
|
||||||
|
|
||||||
assert_eq!(PyUnicode_IS_ASCII(ptr), 1);
|
|
||||||
assert_eq!(PyUnicode_IS_COMPACT(ptr), 1);
|
|
||||||
assert_eq!(PyUnicode_IS_COMPACT_ASCII(ptr), 1);
|
|
||||||
|
|
||||||
assert!(!PyUnicode_1BYTE_DATA(ptr).is_null());
|
|
||||||
// 2 and 4 byte macros return nonsense for this string instance.
|
|
||||||
assert_eq!(PyUnicode_KIND(ptr), PyUnicode_1BYTE_KIND);
|
|
||||||
|
|
||||||
assert!(!_PyUnicode_COMPACT_DATA(ptr).is_null());
|
|
||||||
// _PyUnicode_NONCOMPACT_DATA isn't valid for compact strings.
|
|
||||||
assert!(!PyUnicode_DATA(ptr).is_null());
|
|
||||||
|
|
||||||
assert_eq!(PyUnicode_GET_LENGTH(ptr), s.len().unwrap() as _);
|
|
||||||
assert_eq!(PyUnicode_IS_READY(ptr), 1);
|
|
||||||
|
|
||||||
// This has potential to mutate object. But it should be a no-op since
|
|
||||||
// we're already ready.
|
|
||||||
assert_eq!(PyUnicode_READY(ptr), 0);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg_attr(Py_3_10, allow(deprecated))]
|
|
||||||
fn ucs4() {
|
|
||||||
Python::with_gil(|py| {
|
|
||||||
let s = "哈哈🐈";
|
|
||||||
let py_string = PyString::new(py, s);
|
|
||||||
let ptr = py_string.as_ptr();
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let ascii_ptr = ptr as *mut PyASCIIObject;
|
|
||||||
let ascii = ascii_ptr.as_ref().unwrap();
|
|
||||||
|
|
||||||
assert_eq!(ascii.interned(), 0);
|
|
||||||
assert_eq!(ascii.kind(), PyUnicode_4BYTE_KIND);
|
|
||||||
assert_eq!(ascii.compact(), 1);
|
|
||||||
assert_eq!(ascii.ascii(), 0);
|
|
||||||
assert_eq!(ascii.ready(), 1);
|
|
||||||
|
|
||||||
assert_eq!(PyUnicode_IS_ASCII(ptr), 0);
|
|
||||||
assert_eq!(PyUnicode_IS_COMPACT(ptr), 1);
|
|
||||||
assert_eq!(PyUnicode_IS_COMPACT_ASCII(ptr), 0);
|
|
||||||
|
|
||||||
assert!(!PyUnicode_4BYTE_DATA(ptr).is_null());
|
|
||||||
assert_eq!(PyUnicode_KIND(ptr), PyUnicode_4BYTE_KIND);
|
|
||||||
|
|
||||||
assert!(!_PyUnicode_COMPACT_DATA(ptr).is_null());
|
|
||||||
// _PyUnicode_NONCOMPACT_DATA isn't valid for compact strings.
|
|
||||||
assert!(!PyUnicode_DATA(ptr).is_null());
|
|
||||||
|
|
||||||
assert_eq!(PyUnicode_GET_LENGTH(ptr), py_string.len().unwrap() as _);
|
|
||||||
assert_eq!(PyUnicode_IS_READY(ptr), 1);
|
|
||||||
|
|
||||||
// This has potential to mutate object. But it should be a no-op since
|
|
||||||
// we're already ready.
|
|
||||||
assert_eq!(PyUnicode_READY(ptr), 0);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -9,15 +9,13 @@
|
||||||
//! Support for `PyDateTime_CAPI` is limited as of PyPy 7.0.0.
|
//! Support for `PyDateTime_CAPI` is limited as of PyPy 7.0.0.
|
||||||
//! `DateTime_FromTimestamp` and `Date_FromTimestamp` are currently not supported.
|
//! `DateTime_FromTimestamp` and `Date_FromTimestamp` are currently not supported.
|
||||||
|
|
||||||
use crate::ffi::{PyObject, PyTypeObject};
|
use crate::{PyObject, PyObject_TypeCheck, PyTypeObject, Py_TYPE};
|
||||||
use crate::ffi::{PyObject_TypeCheck, Py_TYPE};
|
use std::cell::UnsafeCell;
|
||||||
use crate::once_cell::GILOnceCell;
|
|
||||||
use crate::Python;
|
|
||||||
use std::ops::Deref;
|
|
||||||
use std::os::raw::{c_char, c_int, c_uchar};
|
use std::os::raw::{c_char, c_int, c_uchar};
|
||||||
|
use std::ptr;
|
||||||
#[cfg(not(PyPy))]
|
#[cfg(not(PyPy))]
|
||||||
use {
|
use {
|
||||||
crate::ffi::{PyCapsule_Import, Py_hash_t},
|
crate::{PyCapsule_Import, Py_hash_t},
|
||||||
std::ffi::CString,
|
std::ffi::CString,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -434,62 +432,38 @@ pub struct PyDateTime_CAPI {
|
||||||
// Python already shares this object between threads, so it's no more evil for us to do it too!
|
// Python already shares this object between threads, so it's no more evil for us to do it too!
|
||||||
unsafe impl Sync for PyDateTime_CAPI {}
|
unsafe impl Sync for PyDateTime_CAPI {}
|
||||||
|
|
||||||
/// Safe wrapper around the Python datetime C-API global. Note that this object differs slightly
|
/// Returns a pointer to a `PyDateTime_CAPI` instance
|
||||||
/// from the equivalent C object: in C, this is implemented as a `static PyDateTime_CAPI *`. Here
|
|
||||||
/// this is implemented as a wrapper which implements [`Deref`] to access a reference to a
|
|
||||||
/// [`PyDateTime_CAPI`] object.
|
|
||||||
///
|
///
|
||||||
/// In the [`Deref`] implementation, if the underlying object has not yet been initialized, the GIL
|
/// # Note
|
||||||
/// will automatically be acquired and [`PyDateTime_IMPORT()`] called.
|
/// This function will return a null pointer until
|
||||||
pub static PyDateTimeAPI: _PyDateTimeAPI_impl = _PyDateTimeAPI_impl {
|
/// `PyDateTime_IMPORT` is called
|
||||||
inner: GILOnceCell::new(),
|
#[inline]
|
||||||
};
|
pub unsafe fn PyDateTimeAPI() -> *mut PyDateTime_CAPI {
|
||||||
|
*PyDateTimeAPI_impl.0.get()
|
||||||
|
}
|
||||||
|
|
||||||
/// Safe wrapper around the Python C-API global `PyDateTime_TimeZone_UTC`. This follows a similar
|
|
||||||
/// strategy as [`PyDateTimeAPI`]: the Python datetime C-API will automatically be imported if this
|
|
||||||
/// type is deferenced.
|
|
||||||
///
|
|
||||||
/// The type obtained by dereferencing this object is `&'static PyObject`. This may change in the
|
|
||||||
/// future to be a more specific type representing that this is a `datetime.timezone` object.
|
|
||||||
#[cfg(not(all(PyPy, not(Py_3_8))))]
|
#[cfg(not(all(PyPy, not(Py_3_8))))]
|
||||||
pub static PyDateTime_TimeZone_UTC: _PyDateTime_TimeZone_UTC_impl = _PyDateTime_TimeZone_UTC_impl {
|
#[inline]
|
||||||
inner: &PyDateTimeAPI,
|
pub unsafe fn PyDateTime_TimeZone_UTC() -> *mut PyObject {
|
||||||
};
|
(*PyDateTimeAPI()).TimeZone_UTC
|
||||||
|
}
|
||||||
|
|
||||||
/// Populates the `PyDateTimeAPI` object
|
/// Populates the `PyDateTimeAPI` object
|
||||||
///
|
pub unsafe fn PyDateTime_IMPORT() {
|
||||||
/// Unlike in C, this does *not* need to be actively invoked in Rust, which
|
// PyPy expects the C-API to be initialized via PyDateTime_Import, so trying to use
|
||||||
/// will populate the `PyDateTimeAPI` struct automatically on first use.
|
// `PyCapsule_Import` will behave unexpectedly in pypy.
|
||||||
/// Use this function only if you want to eagerly load the datetime module,
|
#[cfg(PyPy)]
|
||||||
/// such as if you do not want the first call to a datetime function to be
|
let py_datetime_c_api = PyDateTime_Import();
|
||||||
/// slightly slower than subsequent calls.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
/// The Python GIL must be held.
|
|
||||||
pub unsafe fn PyDateTime_IMPORT() -> &'static PyDateTime_CAPI {
|
|
||||||
PyDateTimeAPI
|
|
||||||
.inner
|
|
||||||
.get_or_init(Python::assume_gil_acquired(), || {
|
|
||||||
// Because `get_or_init` is called with `assume_gil_acquired()`, it's necessary to acquire
|
|
||||||
// the GIL here to prevent segfault in usage of the safe `PyDateTimeAPI::deref` impl.
|
|
||||||
Python::with_gil(|_py| {
|
|
||||||
// PyPy expects the C-API to be initialized via PyDateTime_Import, so trying to use
|
|
||||||
// `PyCapsule_Import` will behave unexpectedly in pypy.
|
|
||||||
#[cfg(PyPy)]
|
|
||||||
let py_datetime_c_api = PyDateTime_Import();
|
|
||||||
|
|
||||||
#[cfg(not(PyPy))]
|
#[cfg(not(PyPy))]
|
||||||
let py_datetime_c_api = {
|
let py_datetime_c_api = {
|
||||||
// PyDateTime_CAPSULE_NAME is a macro in C
|
// PyDateTime_CAPSULE_NAME is a macro in C
|
||||||
let PyDateTime_CAPSULE_NAME = CString::new("datetime.datetime_CAPI").unwrap();
|
let PyDateTime_CAPSULE_NAME = CString::new("datetime.datetime_CAPI").unwrap();
|
||||||
|
|
||||||
&*(PyCapsule_Import(PyDateTime_CAPSULE_NAME.as_ptr(), 1)
|
PyCapsule_Import(PyDateTime_CAPSULE_NAME.as_ptr(), 1) as *mut PyDateTime_CAPI
|
||||||
as *const PyDateTime_CAPI)
|
};
|
||||||
};
|
|
||||||
|
|
||||||
py_datetime_c_api
|
*PyDateTimeAPI_impl.0.get() = py_datetime_c_api;
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// skipped non-limited PyDateTime_TimeZone_UTC
|
// skipped non-limited PyDateTime_TimeZone_UTC
|
||||||
|
@ -502,61 +476,61 @@ pub unsafe fn PyDateTime_IMPORT() -> &'static PyDateTime_CAPI {
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Check if `op` is a `PyDateTimeAPI.DateType` or subtype.
|
/// Check if `op` is a `PyDateTimeAPI.DateType` or subtype.
|
||||||
pub unsafe fn PyDate_Check(op: *mut PyObject) -> c_int {
|
pub unsafe fn PyDate_Check(op: *mut PyObject) -> c_int {
|
||||||
PyObject_TypeCheck(op, PyDateTimeAPI.DateType) as c_int
|
PyObject_TypeCheck(op, (*PyDateTimeAPI()).DateType) as c_int
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Check if `op`'s type is exactly `PyDateTimeAPI.DateType`.
|
/// Check if `op`'s type is exactly `PyDateTimeAPI.DateType`.
|
||||||
pub unsafe fn PyDate_CheckExact(op: *mut PyObject) -> c_int {
|
pub unsafe fn PyDate_CheckExact(op: *mut PyObject) -> c_int {
|
||||||
(Py_TYPE(op) == PyDateTimeAPI.DateType) as c_int
|
(Py_TYPE(op) == (*PyDateTimeAPI()).DateType) as c_int
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Check if `op` is a `PyDateTimeAPI.DateTimeType` or subtype.
|
/// Check if `op` is a `PyDateTimeAPI.DateTimeType` or subtype.
|
||||||
pub unsafe fn PyDateTime_Check(op: *mut PyObject) -> c_int {
|
pub unsafe fn PyDateTime_Check(op: *mut PyObject) -> c_int {
|
||||||
PyObject_TypeCheck(op, PyDateTimeAPI.DateTimeType) as c_int
|
PyObject_TypeCheck(op, (*PyDateTimeAPI()).DateTimeType) as c_int
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Check if `op`'s type is exactly `PyDateTimeAPI.DateTimeType`.
|
/// Check if `op`'s type is exactly `PyDateTimeAPI.DateTimeType`.
|
||||||
pub unsafe fn PyDateTime_CheckExact(op: *mut PyObject) -> c_int {
|
pub unsafe fn PyDateTime_CheckExact(op: *mut PyObject) -> c_int {
|
||||||
(Py_TYPE(op) == PyDateTimeAPI.DateTimeType) as c_int
|
(Py_TYPE(op) == (*PyDateTimeAPI()).DateTimeType) as c_int
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Check if `op` is a `PyDateTimeAPI.TimeType` or subtype.
|
/// Check if `op` is a `PyDateTimeAPI.TimeType` or subtype.
|
||||||
pub unsafe fn PyTime_Check(op: *mut PyObject) -> c_int {
|
pub unsafe fn PyTime_Check(op: *mut PyObject) -> c_int {
|
||||||
PyObject_TypeCheck(op, PyDateTimeAPI.TimeType) as c_int
|
PyObject_TypeCheck(op, (*PyDateTimeAPI()).TimeType) as c_int
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Check if `op`'s type is exactly `PyDateTimeAPI.TimeType`.
|
/// Check if `op`'s type is exactly `PyDateTimeAPI.TimeType`.
|
||||||
pub unsafe fn PyTime_CheckExact(op: *mut PyObject) -> c_int {
|
pub unsafe fn PyTime_CheckExact(op: *mut PyObject) -> c_int {
|
||||||
(Py_TYPE(op) == PyDateTimeAPI.TimeType) as c_int
|
(Py_TYPE(op) == (*PyDateTimeAPI()).TimeType) as c_int
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Check if `op` is a `PyDateTimeAPI.DetaType` or subtype.
|
/// Check if `op` is a `PyDateTimeAPI.DetaType` or subtype.
|
||||||
pub unsafe fn PyDelta_Check(op: *mut PyObject) -> c_int {
|
pub unsafe fn PyDelta_Check(op: *mut PyObject) -> c_int {
|
||||||
PyObject_TypeCheck(op, PyDateTimeAPI.DeltaType) as c_int
|
PyObject_TypeCheck(op, (*PyDateTimeAPI()).DeltaType) as c_int
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Check if `op`'s type is exactly `PyDateTimeAPI.DeltaType`.
|
/// Check if `op`'s type is exactly `PyDateTimeAPI.DeltaType`.
|
||||||
pub unsafe fn PyDelta_CheckExact(op: *mut PyObject) -> c_int {
|
pub unsafe fn PyDelta_CheckExact(op: *mut PyObject) -> c_int {
|
||||||
(Py_TYPE(op) == PyDateTimeAPI.DeltaType) as c_int
|
(Py_TYPE(op) == (*PyDateTimeAPI()).DeltaType) as c_int
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Check if `op` is a `PyDateTimeAPI.TZInfoType` or subtype.
|
/// Check if `op` is a `PyDateTimeAPI.TZInfoType` or subtype.
|
||||||
pub unsafe fn PyTZInfo_Check(op: *mut PyObject) -> c_int {
|
pub unsafe fn PyTZInfo_Check(op: *mut PyObject) -> c_int {
|
||||||
PyObject_TypeCheck(op, PyDateTimeAPI.TZInfoType) as c_int
|
PyObject_TypeCheck(op, (*PyDateTimeAPI()).TZInfoType) as c_int
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Check if `op`'s type is exactly `PyDateTimeAPI.TZInfoType`.
|
/// Check if `op`'s type is exactly `PyDateTimeAPI.TZInfoType`.
|
||||||
pub unsafe fn PyTZInfo_CheckExact(op: *mut PyObject) -> c_int {
|
pub unsafe fn PyTZInfo_CheckExact(op: *mut PyObject) -> c_int {
|
||||||
(Py_TYPE(op) == PyDateTimeAPI.TZInfoType) as c_int
|
(Py_TYPE(op) == (*PyDateTimeAPI()).TZInfoType) as c_int
|
||||||
}
|
}
|
||||||
|
|
||||||
// skipped non-limited PyDate_FromDate
|
// skipped non-limited PyDate_FromDate
|
||||||
|
@ -570,12 +544,14 @@ pub unsafe fn PyTZInfo_CheckExact(op: *mut PyObject) -> c_int {
|
||||||
|
|
||||||
#[cfg(not(PyPy))]
|
#[cfg(not(PyPy))]
|
||||||
pub unsafe fn PyDateTime_FromTimestamp(args: *mut PyObject) -> *mut PyObject {
|
pub unsafe fn PyDateTime_FromTimestamp(args: *mut PyObject) -> *mut PyObject {
|
||||||
(PyDateTimeAPI.DateTime_FromTimestamp)(PyDateTimeAPI.DateTimeType, args, std::ptr::null_mut())
|
let f = (*PyDateTimeAPI()).DateTime_FromTimestamp;
|
||||||
|
f((*PyDateTimeAPI()).DateTimeType, args, std::ptr::null_mut())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(PyPy))]
|
#[cfg(not(PyPy))]
|
||||||
pub unsafe fn PyDate_FromTimestamp(args: *mut PyObject) -> *mut PyObject {
|
pub unsafe fn PyDate_FromTimestamp(args: *mut PyObject) -> *mut PyObject {
|
||||||
(PyDateTimeAPI.Date_FromTimestamp)(PyDateTimeAPI.DateType, args)
|
let f = (*PyDateTimeAPI()).Date_FromTimestamp;
|
||||||
|
f((*PyDateTimeAPI()).DateType, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(PyPy)]
|
#[cfg(PyPy)]
|
||||||
|
@ -589,96 +565,13 @@ extern "C" {
|
||||||
#[cfg(PyPy)]
|
#[cfg(PyPy)]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#[link_name = "_PyPyDateTime_Import"]
|
#[link_name = "_PyPyDateTime_Import"]
|
||||||
pub fn PyDateTime_Import() -> &'static PyDateTime_CAPI;
|
pub fn PyDateTime_Import() -> *mut PyDateTime_CAPI;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- implementation details which are specific to Rust. --
|
// Rust specific implementation details
|
||||||
|
|
||||||
#[doc(hidden)]
|
struct PyDateTimeAPISingleton(UnsafeCell<*mut PyDateTime_CAPI>);
|
||||||
pub struct _PyDateTimeAPI_impl {
|
unsafe impl Sync for PyDateTimeAPISingleton {}
|
||||||
inner: GILOnceCell<&'static PyDateTime_CAPI>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for _PyDateTimeAPI_impl {
|
static PyDateTimeAPI_impl: PyDateTimeAPISingleton =
|
||||||
type Target = PyDateTime_CAPI;
|
PyDateTimeAPISingleton(UnsafeCell::new(ptr::null_mut()));
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn deref(&self) -> &'static PyDateTime_CAPI {
|
|
||||||
unsafe { PyDateTime_IMPORT() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[cfg(not(all(PyPy, not(Py_3_8))))]
|
|
||||||
pub struct _PyDateTime_TimeZone_UTC_impl {
|
|
||||||
inner: &'static _PyDateTimeAPI_impl,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(all(PyPy, not(Py_3_8))))]
|
|
||||||
impl Deref for _PyDateTime_TimeZone_UTC_impl {
|
|
||||||
type Target = crate::PyObject;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn deref(&self) -> &crate::PyObject {
|
|
||||||
unsafe {
|
|
||||||
&*((&self.inner.TimeZone_UTC) as *const *mut crate::ffi::PyObject
|
|
||||||
as *const crate::PyObject)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::{types::PyDict, AsPyPointer, IntoPy, Py, PyAny, Python};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_datetime_fromtimestamp() {
|
|
||||||
Python::with_gil(|py| {
|
|
||||||
let args: Py<PyAny> = (100,).into_py(py);
|
|
||||||
unsafe { PyDateTime_IMPORT() };
|
|
||||||
let dt: &PyAny = unsafe { py.from_owned_ptr(PyDateTime_FromTimestamp(args.as_ptr())) };
|
|
||||||
let locals = PyDict::new(py);
|
|
||||||
locals.set_item("dt", dt).unwrap();
|
|
||||||
py.run(
|
|
||||||
"import datetime; assert dt == datetime.datetime.fromtimestamp(100)",
|
|
||||||
None,
|
|
||||||
Some(locals),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_date_fromtimestamp() {
|
|
||||||
Python::with_gil(|py| {
|
|
||||||
let args: Py<PyAny> = (100,).into_py(py);
|
|
||||||
unsafe { PyDateTime_IMPORT() };
|
|
||||||
let dt: &PyAny = unsafe { py.from_owned_ptr(PyDate_FromTimestamp(args.as_ptr())) };
|
|
||||||
let locals = PyDict::new(py);
|
|
||||||
locals.set_item("dt", dt).unwrap();
|
|
||||||
py.run(
|
|
||||||
"import datetime; assert dt == datetime.date.fromtimestamp(100)",
|
|
||||||
None,
|
|
||||||
Some(locals),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(not(all(PyPy, not(Py_3_8))))]
|
|
||||||
fn test_utc_timezone() {
|
|
||||||
Python::with_gil(|py| {
|
|
||||||
let utc_timezone = PyDateTime_TimeZone_UTC.as_ref(py);
|
|
||||||
let locals = PyDict::new(py);
|
|
||||||
locals.set_item("utc_timezone", utc_timezone).unwrap();
|
|
||||||
py.run(
|
|
||||||
"import datetime; assert utc_timezone is datetime.timezone.utc",
|
|
||||||
None,
|
|
||||||
Some(locals),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::ffi::methodobject::PyMethodDef;
|
use crate::methodobject::PyMethodDef;
|
||||||
use crate::ffi::object::{PyObject, PyTypeObject};
|
use crate::object::{PyObject, PyTypeObject};
|
||||||
use crate::ffi::structmember::PyMemberDef;
|
use crate::structmember::PyMemberDef;
|
||||||
use std::os::raw::{c_char, c_int, c_void};
|
use std::os::raw::{c_char, c_int, c_void};
|
||||||
|
|
||||||
pub type getter = unsafe extern "C" fn(slf: *mut PyObject, closure: *mut c_void) -> *mut PyObject;
|
pub type getter = unsafe extern "C" fn(slf: *mut PyObject, closure: *mut c_void) -> *mut PyObject;
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
use crate::ffi::pyport::Py_ssize_t;
|
use crate::pyport::Py_ssize_t;
|
||||||
use std::os::raw::{c_char, c_int};
|
use std::os::raw::{c_char, c_int};
|
||||||
|
|
||||||
#[cfg_attr(windows, link(name = "pythonXY"))]
|
#[cfg_attr(windows, link(name = "pythonXY"))]
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::ffi::object::PyTypeObject;
|
use crate::object::PyTypeObject;
|
||||||
|
|
||||||
#[cfg_attr(windows, link(name = "pythonXY"))]
|
#[cfg_attr(windows, link(name = "pythonXY"))]
|
||||||
extern "C" {
|
extern "C" {
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::ffi::object::PyObject;
|
use crate::object::PyObject;
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::ffi::object::PyObject;
|
use crate::object::PyObject;
|
||||||
use std::os::raw::{c_char, c_int};
|
use std::os::raw::{c_char, c_int};
|
||||||
|
|
||||||
pub const PY_STDIOTEXTMODE: &str = "b";
|
pub const PY_STDIOTEXTMODE: &str = "b";
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::ffi::pyport::Py_ssize_t;
|
use crate::pyport::Py_ssize_t;
|
||||||
use libc::wchar_t;
|
use libc::wchar_t;
|
||||||
use std::os::raw::c_char;
|
use std::os::raw::c_char;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
use std::os::raw::{c_double, c_int};
|
use std::os::raw::{c_double, c_int};
|
||||||
|
|
||||||
#[cfg(Py_LIMITED_API)]
|
#[cfg(Py_LIMITED_API)]
|
|
@ -1,6 +1,6 @@
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
|
||||||
use crate::ffi::object::{PyObject, PyTypeObject, Py_TYPE};
|
use crate::object::{PyObject, PyTypeObject, Py_TYPE};
|
||||||
|
|
||||||
// skipped PyFunctionObject
|
// skipped PyFunctionObject
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
use crate::ffi::pyport::Py_ssize_t;
|
use crate::pyport::Py_ssize_t;
|
||||||
use crate::ffi::PyFrameObject;
|
use crate::PyFrameObject;
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::ffi::object::PyObject;
|
use crate::object::PyObject;
|
||||||
use std::os::raw::{c_char, c_int, c_long};
|
use std::os::raw::{c_char, c_int, c_long};
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
|
||||||
#[cfg_attr(windows, link(name = "pythonXY"))]
|
#[cfg_attr(windows, link(name = "pythonXY"))]
|
|
@ -0,0 +1,432 @@
|
||||||
|
//! Raw FFI declarations for Python's C API.
|
||||||
|
//!
|
||||||
|
//! PyO3 can be used to write native Python modules or run Python code and modules from Rust.
|
||||||
|
//!
|
||||||
|
//! This crate just provides low level bindings to the Python interpreter.
|
||||||
|
//! It is meant for advanced users only - regular PyO3 users shouldn't
|
||||||
|
//! need to interact with this crate at all.
|
||||||
|
//!
|
||||||
|
//! The contents of this crate are not documented here, as it would entail
|
||||||
|
//! basically copying the documentation from CPython. Consult the [Python/C API Reference
|
||||||
|
//! Manual][capi] for up-to-date documentation.
|
||||||
|
//!
|
||||||
|
//! # Safety
|
||||||
|
//!
|
||||||
|
//! The functions in this crate lack individual safety documentation, but
|
||||||
|
//! generally the following apply:
|
||||||
|
//! - Pointer arguments have to point to a valid Python object of the correct type,
|
||||||
|
//! although null pointers are sometimes valid input.
|
||||||
|
//! - The vast majority can only be used safely while the GIL is held.
|
||||||
|
//! - Some functions have additional safety requirements, consult the
|
||||||
|
//! [Python/C API Reference Manual][capi]
|
||||||
|
//! for more information.
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//! # Feature flags
|
||||||
|
//!
|
||||||
|
//! PyO3 uses [feature flags] to enable you to opt-in to additional functionality. For a detailed
|
||||||
|
//! description, see the [Features chapter of the guide].
|
||||||
|
//!
|
||||||
|
//! ## Optional feature flags
|
||||||
|
//!
|
||||||
|
//! The following features customize PyO3's behavior:
|
||||||
|
//!
|
||||||
|
//! - `abi3`: Restricts PyO3's API to a subset of the full Python API which is guaranteed by
|
||||||
|
//! [PEP 384] to be forward-compatible with future Python versions.
|
||||||
|
//! - `extension-module`: This will tell the linker to keep the Python symbols unresolved, so that
|
||||||
|
//! your module can also be used with statically linked Python interpreters. Use this feature when
|
||||||
|
//! building an extension module.
|
||||||
|
//!
|
||||||
|
//! ## `rustc` environment flags
|
||||||
|
//!
|
||||||
|
//! PyO3 uses `rustc`'s `--cfg` flags to enable or disable code used for different Python versions.
|
||||||
|
//! If you want to do this for your own crate, you can do so with the [`pyo3-build-config`] crate.
|
||||||
|
//!
|
||||||
|
//! - `Py_3_7`, `Py_3_8`, `Py_3_9`, `Py_3_10`: Marks code that is only enabled when
|
||||||
|
//! compiling for a given minimum Python version.
|
||||||
|
//! - `Py_LIMITED_API`: Marks code enabled when the `abi3` feature flag is enabled.
|
||||||
|
//! - `PyPy` - Marks code enabled when compiling for PyPy.
|
||||||
|
//!
|
||||||
|
//! # Minimum supported Rust and Python versions
|
||||||
|
//!
|
||||||
|
//! PyO3 supports the following software versions:
|
||||||
|
//! - Python 3.7 and up (CPython and PyPy)
|
||||||
|
//! - Rust 1.48 and up
|
||||||
|
//!
|
||||||
|
//! # Example: Building Python Native modules
|
||||||
|
//!
|
||||||
|
//! PyO3 can be used to generate a native Python module. The easiest way to try this out for the
|
||||||
|
//! first time is to use [`maturin`]. `maturin` is a tool for building and publishing Rust-based
|
||||||
|
//! Python packages with minimal configuration. The following steps set up some files for an example
|
||||||
|
//! Python module, install `maturin`, and then show how to build and import the Python module.
|
||||||
|
//!
|
||||||
|
//! First, create a new folder (let's call it `string_sum`) containing the following two files:
|
||||||
|
//!
|
||||||
|
//! **`Cargo.toml`**
|
||||||
|
//!
|
||||||
|
//! ```toml
|
||||||
|
//! [lib]
|
||||||
|
//! name = "string_sum"
|
||||||
|
//! # "cdylib" is necessary to produce a shared library for Python to import from.
|
||||||
|
//! #
|
||||||
|
//! # Downstream Rust code (including code in `bin/`, `examples/`, and `tests/`) will not be able
|
||||||
|
//! # to `use string_sum;` unless the "rlib" or "lib" crate type is also included, e.g.:
|
||||||
|
//! # crate-type = ["cdylib", "rlib"]
|
||||||
|
//! crate-type = ["cdylib"]
|
||||||
|
//!
|
||||||
|
//! [dependencies.pyo3-ffi]
|
||||||
|
// workaround for `extended_key_value_attributes`: https://github.com/rust-lang/rust/issues/82768#issuecomment-803935643
|
||||||
|
#![cfg_attr(docsrs, cfg_attr(docsrs, doc = concat!("version = \"", env!("CARGO_PKG_VERSION"), "\"")))]
|
||||||
|
#![cfg_attr(not(docsrs), doc = "version = \"*\"")]
|
||||||
|
//! features = ["extension-module"]
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! **`src/lib.rs`**
|
||||||
|
//! ```rust
|
||||||
|
//! use std::mem::transmute;
|
||||||
|
//! use std::os::raw::c_char;
|
||||||
|
//!
|
||||||
|
//! use pyo3_ffi::*;
|
||||||
|
//!
|
||||||
|
//! #[allow(non_snake_case)]
|
||||||
|
//! #[no_mangle]
|
||||||
|
//! pub unsafe extern "C" fn PyInit_string_sum() -> *mut PyObject {
|
||||||
|
//! let init = PyModuleDef {
|
||||||
|
//! m_base: PyModuleDef_HEAD_INIT,
|
||||||
|
//! m_name: "string_sum\0".as_ptr() as *const c_char,
|
||||||
|
//! m_doc: std::ptr::null(),
|
||||||
|
//! m_size: 0,
|
||||||
|
//! m_methods: std::ptr::null_mut(),
|
||||||
|
//! m_slots: std::ptr::null_mut(),
|
||||||
|
//! m_traverse: None,
|
||||||
|
//! m_clear: None,
|
||||||
|
//! m_free: None,
|
||||||
|
//! };
|
||||||
|
//!
|
||||||
|
//! let mptr = PyModule_Create(Box::into_raw(Box::new(init)));
|
||||||
|
//! let version = env!("CARGO_PKG_VERSION");
|
||||||
|
//! PyModule_AddObject(
|
||||||
|
//! mptr,
|
||||||
|
//! "__version__\0".as_ptr() as *const c_char,
|
||||||
|
//! PyUnicode_FromStringAndSize(version.as_ptr() as *const c_char, version.len() as isize),
|
||||||
|
//! );
|
||||||
|
//!
|
||||||
|
//! // It's necessary to transmute `sum_as_string` here because functions marked with `METH_FASTCALL`
|
||||||
|
//! // have a different signature. However the `PyMethodDef` struct currently represents all
|
||||||
|
//! // functions as a `PyCFunction`. The python interpreter will cast the function pointer back
|
||||||
|
//! // to `_PyCFunctionFast`.
|
||||||
|
//! let wrapped_sum_as_string = PyMethodDef {
|
||||||
|
//! ml_name: "sum_as_string\0".as_ptr() as *const c_char,
|
||||||
|
//! ml_meth: Some(transmute::<_PyCFunctionFast, PyCFunction>(sum_as_string)),
|
||||||
|
//! ml_flags: METH_FASTCALL,
|
||||||
|
//! ml_doc: "returns the sum of two integers as a string\0".as_ptr() as *const c_char,
|
||||||
|
//! };
|
||||||
|
//!
|
||||||
|
//! // PyModule_AddObject can technically fail.
|
||||||
|
//! // For more involved applications error checking may be necessary
|
||||||
|
//! PyModule_AddObject(
|
||||||
|
//! mptr,
|
||||||
|
//! "sum_as_string\0".as_ptr() as *const c_char,
|
||||||
|
//! PyCFunction_NewEx(
|
||||||
|
//! Box::into_raw(Box::new(wrapped_sum_as_string)),
|
||||||
|
//! std::ptr::null_mut(),
|
||||||
|
//! PyUnicode_InternFromString("string_sum\0".as_ptr() as *const c_char),
|
||||||
|
//! ),
|
||||||
|
//! );
|
||||||
|
//!
|
||||||
|
//! let all = ["__all__\0", "__version__\0", "sum_as_string\0"];
|
||||||
|
//!
|
||||||
|
//! let pyall = PyTuple_New(all.len() as isize);
|
||||||
|
//! for (i, obj) in all.iter().enumerate() {
|
||||||
|
//! PyTuple_SET_ITEM(
|
||||||
|
//! pyall,
|
||||||
|
//! i as isize,
|
||||||
|
//! PyUnicode_InternFromString(obj.as_ptr() as *const c_char),
|
||||||
|
//! )
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! PyModule_AddObject(mptr, "__all__\0".as_ptr() as *const c_char, pyall);
|
||||||
|
//!
|
||||||
|
//! mptr
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! pub unsafe extern "C" fn sum_as_string(
|
||||||
|
//! _self: *mut PyObject,
|
||||||
|
//! args: *mut *mut PyObject,
|
||||||
|
//! nargs: Py_ssize_t,
|
||||||
|
//! ) -> *mut PyObject {
|
||||||
|
//! if nargs != 2 {
|
||||||
|
//! return raise_type_error("sum_as_string() expected 2 positional arguments");
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! let arg1 = *args;
|
||||||
|
//! if PyLong_Check(arg1) == 0 {
|
||||||
|
//! return raise_type_error("sum_as_string() expected an int for positional argument 1");
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! let arg1 = PyLong_AsLong(arg1);
|
||||||
|
//! if !PyErr_Occurred().is_null() {
|
||||||
|
//! return ptr::null()
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! let arg2 = *args.add(1);
|
||||||
|
//! if PyLong_Check(arg2) == 0 {
|
||||||
|
//! return raise_type_error("sum_as_string() expected an int for positional argument 2");
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! let arg2 = PyLong_AsLong(arg2);
|
||||||
|
//! if !PyErr_Occurred().is_null() {
|
||||||
|
//! return ptr::null()
|
||||||
|
//! }
|
||||||
|
//! let res = (arg1 + arg2).to_string();
|
||||||
|
//! PyUnicode_FromStringAndSize(res.as_ptr() as *const c_char, res.len() as isize)
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! #[cold]
|
||||||
|
//! #[inline(never)]
|
||||||
|
//! fn raise_type_error(msg: &str) -> *mut PyObject {
|
||||||
|
//! unsafe {
|
||||||
|
//! let err_msg =
|
||||||
|
//! PyUnicode_FromStringAndSize(msg.as_ptr() as *const c_char, msg.len() as isize);
|
||||||
|
//! PyErr_SetObject(PyExc_TypeError, err_msg);
|
||||||
|
//! Py_DECREF(err_msg);
|
||||||
|
//! };
|
||||||
|
//! std::ptr::null_mut()
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! With those two files in place, now `maturin` needs to be installed. This can be done using
|
||||||
|
//! Python's package manager `pip`. First, load up a new Python `virtualenv`, and install `maturin`
|
||||||
|
//! into it:
|
||||||
|
//! ```bash
|
||||||
|
//! $ cd string_sum
|
||||||
|
//! $ python -m venv .env
|
||||||
|
//! $ source .env/bin/activate
|
||||||
|
//! $ pip install maturin
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Now build and execute the module:
|
||||||
|
//! ```bash
|
||||||
|
//! $ maturin develop
|
||||||
|
//! # lots of progress output as maturin runs the compilation...
|
||||||
|
//! $ python
|
||||||
|
//! >>> import string_sum
|
||||||
|
//! >>> string_sum.sum_as_string(5, 20)
|
||||||
|
//! '25'
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! As well as with `maturin`, it is possible to build using [setuptools-rust] or
|
||||||
|
//! [manually][manual_builds]. Both offer more flexibility than `maturin` but require further
|
||||||
|
//! configuration.
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//! # Using Python from Rust
|
||||||
|
//!
|
||||||
|
//! To embed Python into a Rust binary, you need to ensure that your Python installation contains a
|
||||||
|
//! shared library. The following steps demonstrate how to ensure this (for Ubuntu).
|
||||||
|
//!
|
||||||
|
//! To install the Python shared library on Ubuntu:
|
||||||
|
//! ```bash
|
||||||
|
//! sudo apt install python3-dev
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! While most projects use the safe wrapper provided by pyo3,
|
||||||
|
//! you can take a look at the [`orjson`] library as an example on how to use `pyo3-ffi` directly.
|
||||||
|
//! For those well versed in C and Rust the [tutorials] from the CPython documentation
|
||||||
|
//! can be easily converted to rust as well.
|
||||||
|
//!
|
||||||
|
//! [tutorials]: https://docs.python.org/3/extending/
|
||||||
|
//! [`orjson`]: https://github.com/ijl/orjson
|
||||||
|
//! [capi]: https://docs.python.org/3/c-api/index.html
|
||||||
|
//! [`maturin`]: https://github.com/PyO3/maturin "Build and publish crates with pyo3, rust-cpython and cffi bindings as well as rust binaries as python packages"
|
||||||
|
//! [`pyo3-build-config`]: https://docs.rs/pyo3-build-config
|
||||||
|
//! [feature flags]: https://doc.rust-lang.org/cargo/reference/features.html "Features - The Cargo Book"
|
||||||
|
//! [manual_builds]: https://pyo3.rs/latest/building_and_distribution.html#manual-builds "Manual builds - Building and Distribution - PyO3 user guide"
|
||||||
|
//! [setuptools-rust]: https://github.com/PyO3/setuptools-rust "Setuptools plugin for Rust extensions"
|
||||||
|
//! [PEP 384]: https://www.python.org/dev/peps/pep-0384 "PEP 384 -- Defining a Stable ABI"
|
||||||
|
//! [Features chapter of the guide]: https://pyo3.rs/latest/features.html#features-reference "Features Reference - PyO3 user guide"
|
||||||
|
|
||||||
|
#![allow(
|
||||||
|
missing_docs,
|
||||||
|
non_camel_case_types,
|
||||||
|
non_snake_case,
|
||||||
|
non_upper_case_globals,
|
||||||
|
clippy::upper_case_acronyms,
|
||||||
|
clippy::missing_safety_doc
|
||||||
|
)]
|
||||||
|
|
||||||
|
// Until `extern type` is stabilized, use the recommended approach to
|
||||||
|
// model opaque types:
|
||||||
|
// https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs
|
||||||
|
macro_rules! opaque_struct {
|
||||||
|
($name:ident) => {
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct $name([u8; 0]);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use self::abstract_::*;
|
||||||
|
pub use self::bltinmodule::*;
|
||||||
|
pub use self::boolobject::*;
|
||||||
|
pub use self::bytearrayobject::*;
|
||||||
|
pub use self::bytesobject::*;
|
||||||
|
pub use self::ceval::*;
|
||||||
|
pub use self::code::*;
|
||||||
|
pub use self::codecs::*;
|
||||||
|
pub use self::compile::*;
|
||||||
|
pub use self::complexobject::*;
|
||||||
|
#[cfg(all(Py_3_8, not(Py_LIMITED_API)))]
|
||||||
|
pub use self::context::*;
|
||||||
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
|
pub use self::datetime::*;
|
||||||
|
pub use self::descrobject::*;
|
||||||
|
pub use self::dictobject::*;
|
||||||
|
pub use self::enumobject::*;
|
||||||
|
pub use self::eval::*;
|
||||||
|
pub use self::fileobject::*;
|
||||||
|
pub use self::fileutils::*;
|
||||||
|
pub use self::floatobject::*;
|
||||||
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
|
pub use self::funcobject::*;
|
||||||
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
|
pub use self::genobject::*;
|
||||||
|
pub use self::import::*;
|
||||||
|
pub use self::intrcheck::*;
|
||||||
|
pub use self::iterobject::*;
|
||||||
|
pub use self::listobject::*;
|
||||||
|
pub use self::longobject::*;
|
||||||
|
pub use self::marshal::*;
|
||||||
|
pub use self::memoryobject::*;
|
||||||
|
pub use self::methodobject::*;
|
||||||
|
pub use self::modsupport::*;
|
||||||
|
pub use self::moduleobject::*;
|
||||||
|
pub use self::object::*;
|
||||||
|
pub use self::objimpl::*;
|
||||||
|
pub use self::osmodule::*;
|
||||||
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
|
pub use self::pyarena::*;
|
||||||
|
pub use self::pycapsule::*;
|
||||||
|
pub use self::pyerrors::*;
|
||||||
|
pub use self::pyframe::*;
|
||||||
|
pub use self::pyhash::*;
|
||||||
|
pub use self::pylifecycle::*;
|
||||||
|
pub use self::pymem::*;
|
||||||
|
pub use self::pyport::*;
|
||||||
|
pub use self::pystate::*;
|
||||||
|
pub use self::pystrtod::*;
|
||||||
|
pub use self::pythonrun::*;
|
||||||
|
pub use self::rangeobject::*;
|
||||||
|
pub use self::setobject::*;
|
||||||
|
pub use self::sliceobject::*;
|
||||||
|
pub use self::structseq::*;
|
||||||
|
pub use self::sysmodule::*;
|
||||||
|
pub use self::traceback::*;
|
||||||
|
pub use self::tupleobject::*;
|
||||||
|
pub use self::typeslots::*;
|
||||||
|
pub use self::unicodeobject::*;
|
||||||
|
pub use self::warnings::*;
|
||||||
|
pub use self::weakrefobject::*;
|
||||||
|
|
||||||
|
mod abstract_;
|
||||||
|
// skipped asdl.h
|
||||||
|
// skipped ast.h
|
||||||
|
mod bltinmodule;
|
||||||
|
mod boolobject;
|
||||||
|
mod bytearrayobject;
|
||||||
|
mod bytesobject;
|
||||||
|
// skipped cellobject.h
|
||||||
|
mod ceval;
|
||||||
|
// skipped classobject.h
|
||||||
|
mod code;
|
||||||
|
mod codecs;
|
||||||
|
mod compile;
|
||||||
|
mod complexobject;
|
||||||
|
#[cfg(all(Py_3_8, not(Py_LIMITED_API)))]
|
||||||
|
mod context; // It's actually 3.7.1, but no cfg for patches.
|
||||||
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
|
pub(crate) mod datetime;
|
||||||
|
mod descrobject;
|
||||||
|
mod dictobject;
|
||||||
|
// skipped dynamic_annotations.h
|
||||||
|
mod enumobject;
|
||||||
|
// skipped errcode.h
|
||||||
|
mod eval;
|
||||||
|
// skipped exports.h
|
||||||
|
mod fileobject;
|
||||||
|
mod fileutils;
|
||||||
|
mod floatobject;
|
||||||
|
// skipped empty frameobject.h
|
||||||
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
|
pub(crate) mod funcobject;
|
||||||
|
// skipped genericaliasobject.h
|
||||||
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
|
mod genobject;
|
||||||
|
mod import;
|
||||||
|
// skipped interpreteridobject.h
|
||||||
|
mod intrcheck;
|
||||||
|
mod iterobject;
|
||||||
|
mod listobject;
|
||||||
|
// skipped longintrepr.h
|
||||||
|
mod longobject;
|
||||||
|
pub(crate) mod marshal;
|
||||||
|
mod memoryobject;
|
||||||
|
mod methodobject;
|
||||||
|
mod modsupport;
|
||||||
|
mod moduleobject;
|
||||||
|
// skipped namespaceobject.h
|
||||||
|
mod object;
|
||||||
|
mod objimpl;
|
||||||
|
// skipped odictobject.h
|
||||||
|
// skipped opcode.h
|
||||||
|
// skipped osdefs.h
|
||||||
|
mod osmodule;
|
||||||
|
// skipped parser_interface.h
|
||||||
|
// skipped patchlevel.h
|
||||||
|
// skipped picklebufobject.h
|
||||||
|
// skipped pyctype.h
|
||||||
|
// skipped py_curses.h
|
||||||
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
|
mod pyarena;
|
||||||
|
mod pycapsule;
|
||||||
|
// skipped pydecimal.h
|
||||||
|
// skipped pydtrace.h
|
||||||
|
mod pyerrors;
|
||||||
|
// skipped pyexpat.h
|
||||||
|
// skipped pyfpe.h
|
||||||
|
mod pyframe;
|
||||||
|
mod pyhash;
|
||||||
|
mod pylifecycle;
|
||||||
|
// skipped pymacconfig.h
|
||||||
|
// skipped pymacro.h
|
||||||
|
// skipped pymath.h
|
||||||
|
mod pymem;
|
||||||
|
mod pyport;
|
||||||
|
mod pystate;
|
||||||
|
mod pythonrun;
|
||||||
|
// skipped pystrhex.h
|
||||||
|
// skipped pystrcmp.h
|
||||||
|
mod pystrtod;
|
||||||
|
// skipped pythread.h
|
||||||
|
// skipped pytime.h
|
||||||
|
mod rangeobject;
|
||||||
|
mod setobject;
|
||||||
|
mod sliceobject;
|
||||||
|
mod structseq;
|
||||||
|
mod sysmodule;
|
||||||
|
mod traceback;
|
||||||
|
// skipped tracemalloc.h
|
||||||
|
mod tupleobject;
|
||||||
|
mod typeslots;
|
||||||
|
mod unicodeobject;
|
||||||
|
mod warnings;
|
||||||
|
mod weakrefobject;
|
||||||
|
|
||||||
|
// Additional headers that are not exported by Python.h
|
||||||
|
pub mod structmember;
|
||||||
|
|
||||||
|
// "Limited API" definitions matching Python's `include/cpython` directory.
|
||||||
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
|
mod cpython;
|
||||||
|
|
||||||
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
|
pub use self::cpython::*;
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
use crate::ffi::pyport::Py_ssize_t;
|
use crate::pyport::Py_ssize_t;
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
|
||||||
#[cfg_attr(windows, link(name = "pythonXY"))]
|
#[cfg_attr(windows, link(name = "pythonXY"))]
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
use crate::ffi::pyport::Py_ssize_t;
|
use crate::pyport::Py_ssize_t;
|
||||||
use libc::size_t;
|
use libc::size_t;
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
use std::os::raw::c_uchar;
|
use std::os::raw::c_uchar;
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
use crate::ffi::pyport::Py_ssize_t;
|
use crate::pyport::Py_ssize_t;
|
||||||
use std::os::raw::{c_char, c_int};
|
use std::os::raw::{c_char, c_int};
|
||||||
|
|
||||||
// skipped non-limited _PyManagedBuffer_Type
|
// skipped non-limited _PyManagedBuffer_Type
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::ffi::object::{PyObject, PyTypeObject, Py_TYPE};
|
use crate::object::{PyObject, PyTypeObject, Py_TYPE};
|
||||||
#[cfg(Py_3_9)]
|
#[cfg(Py_3_9)]
|
||||||
use crate::ffi::PyObject_TypeCheck;
|
use crate::PyObject_TypeCheck;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::os::raw::{c_char, c_int};
|
use std::os::raw::{c_char, c_int};
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ pub type PyCFunction =
|
||||||
pub type _PyCFunctionFast = unsafe extern "C" fn(
|
pub type _PyCFunctionFast = unsafe extern "C" fn(
|
||||||
slf: *mut PyObject,
|
slf: *mut PyObject,
|
||||||
args: *mut *mut PyObject,
|
args: *mut *mut PyObject,
|
||||||
nargs: crate::ffi::pyport::Py_ssize_t,
|
nargs: crate::pyport::Py_ssize_t,
|
||||||
kwnames: *mut PyObject,
|
kwnames: *mut PyObject,
|
||||||
) -> *mut PyObject;
|
) -> *mut PyObject;
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ pub type PyCFunctionWithKeywords = unsafe extern "C" fn(
|
||||||
pub type _PyCFunctionFastWithKeywords = unsafe extern "C" fn(
|
pub type _PyCFunctionFastWithKeywords = unsafe extern "C" fn(
|
||||||
slf: *mut PyObject,
|
slf: *mut PyObject,
|
||||||
args: *const *mut PyObject,
|
args: *const *mut PyObject,
|
||||||
nargs: crate::ffi::pyport::Py_ssize_t,
|
nargs: crate::pyport::Py_ssize_t,
|
||||||
kwnames: *mut PyObject,
|
kwnames: *mut PyObject,
|
||||||
) -> *mut PyObject;
|
) -> *mut PyObject;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::ffi::methodobject::PyMethodDef;
|
use crate::methodobject::PyMethodDef;
|
||||||
use crate::ffi::moduleobject::PyModuleDef;
|
use crate::moduleobject::PyModuleDef;
|
||||||
use crate::ffi::object::PyObject;
|
use crate::object::PyObject;
|
||||||
use crate::ffi::pyport::Py_ssize_t;
|
use crate::pyport::Py_ssize_t;
|
||||||
use std::os::raw::{c_char, c_int, c_long};
|
use std::os::raw::{c_char, c_int, c_long};
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::ffi::methodobject::PyMethodDef;
|
use crate::methodobject::PyMethodDef;
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
use crate::ffi::pyport::Py_ssize_t;
|
use crate::pyport::Py_ssize_t;
|
||||||
use std::os::raw::{c_char, c_int, c_void};
|
use std::os::raw::{c_char, c_int, c_void};
|
||||||
|
|
||||||
#[cfg_attr(windows, link(name = "pythonXY"))]
|
#[cfg_attr(windows, link(name = "pythonXY"))]
|
|
@ -1,6 +1,6 @@
|
||||||
// FFI note: this file changed a lot between 3.6 and 3.10.
|
// FFI note: this file changed a lot between 3.6 and 3.10.
|
||||||
// Some missing definitions may not be marked "skipped".
|
// Some missing definitions may not be marked "skipped".
|
||||||
use crate::ffi::pyport::{Py_hash_t, Py_ssize_t};
|
use crate::pyport::{Py_hash_t, Py_ssize_t};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::os::raw::{c_char, c_int, c_uint, c_ulong, c_void};
|
use std::os::raw::{c_char, c_int, c_uint, c_ulong, c_void};
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
@ -9,7 +9,7 @@ use std::ptr;
|
||||||
opaque_struct!(PyTypeObject);
|
opaque_struct!(PyTypeObject);
|
||||||
|
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
pub use crate::ffi::cpython::object::PyTypeObject;
|
pub use crate::cpython::object::PyTypeObject;
|
||||||
|
|
||||||
// _PyObject_HEAD_EXTRA: conditionally defined in PyObject_HEAD_INIT
|
// _PyObject_HEAD_EXTRA: conditionally defined in PyObject_HEAD_INIT
|
||||||
// _PyObject_EXTRA_INIT: conditionally defined in PyObject_HEAD_INIT
|
// _PyObject_EXTRA_INIT: conditionally defined in PyObject_HEAD_INIT
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
use crate::ffi::pyport::Py_ssize_t;
|
use crate::pyport::Py_ssize_t;
|
||||||
use libc::size_t;
|
use libc::size_t;
|
||||||
use std::os::raw::{c_int, c_void};
|
use std::os::raw::{c_int, c_void};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::ffi::object::PyObject;
|
use crate::object::PyObject;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn PyOS_FSPath(path: *mut PyObject) -> *mut PyObject;
|
pub fn PyOS_FSPath(path: *mut PyObject) -> *mut PyObject;
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
use std::os::raw::{c_char, c_int, c_void};
|
use std::os::raw::{c_char, c_int, c_void};
|
||||||
|
|
||||||
#[cfg_attr(windows, link(name = "pythonXY"))]
|
#[cfg_attr(windows, link(name = "pythonXY"))]
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
use crate::ffi::pyport::Py_ssize_t;
|
use crate::pyport::Py_ssize_t;
|
||||||
use std::os::raw::{c_char, c_int};
|
use std::os::raw::{c_char, c_int};
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
@ -158,7 +158,7 @@ pub unsafe fn PyUnicodeDecodeError_Create(
|
||||||
end: Py_ssize_t,
|
end: Py_ssize_t,
|
||||||
_reason: *const c_char,
|
_reason: *const c_char,
|
||||||
) -> *mut PyObject {
|
) -> *mut PyObject {
|
||||||
crate::ffi::PyObject_CallFunction(
|
crate::PyObject_CallFunction(
|
||||||
PyExc_UnicodeDecodeError,
|
PyExc_UnicodeDecodeError,
|
||||||
std::ffi::CStr::from_bytes_with_nul(b"sy#nns\0")
|
std::ffi::CStr::from_bytes_with_nul(b"sy#nns\0")
|
||||||
.unwrap()
|
.unwrap()
|
|
@ -1,5 +1,5 @@
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
use crate::ffi::PyFrameObject;
|
use crate::PyFrameObject;
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
|
||||||
#[cfg(Py_LIMITED_API)]
|
#[cfg(Py_LIMITED_API)]
|
|
@ -1,5 +1,5 @@
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
use crate::ffi::pyport::{Py_hash_t, Py_ssize_t};
|
use crate::pyport::{Py_hash_t, Py_ssize_t};
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
use std::os::raw::{c_char, c_void};
|
use std::os::raw::{c_char, c_void};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::ffi::pystate::PyThreadState;
|
use crate::pystate::PyThreadState;
|
||||||
|
|
||||||
use libc::wchar_t;
|
use libc::wchar_t;
|
||||||
use std::os::raw::{c_char, c_int};
|
use std::os::raw::{c_char, c_int};
|
|
@ -1,6 +1,6 @@
|
||||||
#[cfg(not(PyPy))]
|
#[cfg(not(PyPy))]
|
||||||
use crate::ffi::moduleobject::PyModuleDef;
|
use crate::moduleobject::PyModuleDef;
|
||||||
use crate::ffi::object::PyObject;
|
use crate::object::PyObject;
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
|
||||||
#[cfg(not(PyPy))]
|
#[cfg(not(PyPy))]
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::ffi::object::PyObject;
|
use crate::object::PyObject;
|
||||||
use std::os::raw::{c_char, c_double, c_int};
|
use std::os::raw::{c_char, c_double, c_int};
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
#[cfg(all(not(Py_LIMITED_API), not(Py_3_10)))]
|
#[cfg(all(not(Py_LIMITED_API), not(Py_3_10)))]
|
||||||
use libc::FILE;
|
use libc::FILE;
|
||||||
#[cfg(any(Py_LIMITED_API, not(Py_3_10)))]
|
#[cfg(any(Py_LIMITED_API, not(Py_3_10)))]
|
||||||
|
@ -47,7 +47,7 @@ opaque_struct!(_node);
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn PyParser_SimpleParseString(s: *const c_char, b: c_int) -> *mut _node {
|
pub unsafe fn PyParser_SimpleParseString(s: *const c_char, b: c_int) -> *mut _node {
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
crate::ffi::PyParser_SimpleParseStringFlags(s, b, 0)
|
crate::PyParser_SimpleParseStringFlags(s, b, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(not(Py_LIMITED_API), not(Py_3_10)))]
|
#[cfg(all(not(Py_LIMITED_API), not(Py_3_10)))]
|
||||||
|
@ -55,7 +55,7 @@ pub unsafe fn PyParser_SimpleParseString(s: *const c_char, b: c_int) -> *mut _no
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn PyParser_SimpleParseFile(fp: *mut FILE, s: *const c_char, b: c_int) -> *mut _node {
|
pub unsafe fn PyParser_SimpleParseFile(fp: *mut FILE, s: *const c_char, b: c_int) -> *mut _node {
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
crate::ffi::PyParser_SimpleParseFileFlags(fp, s, b, 0)
|
crate::PyParser_SimpleParseFileFlags(fp, s, b, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
|
||||||
#[cfg_attr(windows, link(name = "pythonXY"))]
|
#[cfg_attr(windows, link(name = "pythonXY"))]
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
use crate::ffi::pyport::Py_hash_t;
|
use crate::pyport::Py_hash_t;
|
||||||
use crate::ffi::pyport::Py_ssize_t;
|
use crate::pyport::Py_ssize_t;
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
|
||||||
pub const PySet_MINSIZE: usize = 8;
|
pub const PySet_MINSIZE: usize = 8;
|
||||||
|
@ -120,7 +120,7 @@ pub unsafe fn PyAnySet_Check(ob: *mut PyObject) -> c_int {
|
||||||
#[inline]
|
#[inline]
|
||||||
#[cfg(Py_3_10)]
|
#[cfg(Py_3_10)]
|
||||||
pub unsafe fn PySet_CheckExact(op: *mut PyObject) -> c_int {
|
pub unsafe fn PySet_CheckExact(op: *mut PyObject) -> c_int {
|
||||||
crate::ffi::Py_IS_TYPE(op, &mut PySet_Type)
|
crate::Py_IS_TYPE(op, &mut PySet_Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
use crate::ffi::pyport::Py_ssize_t;
|
use crate::pyport::Py_ssize_t;
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::ffi::object::PyObject;
|
use crate::object::PyObject;
|
||||||
use crate::ffi::pyport::Py_ssize_t;
|
use crate::pyport::Py_ssize_t;
|
||||||
use std::os::raw::{c_char, c_int};
|
use std::os::raw::{c_char, c_int};
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::ffi::object::{PyObject, PyTypeObject};
|
use crate::object::{PyObject, PyTypeObject};
|
||||||
#[cfg(not(PyPy))]
|
#[cfg(not(PyPy))]
|
||||||
use crate::ffi::pyport::Py_ssize_t;
|
use crate::pyport::Py_ssize_t;
|
||||||
use std::os::raw::{c_char, c_int};
|
use std::os::raw::{c_char, c_int};
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
@ -40,18 +40,18 @@ extern "C" {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
pub type PyStructSequence = crate::ffi::PyTupleObject;
|
pub type PyStructSequence = crate::PyTupleObject;
|
||||||
|
|
||||||
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn PyStructSequence_SET_ITEM(op: *mut PyObject, i: Py_ssize_t, v: *mut PyObject) {
|
pub unsafe fn PyStructSequence_SET_ITEM(op: *mut PyObject, i: Py_ssize_t, v: *mut PyObject) {
|
||||||
crate::ffi::PyTuple_SET_ITEM(op, i, v)
|
crate::PyTuple_SET_ITEM(op, i, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn PyStructSequence_GET_ITEM(op: *mut PyObject, i: Py_ssize_t) -> *mut PyObject {
|
pub unsafe fn PyStructSequence_GET_ITEM(op: *mut PyObject, i: Py_ssize_t) -> *mut PyObject {
|
||||||
crate::ffi::PyTuple_GET_ITEM(op, i)
|
crate::PyTuple_GET_ITEM(op, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::ffi::object::PyObject;
|
use crate::object::PyObject;
|
||||||
use libc::wchar_t;
|
use libc::wchar_t;
|
||||||
use std::os::raw::{c_char, c_int};
|
use std::os::raw::{c_char, c_int};
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#[cfg_attr(PyPy, link_name = "PyPyTraceBack_Here")]
|
#[cfg_attr(PyPy, link_name = "PyPyTraceBack_Here")]
|
||||||
pub fn PyTraceBack_Here(arg1: *mut crate::ffi::PyFrameObject) -> c_int;
|
pub fn PyTraceBack_Here(arg1: *mut crate::PyFrameObject) -> c_int;
|
||||||
#[cfg_attr(PyPy, link_name = "PyPyTraceBack_Print")]
|
#[cfg_attr(PyPy, link_name = "PyPyTraceBack_Print")]
|
||||||
pub fn PyTraceBack_Print(arg1: *mut PyObject, arg2: *mut PyObject) -> c_int;
|
pub fn PyTraceBack_Print(arg1: *mut PyObject, arg2: *mut PyObject) -> c_int;
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
use crate::ffi::pyport::Py_ssize_t;
|
use crate::pyport::Py_ssize_t;
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
|
||||||
#[cfg_attr(windows, link(name = "pythonXY"))]
|
#[cfg_attr(windows, link(name = "pythonXY"))]
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
use crate::ffi::pyport::Py_ssize_t;
|
use crate::pyport::Py_ssize_t;
|
||||||
use libc::wchar_t;
|
use libc::wchar_t;
|
||||||
use std::os::raw::{c_char, c_int, c_void};
|
use std::os::raw::{c_char, c_int, c_void};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::ffi::object::PyObject;
|
use crate::object::PyObject;
|
||||||
use crate::ffi::pyport::Py_ssize_t;
|
use crate::pyport::Py_ssize_t;
|
||||||
use std::os::raw::{c_char, c_int};
|
use std::os::raw::{c_char, c_int};
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::ffi::object::*;
|
use crate::object::*;
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
|
||||||
opaque_struct!(PyWeakReference);
|
opaque_struct!(PyWeakReference);
|
|
@ -1,15 +0,0 @@
|
||||||
rust python3 ffi
|
|
||||||
================
|
|
||||||
|
|
||||||
[Rust](https://www.rust-lang.org/) FFI declarations for Python 3.
|
|
||||||
Supports the PEP 384 stable ABI for Python 3.4 or higher.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
This [cargo -sys package](https://doc.rust-lang.org/cargo/reference/build-scripts.html#-sys-packages) provides `python3` declarations.
|
|
||||||
Licensed under the Python license (see `LICENSE`).
|
|
||||||
|
|
||||||
For a safe high-level API, see [PyO3](https://github.com/PyO3/PyO3).
|
|
||||||
|
|
||||||
Documentation for the Python API is available on [https://docs.python.org/3/c-api/].
|
|
||||||
|
|
186
src/ffi/mod.rs
186
src/ffi/mod.rs
|
@ -20,190 +20,12 @@
|
||||||
//! for more information.
|
//! for more information.
|
||||||
//!
|
//!
|
||||||
//! [capi]: https://docs.python.org/3/c-api/index.html
|
//! [capi]: https://docs.python.org/3/c-api/index.html
|
||||||
#![allow(
|
|
||||||
missing_docs,
|
|
||||||
non_camel_case_types,
|
|
||||||
non_snake_case,
|
|
||||||
non_upper_case_globals,
|
|
||||||
clippy::upper_case_acronyms,
|
|
||||||
clippy::missing_safety_doc
|
|
||||||
)]
|
|
||||||
|
|
||||||
// Until `extern type` is stabilized, use the recommended approach to
|
#[cfg(all(not(Py_LIMITED_API), test))]
|
||||||
// model opaque types:
|
mod tests;
|
||||||
// https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs
|
|
||||||
macro_rules! opaque_struct {
|
|
||||||
($name:ident) => {
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct $name([u8; 0]);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub use self::abstract_::*;
|
// reexport raw bindings exposed in pyo3_ffi
|
||||||
pub use self::bltinmodule::*;
|
pub use pyo3_ffi::*;
|
||||||
pub use self::boolobject::*;
|
|
||||||
pub use self::bytearrayobject::*;
|
|
||||||
pub use self::bytesobject::*;
|
|
||||||
pub use self::ceval::*;
|
|
||||||
pub use self::code::*;
|
|
||||||
pub use self::codecs::*;
|
|
||||||
pub use self::compile::*;
|
|
||||||
pub use self::complexobject::*;
|
|
||||||
#[cfg(all(Py_3_8, not(Py_LIMITED_API)))]
|
|
||||||
pub use self::context::*;
|
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
|
||||||
pub use self::datetime::*;
|
|
||||||
pub use self::descrobject::*;
|
|
||||||
pub use self::dictobject::*;
|
|
||||||
pub use self::enumobject::*;
|
|
||||||
pub use self::eval::*;
|
|
||||||
pub use self::fileobject::*;
|
|
||||||
pub use self::fileutils::*;
|
|
||||||
pub use self::floatobject::*;
|
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
|
||||||
pub use self::funcobject::*;
|
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
|
||||||
pub use self::genobject::*;
|
|
||||||
pub use self::import::*;
|
|
||||||
pub use self::intrcheck::*;
|
|
||||||
pub use self::iterobject::*;
|
|
||||||
pub use self::listobject::*;
|
|
||||||
pub use self::longobject::*;
|
|
||||||
pub use self::marshal::*;
|
|
||||||
pub use self::memoryobject::*;
|
|
||||||
pub use self::methodobject::*;
|
|
||||||
pub use self::modsupport::*;
|
|
||||||
pub use self::moduleobject::*;
|
|
||||||
pub use self::object::*;
|
|
||||||
pub use self::objimpl::*;
|
|
||||||
pub use self::osmodule::*;
|
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
|
||||||
pub use self::pyarena::*;
|
|
||||||
pub use self::pycapsule::*;
|
|
||||||
pub use self::pyerrors::*;
|
|
||||||
pub use self::pyframe::*;
|
|
||||||
pub use self::pyhash::*;
|
|
||||||
pub use self::pylifecycle::*;
|
|
||||||
pub use self::pymem::*;
|
|
||||||
pub use self::pyport::*;
|
|
||||||
pub use self::pystate::*;
|
|
||||||
pub use self::pystrtod::*;
|
|
||||||
pub use self::pythonrun::*;
|
|
||||||
pub use self::rangeobject::*;
|
|
||||||
pub use self::setobject::*;
|
|
||||||
pub use self::sliceobject::*;
|
|
||||||
pub use self::structseq::*;
|
|
||||||
pub use self::sysmodule::*;
|
|
||||||
pub use self::traceback::*;
|
|
||||||
pub use self::tupleobject::*;
|
|
||||||
pub use self::typeslots::*;
|
|
||||||
pub use self::unicodeobject::*;
|
|
||||||
pub use self::warnings::*;
|
|
||||||
pub use self::weakrefobject::*;
|
|
||||||
|
|
||||||
mod abstract_;
|
|
||||||
// skipped asdl.h
|
|
||||||
// skipped ast.h
|
|
||||||
mod bltinmodule;
|
|
||||||
mod boolobject;
|
|
||||||
mod bytearrayobject;
|
|
||||||
mod bytesobject;
|
|
||||||
// skipped cellobject.h
|
|
||||||
mod ceval;
|
|
||||||
// skipped classobject.h
|
|
||||||
mod code;
|
|
||||||
mod codecs;
|
|
||||||
mod compile;
|
|
||||||
mod complexobject;
|
|
||||||
#[cfg(all(Py_3_8, not(Py_LIMITED_API)))]
|
|
||||||
mod context; // It's actually 3.7.1, but no cfg for patches.
|
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
|
||||||
pub(crate) mod datetime;
|
|
||||||
mod descrobject;
|
|
||||||
mod dictobject;
|
|
||||||
// skipped dynamic_annotations.h
|
|
||||||
mod enumobject;
|
|
||||||
// skipped errcode.h
|
|
||||||
mod eval;
|
|
||||||
// skipped exports.h
|
|
||||||
mod fileobject;
|
|
||||||
mod fileutils;
|
|
||||||
mod floatobject;
|
|
||||||
// skipped empty frameobject.h
|
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
|
||||||
pub(crate) mod funcobject;
|
|
||||||
// skipped genericaliasobject.h
|
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
|
||||||
mod genobject;
|
|
||||||
mod import;
|
|
||||||
// skipped interpreteridobject.h
|
|
||||||
mod intrcheck;
|
|
||||||
mod iterobject;
|
|
||||||
mod listobject;
|
|
||||||
// skipped longintrepr.h
|
|
||||||
mod longobject;
|
|
||||||
pub(crate) mod marshal;
|
|
||||||
mod memoryobject;
|
|
||||||
mod methodobject;
|
|
||||||
mod modsupport;
|
|
||||||
mod moduleobject;
|
|
||||||
// skipped namespaceobject.h
|
|
||||||
mod object;
|
|
||||||
mod objimpl;
|
|
||||||
// skipped odictobject.h
|
|
||||||
// skipped opcode.h
|
|
||||||
// skipped osdefs.h
|
|
||||||
mod osmodule;
|
|
||||||
// skipped parser_interface.h
|
|
||||||
// skipped patchlevel.h
|
|
||||||
// skipped picklebufobject.h
|
|
||||||
// skipped pyctype.h
|
|
||||||
// skipped py_curses.h
|
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
|
||||||
mod pyarena;
|
|
||||||
mod pycapsule;
|
|
||||||
// skipped pydecimal.h
|
|
||||||
// skipped pydtrace.h
|
|
||||||
mod pyerrors;
|
|
||||||
// skipped pyexpat.h
|
|
||||||
// skipped pyfpe.h
|
|
||||||
mod pyframe;
|
|
||||||
mod pyhash;
|
|
||||||
mod pylifecycle;
|
|
||||||
// skipped pymacconfig.h
|
|
||||||
// skipped pymacro.h
|
|
||||||
// skipped pymath.h
|
|
||||||
mod pymem;
|
|
||||||
mod pyport;
|
|
||||||
mod pystate;
|
|
||||||
mod pythonrun;
|
|
||||||
// skipped pystrhex.h
|
|
||||||
// skipped pystrcmp.h
|
|
||||||
mod pystrtod;
|
|
||||||
// skipped pythread.h
|
|
||||||
// skipped pytime.h
|
|
||||||
mod rangeobject;
|
|
||||||
mod setobject;
|
|
||||||
mod sliceobject;
|
|
||||||
mod structseq;
|
|
||||||
mod sysmodule;
|
|
||||||
mod traceback;
|
|
||||||
// skipped tracemalloc.h
|
|
||||||
mod tupleobject;
|
|
||||||
mod typeslots;
|
|
||||||
mod unicodeobject;
|
|
||||||
mod warnings;
|
|
||||||
mod weakrefobject;
|
|
||||||
|
|
||||||
// Additional headers that are not exported by Python.h
|
|
||||||
pub mod structmember;
|
|
||||||
|
|
||||||
// "Limited API" definitions matching Python's `include/cpython` directory.
|
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
|
||||||
mod cpython;
|
|
||||||
|
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
|
||||||
pub use self::cpython::*;
|
|
||||||
|
|
||||||
/// Helper to enable #\[pymethods\] to see the workaround for __ipow__ on Python 3.7
|
/// Helper to enable #\[pymethods\] to see the workaround for __ipow__ on Python 3.7
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
|
|
@ -0,0 +1,183 @@
|
||||||
|
use crate::ffi::*;
|
||||||
|
use crate::{types::PyDict, AsPyPointer, IntoPy, Py, PyAny, Python};
|
||||||
|
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
use crate::types::PyString;
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
use libc::wchar_t;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_datetime_fromtimestamp() {
|
||||||
|
Python::with_gil(|py| {
|
||||||
|
let args: Py<PyAny> = (100,).into_py(py);
|
||||||
|
unsafe { PyDateTime_IMPORT() };
|
||||||
|
let dt: &PyAny = unsafe { py.from_owned_ptr(PyDateTime_FromTimestamp(args.as_ptr())) };
|
||||||
|
let locals = PyDict::new(py);
|
||||||
|
locals.set_item("dt", dt).unwrap();
|
||||||
|
py.run(
|
||||||
|
"import datetime; assert dt == datetime.datetime.fromtimestamp(100)",
|
||||||
|
None,
|
||||||
|
Some(locals),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_date_fromtimestamp() {
|
||||||
|
Python::with_gil(|py| {
|
||||||
|
let args: Py<PyAny> = (100,).into_py(py);
|
||||||
|
unsafe { PyDateTime_IMPORT() };
|
||||||
|
let dt: &PyAny = unsafe { py.from_owned_ptr(PyDate_FromTimestamp(args.as_ptr())) };
|
||||||
|
let locals = PyDict::new(py);
|
||||||
|
locals.set_item("dt", dt).unwrap();
|
||||||
|
py.run(
|
||||||
|
"import datetime; assert dt == datetime.date.fromtimestamp(100)",
|
||||||
|
None,
|
||||||
|
Some(locals),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(not(all(PyPy, not(Py_3_8))))]
|
||||||
|
fn test_utc_timezone() {
|
||||||
|
Python::with_gil(|py| {
|
||||||
|
let utc_timezone = unsafe {
|
||||||
|
PyDateTime_IMPORT();
|
||||||
|
&*(&PyDateTime_TimeZone_UTC() as *const *mut crate::ffi::PyObject
|
||||||
|
as *const crate::PyObject)
|
||||||
|
};
|
||||||
|
let locals = PyDict::new(py);
|
||||||
|
locals.set_item("utc_timezone", utc_timezone).unwrap();
|
||||||
|
py.run(
|
||||||
|
"import datetime; assert utc_timezone is datetime.timezone.utc",
|
||||||
|
None,
|
||||||
|
Some(locals),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
#[test]
|
||||||
|
fn ascii_object_bitfield() {
|
||||||
|
let ob_base: PyObject = unsafe { std::mem::zeroed() };
|
||||||
|
|
||||||
|
let mut o = PyASCIIObject {
|
||||||
|
ob_base,
|
||||||
|
length: 0,
|
||||||
|
hash: 0,
|
||||||
|
state: 0,
|
||||||
|
wstr: std::ptr::null_mut() as *mut wchar_t,
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
assert_eq!(o.interned(), 0);
|
||||||
|
assert_eq!(o.kind(), 0);
|
||||||
|
assert_eq!(o.compact(), 0);
|
||||||
|
assert_eq!(o.ascii(), 0);
|
||||||
|
assert_eq!(o.ready(), 0);
|
||||||
|
|
||||||
|
for i in 0..4 {
|
||||||
|
o.state = i;
|
||||||
|
assert_eq!(o.interned(), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..8 {
|
||||||
|
o.state = i << 2;
|
||||||
|
assert_eq!(o.kind(), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
o.state = 1 << 5;
|
||||||
|
assert_eq!(o.compact(), 1);
|
||||||
|
|
||||||
|
o.state = 1 << 6;
|
||||||
|
assert_eq!(o.ascii(), 1);
|
||||||
|
|
||||||
|
o.state = 1 << 7;
|
||||||
|
assert_eq!(o.ready(), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
#[test]
|
||||||
|
#[cfg_attr(Py_3_10, allow(deprecated))]
|
||||||
|
fn ascii() {
|
||||||
|
Python::with_gil(|py| {
|
||||||
|
// This test relies on implementation details of PyString.
|
||||||
|
let s = PyString::new(py, "hello, world");
|
||||||
|
let ptr = s.as_ptr();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let ascii_ptr = ptr as *mut PyASCIIObject;
|
||||||
|
let ascii = ascii_ptr.as_ref().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(ascii.interned(), 0);
|
||||||
|
assert_eq!(ascii.kind(), PyUnicode_1BYTE_KIND);
|
||||||
|
assert_eq!(ascii.compact(), 1);
|
||||||
|
assert_eq!(ascii.ascii(), 1);
|
||||||
|
assert_eq!(ascii.ready(), 1);
|
||||||
|
|
||||||
|
assert_eq!(PyUnicode_IS_ASCII(ptr), 1);
|
||||||
|
assert_eq!(PyUnicode_IS_COMPACT(ptr), 1);
|
||||||
|
assert_eq!(PyUnicode_IS_COMPACT_ASCII(ptr), 1);
|
||||||
|
|
||||||
|
assert!(!PyUnicode_1BYTE_DATA(ptr).is_null());
|
||||||
|
// 2 and 4 byte macros return nonsense for this string instance.
|
||||||
|
assert_eq!(PyUnicode_KIND(ptr), PyUnicode_1BYTE_KIND);
|
||||||
|
|
||||||
|
assert!(!_PyUnicode_COMPACT_DATA(ptr).is_null());
|
||||||
|
// _PyUnicode_NONCOMPACT_DATA isn't valid for compact strings.
|
||||||
|
assert!(!PyUnicode_DATA(ptr).is_null());
|
||||||
|
|
||||||
|
assert_eq!(PyUnicode_GET_LENGTH(ptr), s.len().unwrap() as _);
|
||||||
|
assert_eq!(PyUnicode_IS_READY(ptr), 1);
|
||||||
|
|
||||||
|
// This has potential to mutate object. But it should be a no-op since
|
||||||
|
// we're already ready.
|
||||||
|
assert_eq!(PyUnicode_READY(ptr), 0);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
#[test]
|
||||||
|
#[cfg_attr(Py_3_10, allow(deprecated))]
|
||||||
|
fn ucs4() {
|
||||||
|
Python::with_gil(|py| {
|
||||||
|
let s = "哈哈🐈";
|
||||||
|
let py_string = PyString::new(py, s);
|
||||||
|
let ptr = py_string.as_ptr();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let ascii_ptr = ptr as *mut PyASCIIObject;
|
||||||
|
let ascii = ascii_ptr.as_ref().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(ascii.interned(), 0);
|
||||||
|
assert_eq!(ascii.kind(), PyUnicode_4BYTE_KIND);
|
||||||
|
assert_eq!(ascii.compact(), 1);
|
||||||
|
assert_eq!(ascii.ascii(), 0);
|
||||||
|
assert_eq!(ascii.ready(), 1);
|
||||||
|
|
||||||
|
assert_eq!(PyUnicode_IS_ASCII(ptr), 0);
|
||||||
|
assert_eq!(PyUnicode_IS_COMPACT(ptr), 1);
|
||||||
|
assert_eq!(PyUnicode_IS_COMPACT_ASCII(ptr), 0);
|
||||||
|
|
||||||
|
assert!(!PyUnicode_4BYTE_DATA(ptr).is_null());
|
||||||
|
assert_eq!(PyUnicode_KIND(ptr), PyUnicode_4BYTE_KIND);
|
||||||
|
|
||||||
|
assert!(!_PyUnicode_COMPACT_DATA(ptr).is_null());
|
||||||
|
// _PyUnicode_NONCOMPACT_DATA isn't valid for compact strings.
|
||||||
|
assert!(!PyUnicode_DATA(ptr).is_null());
|
||||||
|
|
||||||
|
assert_eq!(PyUnicode_GET_LENGTH(ptr), py_string.len().unwrap() as _);
|
||||||
|
assert_eq!(PyUnicode_IS_READY(ptr), 1);
|
||||||
|
|
||||||
|
// This has potential to mutate object. But it should be a no-op since
|
||||||
|
// we're already ready.
|
||||||
|
assert_eq!(PyUnicode_READY(ptr), 0);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -5,10 +5,9 @@
|
||||||
|
|
||||||
use crate::err::PyResult;
|
use crate::err::PyResult;
|
||||||
use crate::ffi;
|
use crate::ffi;
|
||||||
#[cfg(PyPy)]
|
use crate::ffi::{
|
||||||
use crate::ffi::datetime::{PyDateTime_FromTimestamp, PyDate_FromTimestamp};
|
PyDateTime_CAPI, PyDateTime_FromTimestamp, PyDateTime_IMPORT, PyDate_FromTimestamp,
|
||||||
use crate::ffi::PyDateTimeAPI;
|
};
|
||||||
use crate::ffi::{PyDateTime_Check, PyDate_Check, PyDelta_Check, PyTZInfo_Check, PyTime_Check};
|
|
||||||
#[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};
|
||||||
use crate::ffi::{
|
use crate::ffi::{
|
||||||
|
@ -26,8 +25,64 @@ use crate::ffi::{
|
||||||
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;
|
||||||
#[cfg(not(PyPy))]
|
|
||||||
use std::ptr;
|
fn ensure_datetime_api(_py: Python) -> &'static PyDateTime_CAPI {
|
||||||
|
unsafe {
|
||||||
|
if pyo3_ffi::PyDateTimeAPI().is_null() {
|
||||||
|
PyDateTime_IMPORT()
|
||||||
|
}
|
||||||
|
|
||||||
|
&*pyo3_ffi::PyDateTimeAPI()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type Check macros
|
||||||
|
//
|
||||||
|
// These are bindings around the C API typecheck macros, all of them return
|
||||||
|
// `1` if True and `0` if False. In all type check macros, the argument (`op`)
|
||||||
|
// must not be `NULL`. The implementations here all call ensure_datetime_api
|
||||||
|
// to ensure that the PyDateTimeAPI is initalized before use
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// # Safety
|
||||||
|
//
|
||||||
|
// These functions must only be called when the GIL is held!
|
||||||
|
|
||||||
|
macro_rules! ffi_fun_with_autoinit {
|
||||||
|
($(#[$outer:meta] unsafe fn $name: ident($arg: ident: *mut PyObject) -> $ret: ty;)*) => {
|
||||||
|
$(
|
||||||
|
#[$outer]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Must only be called while the GIL is held
|
||||||
|
unsafe fn $name($arg: *mut crate::ffi::PyObject) -> $ret {
|
||||||
|
|
||||||
|
let _ = ensure_datetime_api(Python::assume_gil_acquired());
|
||||||
|
crate::ffi::$name($arg)
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ffi_fun_with_autoinit! {
|
||||||
|
/// Check if `op` is a `PyDateTimeAPI.DateType` or subtype.
|
||||||
|
unsafe fn PyDate_Check(op: *mut PyObject) -> c_int;
|
||||||
|
|
||||||
|
/// Check if `op` is a `PyDateTimeAPI.DateTimeType` or subtype.
|
||||||
|
unsafe fn PyDateTime_Check(op: *mut PyObject) -> c_int;
|
||||||
|
|
||||||
|
/// Check if `op` is a `PyDateTimeAPI.TimeType` or subtype.
|
||||||
|
unsafe fn PyTime_Check(op: *mut PyObject) -> c_int;
|
||||||
|
|
||||||
|
/// Check if `op` is a `PyDateTimeAPI.DetaType` or subtype.
|
||||||
|
unsafe fn PyDelta_Check(op: *mut PyObject) -> c_int;
|
||||||
|
|
||||||
|
/// Check if `op` is a `PyDateTimeAPI.TZInfoType` or subtype.
|
||||||
|
unsafe fn PyTZInfo_Check(op: *mut PyObject) -> c_int;
|
||||||
|
}
|
||||||
|
|
||||||
// Access traits
|
// Access traits
|
||||||
|
|
||||||
|
@ -111,7 +166,7 @@ pub struct PyDate(PyAny);
|
||||||
pyobject_native_type!(
|
pyobject_native_type!(
|
||||||
PyDate,
|
PyDate,
|
||||||
crate::ffi::PyDateTime_Date,
|
crate::ffi::PyDateTime_Date,
|
||||||
*PyDateTimeAPI.DateType,
|
*ensure_datetime_api(Python::assume_gil_acquired()).DateType,
|
||||||
#module=Some("datetime"),
|
#module=Some("datetime"),
|
||||||
#checkfunction=PyDate_Check
|
#checkfunction=PyDate_Check
|
||||||
);
|
);
|
||||||
|
@ -120,11 +175,11 @@ impl PyDate {
|
||||||
/// Creates a new `datetime.date`.
|
/// Creates a new `datetime.date`.
|
||||||
pub fn new(py: Python, year: i32, month: u8, day: u8) -> PyResult<&PyDate> {
|
pub fn new(py: Python, year: i32, month: u8, day: u8) -> PyResult<&PyDate> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = (PyDateTimeAPI.Date_FromDate)(
|
let ptr = (ensure_datetime_api(py).Date_FromDate)(
|
||||||
year,
|
year,
|
||||||
c_int::from(month),
|
c_int::from(month),
|
||||||
c_int::from(day),
|
c_int::from(day),
|
||||||
PyDateTimeAPI.DateType,
|
ensure_datetime_api(py).DateType,
|
||||||
);
|
);
|
||||||
py.from_owned_ptr_or_err(ptr)
|
py.from_owned_ptr_or_err(ptr)
|
||||||
}
|
}
|
||||||
|
@ -136,14 +191,11 @@ impl PyDate {
|
||||||
pub fn from_timestamp(py: Python, timestamp: i64) -> PyResult<&PyDate> {
|
pub fn from_timestamp(py: Python, timestamp: i64) -> PyResult<&PyDate> {
|
||||||
let time_tuple = PyTuple::new(py, &[timestamp]);
|
let time_tuple = PyTuple::new(py, &[timestamp]);
|
||||||
|
|
||||||
|
// safety ensure that the API is loaded
|
||||||
|
let _api = ensure_datetime_api(py);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
#[cfg(PyPy)]
|
|
||||||
let ptr = PyDate_FromTimestamp(time_tuple.as_ptr());
|
let ptr = PyDate_FromTimestamp(time_tuple.as_ptr());
|
||||||
|
|
||||||
#[cfg(not(PyPy))]
|
|
||||||
let ptr =
|
|
||||||
(PyDateTimeAPI.Date_FromTimestamp)(PyDateTimeAPI.DateType, time_tuple.as_ptr());
|
|
||||||
|
|
||||||
py.from_owned_ptr_or_err(ptr)
|
py.from_owned_ptr_or_err(ptr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -169,7 +221,7 @@ pub struct PyDateTime(PyAny);
|
||||||
pyobject_native_type!(
|
pyobject_native_type!(
|
||||||
PyDateTime,
|
PyDateTime,
|
||||||
crate::ffi::PyDateTime_DateTime,
|
crate::ffi::PyDateTime_DateTime,
|
||||||
*PyDateTimeAPI.DateTimeType,
|
*ensure_datetime_api(Python::assume_gil_acquired()).DateType,
|
||||||
#module=Some("datetime"),
|
#module=Some("datetime"),
|
||||||
#checkfunction=PyDateTime_Check
|
#checkfunction=PyDateTime_Check
|
||||||
);
|
);
|
||||||
|
@ -187,8 +239,9 @@ impl PyDateTime {
|
||||||
microsecond: u32,
|
microsecond: u32,
|
||||||
tzinfo: Option<&PyObject>,
|
tzinfo: Option<&PyObject>,
|
||||||
) -> PyResult<&'p PyDateTime> {
|
) -> PyResult<&'p PyDateTime> {
|
||||||
|
let api = ensure_datetime_api(py);
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = (PyDateTimeAPI.DateTime_FromDateAndTime)(
|
let ptr = (api.DateTime_FromDateAndTime)(
|
||||||
year,
|
year,
|
||||||
c_int::from(month),
|
c_int::from(month),
|
||||||
c_int::from(day),
|
c_int::from(day),
|
||||||
|
@ -197,7 +250,7 @@ impl PyDateTime {
|
||||||
c_int::from(second),
|
c_int::from(second),
|
||||||
microsecond as c_int,
|
microsecond as c_int,
|
||||||
opt_to_pyobj(py, tzinfo),
|
opt_to_pyobj(py, tzinfo),
|
||||||
PyDateTimeAPI.DateTimeType,
|
api.DateTimeType,
|
||||||
);
|
);
|
||||||
py.from_owned_ptr_or_err(ptr)
|
py.from_owned_ptr_or_err(ptr)
|
||||||
}
|
}
|
||||||
|
@ -219,8 +272,9 @@ impl PyDateTime {
|
||||||
tzinfo: Option<&PyObject>,
|
tzinfo: Option<&PyObject>,
|
||||||
fold: bool,
|
fold: bool,
|
||||||
) -> PyResult<&'p PyDateTime> {
|
) -> PyResult<&'p PyDateTime> {
|
||||||
|
let api = ensure_datetime_api(py);
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = (PyDateTimeAPI.DateTime_FromDateAndTimeAndFold)(
|
let ptr = (api.DateTime_FromDateAndTimeAndFold)(
|
||||||
year,
|
year,
|
||||||
c_int::from(month),
|
c_int::from(month),
|
||||||
c_int::from(day),
|
c_int::from(day),
|
||||||
|
@ -230,7 +284,7 @@ impl PyDateTime {
|
||||||
microsecond as c_int,
|
microsecond as c_int,
|
||||||
opt_to_pyobj(py, tzinfo),
|
opt_to_pyobj(py, tzinfo),
|
||||||
c_int::from(fold),
|
c_int::from(fold),
|
||||||
PyDateTimeAPI.DateTimeType,
|
api.DateTimeType,
|
||||||
);
|
);
|
||||||
py.from_owned_ptr_or_err(ptr)
|
py.from_owned_ptr_or_err(ptr)
|
||||||
}
|
}
|
||||||
|
@ -253,19 +307,11 @@ impl PyDateTime {
|
||||||
|
|
||||||
let args = PyTuple::new(py, &[timestamp, time_zone_info]);
|
let args = PyTuple::new(py, &[timestamp, time_zone_info]);
|
||||||
|
|
||||||
|
// safety ensure API is loaded
|
||||||
|
let _api = ensure_datetime_api(py);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
#[cfg(PyPy)]
|
|
||||||
let ptr = PyDateTime_FromTimestamp(args.as_ptr());
|
let ptr = PyDateTime_FromTimestamp(args.as_ptr());
|
||||||
|
|
||||||
#[cfg(not(PyPy))]
|
|
||||||
let ptr = {
|
|
||||||
(PyDateTimeAPI.DateTime_FromTimestamp)(
|
|
||||||
PyDateTimeAPI.DateTimeType,
|
|
||||||
args.as_ptr(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
py.from_owned_ptr_or_err(ptr)
|
py.from_owned_ptr_or_err(ptr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -314,7 +360,7 @@ pub struct PyTime(PyAny);
|
||||||
pyobject_native_type!(
|
pyobject_native_type!(
|
||||||
PyTime,
|
PyTime,
|
||||||
crate::ffi::PyDateTime_Time,
|
crate::ffi::PyDateTime_Time,
|
||||||
*PyDateTimeAPI.TimeType,
|
*ensure_datetime_api(Python::assume_gil_acquired()).TimeType,
|
||||||
#module=Some("datetime"),
|
#module=Some("datetime"),
|
||||||
#checkfunction=PyTime_Check
|
#checkfunction=PyTime_Check
|
||||||
);
|
);
|
||||||
|
@ -329,14 +375,15 @@ impl PyTime {
|
||||||
microsecond: u32,
|
microsecond: u32,
|
||||||
tzinfo: Option<&PyObject>,
|
tzinfo: Option<&PyObject>,
|
||||||
) -> PyResult<&'p PyTime> {
|
) -> PyResult<&'p PyTime> {
|
||||||
|
let api = ensure_datetime_api(py);
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = (PyDateTimeAPI.Time_FromTime)(
|
let ptr = (api.Time_FromTime)(
|
||||||
c_int::from(hour),
|
c_int::from(hour),
|
||||||
c_int::from(minute),
|
c_int::from(minute),
|
||||||
c_int::from(second),
|
c_int::from(second),
|
||||||
microsecond as c_int,
|
microsecond as c_int,
|
||||||
opt_to_pyobj(py, tzinfo),
|
opt_to_pyobj(py, tzinfo),
|
||||||
PyDateTimeAPI.TimeType,
|
api.TimeType,
|
||||||
);
|
);
|
||||||
py.from_owned_ptr_or_err(ptr)
|
py.from_owned_ptr_or_err(ptr)
|
||||||
}
|
}
|
||||||
|
@ -353,15 +400,16 @@ impl PyTime {
|
||||||
tzinfo: Option<&PyObject>,
|
tzinfo: Option<&PyObject>,
|
||||||
fold: bool,
|
fold: bool,
|
||||||
) -> PyResult<&'p PyTime> {
|
) -> PyResult<&'p PyTime> {
|
||||||
|
let api = ensure_datetime_api(py);
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = (PyDateTimeAPI.Time_FromTimeAndFold)(
|
let ptr = (api.Time_FromTimeAndFold)(
|
||||||
c_int::from(hour),
|
c_int::from(hour),
|
||||||
c_int::from(minute),
|
c_int::from(minute),
|
||||||
c_int::from(second),
|
c_int::from(second),
|
||||||
microsecond as c_int,
|
microsecond as c_int,
|
||||||
opt_to_pyobj(py, tzinfo),
|
opt_to_pyobj(py, tzinfo),
|
||||||
fold as c_int,
|
fold as c_int,
|
||||||
PyDateTimeAPI.TimeType,
|
api.TimeType,
|
||||||
);
|
);
|
||||||
py.from_owned_ptr_or_err(ptr)
|
py.from_owned_ptr_or_err(ptr)
|
||||||
}
|
}
|
||||||
|
@ -399,7 +447,7 @@ pub struct PyTzInfo(PyAny);
|
||||||
pyobject_native_type!(
|
pyobject_native_type!(
|
||||||
PyTzInfo,
|
PyTzInfo,
|
||||||
crate::ffi::PyObject,
|
crate::ffi::PyObject,
|
||||||
*PyDateTimeAPI.TZInfoType,
|
*ensure_datetime_api(Python::assume_gil_acquired()).TZInfoType,
|
||||||
#module=Some("datetime"),
|
#module=Some("datetime"),
|
||||||
#checkfunction=PyTZInfo_Check
|
#checkfunction=PyTZInfo_Check
|
||||||
);
|
);
|
||||||
|
@ -410,7 +458,7 @@ pub struct PyDelta(PyAny);
|
||||||
pyobject_native_type!(
|
pyobject_native_type!(
|
||||||
PyDelta,
|
PyDelta,
|
||||||
crate::ffi::PyDateTime_Delta,
|
crate::ffi::PyDateTime_Delta,
|
||||||
*PyDateTimeAPI.DeltaType,
|
*ensure_datetime_api(Python::assume_gil_acquired()).DeltaType,
|
||||||
#module=Some("datetime"),
|
#module=Some("datetime"),
|
||||||
#checkfunction=PyDelta_Check
|
#checkfunction=PyDelta_Check
|
||||||
);
|
);
|
||||||
|
@ -424,13 +472,14 @@ impl PyDelta {
|
||||||
microseconds: i32,
|
microseconds: i32,
|
||||||
normalize: bool,
|
normalize: bool,
|
||||||
) -> PyResult<&PyDelta> {
|
) -> PyResult<&PyDelta> {
|
||||||
|
let api = ensure_datetime_api(py);
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = (PyDateTimeAPI.Delta_FromDelta)(
|
let ptr = (api.Delta_FromDelta)(
|
||||||
days as c_int,
|
days as c_int,
|
||||||
seconds as c_int,
|
seconds as c_int,
|
||||||
microseconds as c_int,
|
microseconds as c_int,
|
||||||
normalize as c_int,
|
normalize as c_int,
|
||||||
PyDateTimeAPI.DeltaType,
|
api.DeltaType,
|
||||||
);
|
);
|
||||||
py.from_owned_ptr_or_err(ptr)
|
py.from_owned_ptr_or_err(ptr)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
use pyo3::types::IntoPyDict;
|
use pyo3::types::IntoPyDict;
|
||||||
|
use pyo3_ffi::PyDateTime_IMPORT;
|
||||||
|
|
||||||
fn _get_subclasses<'p>(
|
fn _get_subclasses<'p>(
|
||||||
py: &'p Python,
|
py: &'p Python,
|
||||||
|
@ -57,7 +58,7 @@ fn test_date_check() {
|
||||||
let gil = Python::acquire_gil();
|
let gil = Python::acquire_gil();
|
||||||
let py = gil.python();
|
let py = gil.python();
|
||||||
let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, "date", "2018, 1, 1").unwrap();
|
let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, "date", "2018, 1, 1").unwrap();
|
||||||
|
unsafe { PyDateTime_IMPORT() }
|
||||||
assert_check_exact!(PyDate_Check, PyDate_CheckExact, obj);
|
assert_check_exact!(PyDate_Check, PyDate_CheckExact, obj);
|
||||||
assert_check_only!(PyDate_Check, PyDate_CheckExact, sub_obj);
|
assert_check_only!(PyDate_Check, PyDate_CheckExact, sub_obj);
|
||||||
assert_check_only!(PyDate_Check, PyDate_CheckExact, sub_sub_obj);
|
assert_check_only!(PyDate_Check, PyDate_CheckExact, sub_sub_obj);
|
||||||
|
@ -68,6 +69,7 @@ fn test_time_check() {
|
||||||
let gil = Python::acquire_gil();
|
let gil = Python::acquire_gil();
|
||||||
let py = gil.python();
|
let py = gil.python();
|
||||||
let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, "time", "12, 30, 15").unwrap();
|
let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, "time", "12, 30, 15").unwrap();
|
||||||
|
unsafe { PyDateTime_IMPORT() }
|
||||||
|
|
||||||
assert_check_exact!(PyTime_Check, PyTime_CheckExact, obj);
|
assert_check_exact!(PyTime_Check, PyTime_CheckExact, obj);
|
||||||
assert_check_only!(PyTime_Check, PyTime_CheckExact, sub_obj);
|
assert_check_only!(PyTime_Check, PyTime_CheckExact, sub_obj);
|
||||||
|
@ -81,6 +83,7 @@ fn test_datetime_check() {
|
||||||
let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, "datetime", "2018, 1, 1, 13, 30, 15")
|
let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, "datetime", "2018, 1, 1, 13, 30, 15")
|
||||||
.map_err(|e| e.print(py))
|
.map_err(|e| e.print(py))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
unsafe { PyDateTime_IMPORT() }
|
||||||
|
|
||||||
assert_check_only!(PyDate_Check, PyDate_CheckExact, obj);
|
assert_check_only!(PyDate_Check, PyDate_CheckExact, obj);
|
||||||
assert_check_exact!(PyDateTime_Check, PyDateTime_CheckExact, obj);
|
assert_check_exact!(PyDateTime_Check, PyDateTime_CheckExact, obj);
|
||||||
|
@ -93,6 +96,7 @@ fn test_delta_check() {
|
||||||
let gil = Python::acquire_gil();
|
let gil = Python::acquire_gil();
|
||||||
let py = gil.python();
|
let py = gil.python();
|
||||||
let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, "timedelta", "1, -3").unwrap();
|
let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, "timedelta", "1, -3").unwrap();
|
||||||
|
unsafe { PyDateTime_IMPORT() }
|
||||||
|
|
||||||
assert_check_exact!(PyDelta_Check, PyDelta_CheckExact, obj);
|
assert_check_exact!(PyDelta_Check, PyDelta_CheckExact, obj);
|
||||||
assert_check_only!(PyDelta_Check, PyDelta_CheckExact, sub_obj);
|
assert_check_only!(PyDelta_Check, PyDelta_CheckExact, sub_obj);
|
||||||
|
|
|
@ -107,6 +107,7 @@ fn llvm_cov_command(args: &[&str]) -> Command {
|
||||||
"--package=pyo3-build-config",
|
"--package=pyo3-build-config",
|
||||||
"--package=pyo3-macros-backend",
|
"--package=pyo3-macros-backend",
|
||||||
"--package=pyo3-macros",
|
"--package=pyo3-macros",
|
||||||
|
"--package=pyo3-ffi",
|
||||||
])
|
])
|
||||||
.args(args);
|
.args(args);
|
||||||
command
|
command
|
||||||
|
|
Loading…
Reference in New Issue