Merge pull request #2126 from DSPOM2/main
move ffi module to separate crate
This commit is contained in:
commit
199cc989d4
|
@ -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.
|
||||
|
||||
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)
|
||||
- [`src/instance.rs`] and [`src/types`]
|
||||
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
|
||||
|
||||
[`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
|
||||
[`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 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)]`.
|
||||
`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
|
||||
|
@ -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-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-ffi`]: https://github.com/PyO3/pyo3/tree/main/pyo3-ffi
|
||||
|
||||
<!-- 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)
|
||||
- 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)
|
||||
- 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
|
||||
|
||||
|
@ -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)
|
||||
- `__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)
|
||||
- `_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
|
||||
|
||||
|
|
16
Cargo.toml
16
Cargo.toml
|
@ -19,6 +19,9 @@ cfg-if = "1.0"
|
|||
libc = "0.2.62"
|
||||
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
|
||||
pyo3-macros = { path = "pyo3-macros", version = "=0.15.1", 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.
|
||||
# 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 = []
|
||||
extension-module = ["pyo3-ffi/extension-module"]
|
||||
|
||||
# Use the Python limited API. See https://www.python.org/dev/peps/pep-0384/ for more.
|
||||
abi3 = ["pyo3-build-config/abi3"]
|
||||
abi3 = ["pyo3-build-config/abi3", "pyo3-ffi/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"]
|
||||
abi3-py37 = ["abi3-py38", "pyo3-build-config/abi3-py37", "pyo3-ffi/abi3-py37"]
|
||||
abi3-py38 = ["abi3-py39", "pyo3-build-config/abi3-py38", "pyo3-ffi/abi3-py38"]
|
||||
abi3-py39 = ["abi3-py310", "pyo3-build-config/abi3-py39", "pyo3-ffi/abi3-py39"]
|
||||
abi3-py310 = ["abi3", "pyo3-build-config/abi3-py310", "pyo3-ffi/abi3-py310"]
|
||||
|
||||
# Changes `Python::with_gil` and `Python::acquire_gil` to automatically initialize the
|
||||
# Python interpreter if needed.
|
||||
|
@ -126,6 +129,7 @@ harness = false
|
|||
|
||||
[workspace]
|
||||
members = [
|
||||
"pyo3-ffi",
|
||||
"pyo3-macros",
|
||||
"pyo3-macros-backend",
|
||||
"pytests/pyo3-benchmarks",
|
||||
|
|
103
build.rs
103
build.rs
|
@ -1,48 +1,7 @@
|
|||
use std::{env, process::Command};
|
||||
|
||||
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: 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(())
|
||||
}
|
||||
use pyo3_build_config::pyo3_build_script_impl::{cargo_env_var, errors::Result};
|
||||
use pyo3_build_config::{bail, InterpreterConfig};
|
||||
|
||||
fn ensure_auto_initialize_ok(interpreter_config: &InterpreterConfig) -> Result<()> {
|
||||
if cargo_env_var("CARGO_FEATURE_AUTO_INITIALIZE").is_some() {
|
||||
|
@ -84,36 +43,6 @@ fn rustc_minor_version() -> Option<u32> {
|
|||
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.
|
||||
///
|
||||
/// 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
|
||||
/// 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)?;
|
||||
ensure_auto_initialize_ok(&interpreter_config)?;
|
||||
|
||||
if !interpreter_config.suppress_build_script_link_lines {
|
||||
emit_link_config(&interpreter_config)?;
|
||||
}
|
||||
let interpreter_config = pyo3_build_config::get();
|
||||
|
||||
interpreter_config.emit_pyo3_cfgs();
|
||||
|
||||
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
|
||||
if rustc_minor_version >= 51 {
|
||||
|
@ -150,22 +68,9 @@ fn configure_pyo3() -> Result<()> {
|
|||
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(())
|
||||
}
|
||||
|
||||
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());
|
||||
|
|
|
@ -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::ffi::pyport::Py_ssize_t;
|
||||
use crate::object::*;
|
||||
use crate::pyport::Py_ssize_t;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
use std::ptr;
|
||||
|
||||
|
@ -96,10 +96,9 @@ extern "C" {
|
|||
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||
#[inline]
|
||||
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) => {
|
||||
tp_iternext as *const std::os::raw::c_void
|
||||
!= crate::ffi::_PyObject_NextNotImplemented as _
|
||||
tp_iternext as *const std::os::raw::c_void != crate::_PyObject_NextNotImplemented as _
|
||||
}
|
||||
None => false,
|
||||
}) as c_int
|
|
@ -1,4 +1,4 @@
|
|||
use crate::ffi::object::PyTypeObject;
|
||||
use crate::object::PyTypeObject;
|
||||
|
||||
#[cfg_attr(windows, link(name = "pythonXY"))]
|
||||
extern "C" {
|
|
@ -1,5 +1,5 @@
|
|||
use crate::ffi::longobject::PyLongObject;
|
||||
use crate::ffi::object::*;
|
||||
use crate::longobject::PyLongObject;
|
||||
use crate::object::*;
|
||||
use std::os::raw::{c_int, c_long};
|
||||
|
||||
#[cfg_attr(windows, link(name = "pythonXY"))]
|
|
@ -1,5 +1,5 @@
|
|||
use crate::ffi::object::*;
|
||||
use crate::ffi::pyport::Py_ssize_t;
|
||||
use crate::object::*;
|
||||
use crate::pyport::Py_ssize_t;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
#[cfg_attr(windows, link(name = "pythonXY"))]
|
|
@ -1,5 +1,5 @@
|
|||
use crate::ffi::object::*;
|
||||
use crate::ffi::pyport::Py_ssize_t;
|
||||
use crate::object::*;
|
||||
use crate::pyport::Py_ssize_t;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
#[cfg_attr(windows, link(name = "pythonXY"))]
|
|
@ -1,5 +1,5 @@
|
|||
use crate::ffi::object::PyObject;
|
||||
use crate::ffi::pystate::PyThreadState;
|
||||
use crate::object::PyObject;
|
||||
use crate::pystate::PyThreadState;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
|
||||
extern "C" {
|
||||
|
@ -37,7 +37,7 @@ extern "C" {
|
|||
pub fn PyEval_GetGlobals() -> *mut PyObject;
|
||||
#[cfg_attr(PyPy, link_name = "PyPyEval_GetLocals")]
|
||||
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")]
|
||||
pub fn Py_AddPendingCall(
|
||||
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_GetFuncDesc(arg1: *mut PyObject) -> *const c_char;
|
||||
pub fn PyEval_GetCallStats(arg1: *mut PyObject) -> *mut PyObject;
|
||||
pub fn PyEval_EvalFrame(arg1: *mut crate::ffi::PyFrameObject) -> *mut PyObject;
|
||||
pub fn PyEval_EvalFrameEx(f: *mut crate::ffi::PyFrameObject, exc: c_int) -> *mut PyObject;
|
||||
pub fn PyEval_EvalFrame(arg1: *mut crate::PyFrameObject) -> *mut PyObject;
|
||||
pub fn PyEval_EvalFrameEx(f: *mut crate::PyFrameObject, exc: c_int) -> *mut PyObject;
|
||||
#[cfg_attr(PyPy, link_name = "PyPyEval_SaveThread")]
|
||||
pub fn PyEval_SaveThread() -> *mut PyThreadState;
|
||||
#[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};
|
||||
|
||||
extern "C" {
|
|
@ -1,4 +1,4 @@
|
|||
use crate::ffi::object::*;
|
||||
use crate::object::*;
|
||||
use std::os::raw::{c_double, c_int};
|
||||
|
||||
#[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};
|
||||
|
||||
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};
|
||||
|
||||
#[cfg(all(Py_3_8, not(PyPy)))]
|
||||
use crate::ffi::{
|
||||
use crate::{
|
||||
pyport::PY_SSIZE_T_MAX, vectorcallfunc, PyCallable_Check, PyThreadState, PyThreadState_GET,
|
||||
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)]
|
||||
pub unsafe fn PyVectorcall_Function(callable: *mut PyObject) -> Option<vectorcallfunc> {
|
||||
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 {
|
||||
return None;
|
||||
}
|
||||
|
@ -218,7 +218,7 @@ extern "C" {
|
|||
#[cfg(not(any(Py_3_9, PyPy)))]
|
||||
#[inline]
|
||||
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
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::ffi::object::PyObject;
|
||||
use crate::ffi::pyport::Py_ssize_t;
|
||||
use crate::object::PyObject;
|
||||
use crate::pyport::Py_ssize_t;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
extern "C" {
|
|
@ -1,12 +1,9 @@
|
|||
use crate::ffi::cpython::pystate::Py_tracefunc;
|
||||
use crate::ffi::object::{freefunc, PyObject};
|
||||
use crate::cpython::pystate::Py_tracefunc;
|
||||
use crate::object::{freefunc, PyObject};
|
||||
use std::os::raw::c_int;
|
||||
|
||||
extern "C" {
|
||||
pub fn _PyEval_EvalFrameDefault(
|
||||
arg1: *mut crate::ffi::PyFrameObject,
|
||||
exc: c_int,
|
||||
) -> *mut PyObject;
|
||||
pub fn _PyEval_EvalFrameDefault(arg1: *mut crate::PyFrameObject, exc: c_int) -> *mut PyObject;
|
||||
pub fn _PyEval_RequestCodeExtraIndex(func: freefunc) -> c_int;
|
||||
pub fn PyEval_SetProfile(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::ffi::pyport::Py_ssize_t;
|
||||
use crate::object::*;
|
||||
use crate::pyport::Py_ssize_t;
|
||||
use std::os::raw::{c_char, c_int, c_uchar, c_void};
|
||||
|
||||
// skipped _Py_CODEUNIT
|
||||
|
@ -90,7 +90,7 @@ pub unsafe fn PyCode_Check(op: *mut PyObject) -> c_int {
|
|||
#[inline]
|
||||
#[cfg(not(PyPy))]
|
||||
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" {
|
|
@ -1,11 +1,11 @@
|
|||
#[cfg(not(Py_3_10))]
|
||||
use crate::ffi::object::PyObject;
|
||||
use crate::object::PyObject;
|
||||
#[cfg(not(Py_3_10))]
|
||||
use crate::ffi::pyarena::*;
|
||||
use crate::pyarena::*;
|
||||
#[cfg(not(Py_3_10))]
|
||||
use crate::ffi::pythonrun::*;
|
||||
use crate::pythonrun::*;
|
||||
#[cfg(not(Py_3_10))]
|
||||
use crate::ffi::PyCodeObject;
|
||||
use crate::PyCodeObject;
|
||||
#[cfg(not(Py_3_10))]
|
||||
use std::os::raw::c_char;
|
||||
use std::os::raw::c_int;
|
|
@ -1,5 +1,5 @@
|
|||
use crate::ffi::object::*;
|
||||
use crate::ffi::pyport::Py_ssize_t;
|
||||
use crate::object::*;
|
||||
use crate::pyport::Py_ssize_t;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
opaque_struct!(PyDictKeysObject);
|
||||
|
@ -24,7 +24,7 @@ extern "C" {
|
|||
mp: *mut PyObject,
|
||||
key: *mut PyObject,
|
||||
item: *mut PyObject,
|
||||
hash: crate::ffi::Py_hash_t,
|
||||
hash: crate::Py_hash_t,
|
||||
) -> c_int;
|
||||
// skipped _PyDict_DelItem_KnownHash
|
||||
// skipped _PyDict_DelItemIf
|
||||
|
@ -34,7 +34,7 @@ extern "C" {
|
|||
pos: *mut Py_ssize_t,
|
||||
key: *mut *mut PyObject,
|
||||
value: *mut *mut PyObject,
|
||||
hash: *mut crate::ffi::Py_hash_t,
|
||||
hash: *mut crate::Py_hash_t,
|
||||
) -> c_int;
|
||||
// skipped PyDict_GET_SIZE
|
||||
// skipped _PyDict_Contains_KnownHash
|
|
@ -1,6 +1,6 @@
|
|||
use crate::ffi::cpython::code::{PyCodeObject, CO_MAXBLOCKS};
|
||||
use crate::ffi::object::*;
|
||||
use crate::ffi::pystate::PyThreadState;
|
||||
use crate::cpython::code::{PyCodeObject, CO_MAXBLOCKS};
|
||||
use crate::object::*;
|
||||
use crate::pystate::PyThreadState;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
// 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};
|
||||
|
||||
// skipped PyInit__imp
|
|
@ -1,6 +1,6 @@
|
|||
/* --- PyStatus ----------------------------------------------- */
|
||||
|
||||
use crate::ffi::Py_ssize_t;
|
||||
use crate::Py_ssize_t;
|
||||
use libc::wchar_t;
|
||||
use std::os::raw::{c_char, c_int, c_ulong};
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::ffi::object::*;
|
||||
use crate::ffi::pyport::Py_ssize_t;
|
||||
use crate::object::*;
|
||||
use crate::pyport::Py_ssize_t;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
|
@ -1,4 +1,4 @@
|
|||
use crate::ffi::PyObject;
|
||||
use crate::PyObject;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
// skipped _Py_NewReference
|
||||
|
@ -12,7 +12,7 @@ use std::os::raw::c_int;
|
|||
// skipped _Py_IDENTIFIER
|
||||
|
||||
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::ptr;
|
||||
|
||||
|
@ -24,7 +24,7 @@ mod bufferinfo {
|
|||
pub struct Py_buffer {
|
||||
pub buf: *mut c_void,
|
||||
/// Owned reference
|
||||
pub obj: *mut crate::ffi::PyObject,
|
||||
pub obj: *mut crate::PyObject,
|
||||
pub len: Py_ssize_t,
|
||||
pub itemsize: Py_ssize_t,
|
||||
pub readonly: c_int,
|
||||
|
@ -67,12 +67,12 @@ mod bufferinfo {
|
|||
}
|
||||
|
||||
pub type getbufferproc = unsafe extern "C" fn(
|
||||
arg1: *mut crate::ffi::PyObject,
|
||||
arg1: *mut crate::PyObject,
|
||||
arg2: *mut Py_buffer,
|
||||
arg3: c_int,
|
||||
) -> c_int;
|
||||
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
|
||||
pub const PyBUF_MAX_NDIM: c_int = 64;
|
||||
|
@ -117,8 +117,8 @@ pub type vectorcallfunc = unsafe extern "C" fn(
|
|||
) -> *mut PyObject;
|
||||
|
||||
mod typeobject {
|
||||
use crate::ffi::{self, object};
|
||||
use crate::ffi::{PyObject, Py_ssize_t};
|
||||
use crate::object;
|
||||
use crate::{PyObject, Py_ssize_t};
|
||||
use std::mem;
|
||||
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_iter: Option<object::getiterfunc>,
|
||||
pub tp_iternext: Option<object::iternextfunc>,
|
||||
pub tp_methods: *mut ffi::methodobject::PyMethodDef,
|
||||
pub tp_members: *mut ffi::structmember::PyMemberDef,
|
||||
pub tp_getset: *mut ffi::descrobject::PyGetSetDef,
|
||||
pub tp_methods: *mut crate::methodobject::PyMethodDef,
|
||||
pub tp_members: *mut crate::structmember::PyMemberDef,
|
||||
pub tp_getset: *mut crate::descrobject::PyGetSetDef,
|
||||
pub tp_base: *mut PyTypeObject,
|
||||
pub tp_dict: *mut object::PyObject,
|
||||
pub tp_descr_get: Option<object::descrgetfunc>,
|
||||
|
@ -310,10 +310,10 @@ mod typeobject {
|
|||
#[inline]
|
||||
pub unsafe fn PyHeapType_GET_MEMBERS(
|
||||
etype: *mut PyHeapTypeObject,
|
||||
) -> *mut ffi::structmember::PyMemberDef {
|
||||
) -> *mut crate::structmember::PyMemberDef {
|
||||
let py_type = object::Py_TYPE(etype as *mut object::PyObject);
|
||||
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 std::os::raw::{c_char, c_int};
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
#[cfg(not(PyPy))]
|
||||
use crate::ffi::PyThreadState;
|
||||
use crate::ffi::{PyFrameObject, PyInterpreterState, PyObject};
|
||||
use crate::PyThreadState;
|
||||
use crate::{PyFrameObject, PyInterpreterState, PyObject};
|
||||
use std::os::raw::c_int;
|
||||
|
||||
// skipped _PyInterpreterState_RequiresIDRef
|
||||
|
@ -60,10 +60,10 @@ extern "C" {
|
|||
|
||||
#[cfg(Py_3_9)]
|
||||
pub type _PyFrameEvalFunction = extern "C" fn(
|
||||
*mut crate::ffi::PyThreadState,
|
||||
*mut crate::ffi::PyFrameObject,
|
||||
*mut crate::PyThreadState,
|
||||
*mut crate::PyFrameObject,
|
||||
c_int,
|
||||
) -> *mut crate::ffi::object::PyObject;
|
||||
) -> *mut crate::object::PyObject;
|
||||
|
||||
#[cfg(Py_3_9)]
|
||||
extern "C" {
|
|
@ -1,9 +1,9 @@
|
|||
use crate::ffi::object::*;
|
||||
use crate::object::*;
|
||||
#[cfg(all(not(Py_LIMITED_API), not(Py_3_10)))]
|
||||
use crate::ffi::pyarena::PyArena;
|
||||
use crate::ffi::PyCompilerFlags;
|
||||
use crate::pyarena::PyArena;
|
||||
use crate::PyCompilerFlags;
|
||||
#[cfg(not(Py_3_10))]
|
||||
use crate::ffi::{_mod, _node};
|
||||
use crate::{_mod, _node};
|
||||
use libc::FILE;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::ffi::object::*;
|
||||
use crate::object::*;
|
||||
#[cfg(not(PyPy))]
|
||||
use crate::ffi::pyport::Py_ssize_t;
|
||||
use crate::pyport::Py_ssize_t;
|
||||
|
||||
#[repr(C)]
|
||||
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 std::os::raw::{c_char, c_int, c_uint, c_void};
|
||||
|
||||
|
@ -119,7 +119,7 @@ pub const SSTATE_INTERNED_IMMORTAL: c_uint = 2;
|
|||
#[inline]
|
||||
#[cfg(target_endian = "little")]
|
||||
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);
|
||||
|
||||
(*(op as *mut PyASCIIObject)).ascii()
|
||||
|
@ -170,7 +170,7 @@ pub unsafe fn PyUnicode_4BYTE_DATA(op: *mut PyObject) -> *mut Py_UCS4 {
|
|||
#[inline]
|
||||
#[cfg(target_endian = "little")]
|
||||
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);
|
||||
|
||||
(*(op as *mut PyASCIIObject)).kind()
|
||||
|
@ -197,7 +197,7 @@ pub unsafe fn _PyUnicode_NONCOMPACT_DATA(op: *mut PyObject) -> *mut c_void {
|
|||
#[inline]
|
||||
#[cfg(target_endian = "little")]
|
||||
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 {
|
||||
_PyUnicode_COMPACT_DATA(op)
|
||||
|
@ -213,7 +213,7 @@ pub unsafe fn PyUnicode_DATA(op: *mut PyObject) -> *mut c_void {
|
|||
#[inline]
|
||||
#[cfg(target_endian = "little")]
|
||||
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);
|
||||
|
||||
(*(op as *mut PyASCIIObject)).length
|
||||
|
@ -230,7 +230,7 @@ pub unsafe fn PyUnicode_IS_READY(op: *mut PyObject) -> c_uint {
|
|||
#[inline]
|
||||
#[cfg(target_endian = "little")]
|
||||
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 {
|
||||
0
|
||||
|
@ -480,130 +480,3 @@ extern "C" {
|
|||
// skipped _PyUnicode_FromId
|
||||
// skipped _PyUnicode_EQ
|
||||
// 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.
|
||||
//! `DateTime_FromTimestamp` and `Date_FromTimestamp` are currently not supported.
|
||||
|
||||
use crate::ffi::{PyObject, PyTypeObject};
|
||||
use crate::ffi::{PyObject_TypeCheck, Py_TYPE};
|
||||
use crate::once_cell::GILOnceCell;
|
||||
use crate::Python;
|
||||
use std::ops::Deref;
|
||||
use crate::{PyObject, PyObject_TypeCheck, PyTypeObject, Py_TYPE};
|
||||
use std::cell::UnsafeCell;
|
||||
use std::os::raw::{c_char, c_int, c_uchar};
|
||||
use std::ptr;
|
||||
#[cfg(not(PyPy))]
|
||||
use {
|
||||
crate::ffi::{PyCapsule_Import, Py_hash_t},
|
||||
crate::{PyCapsule_Import, Py_hash_t},
|
||||
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!
|
||||
unsafe impl Sync for PyDateTime_CAPI {}
|
||||
|
||||
/// Safe wrapper around the Python datetime C-API global. Note that this object differs slightly
|
||||
/// 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.
|
||||
/// Returns a pointer to a `PyDateTime_CAPI` instance
|
||||
///
|
||||
/// In the [`Deref`] implementation, if the underlying object has not yet been initialized, the GIL
|
||||
/// will automatically be acquired and [`PyDateTime_IMPORT()`] called.
|
||||
pub static PyDateTimeAPI: _PyDateTimeAPI_impl = _PyDateTimeAPI_impl {
|
||||
inner: GILOnceCell::new(),
|
||||
};
|
||||
/// # Note
|
||||
/// This function will return a null pointer until
|
||||
/// `PyDateTime_IMPORT` is called
|
||||
#[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))))]
|
||||
pub static PyDateTime_TimeZone_UTC: _PyDateTime_TimeZone_UTC_impl = _PyDateTime_TimeZone_UTC_impl {
|
||||
inner: &PyDateTimeAPI,
|
||||
};
|
||||
#[inline]
|
||||
pub unsafe fn PyDateTime_TimeZone_UTC() -> *mut PyObject {
|
||||
(*PyDateTimeAPI()).TimeZone_UTC
|
||||
}
|
||||
|
||||
/// Populates the `PyDateTimeAPI` object
|
||||
///
|
||||
/// Unlike in C, this does *not* need to be actively invoked in Rust, which
|
||||
/// will populate the `PyDateTimeAPI` struct automatically on first use.
|
||||
/// Use this function only if you want to eagerly load the datetime module,
|
||||
/// such as if you do not want the first call to a datetime function to be
|
||||
/// 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();
|
||||
pub unsafe fn PyDateTime_IMPORT() {
|
||||
// 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))]
|
||||
let py_datetime_c_api = {
|
||||
// PyDateTime_CAPSULE_NAME is a macro in C
|
||||
let PyDateTime_CAPSULE_NAME = CString::new("datetime.datetime_CAPI").unwrap();
|
||||
#[cfg(not(PyPy))]
|
||||
let py_datetime_c_api = {
|
||||
// PyDateTime_CAPSULE_NAME is a macro in C
|
||||
let PyDateTime_CAPSULE_NAME = CString::new("datetime.datetime_CAPI").unwrap();
|
||||
|
||||
&*(PyCapsule_Import(PyDateTime_CAPSULE_NAME.as_ptr(), 1)
|
||||
as *const PyDateTime_CAPI)
|
||||
};
|
||||
PyCapsule_Import(PyDateTime_CAPSULE_NAME.as_ptr(), 1) as *mut PyDateTime_CAPI
|
||||
};
|
||||
|
||||
py_datetime_c_api
|
||||
})
|
||||
})
|
||||
*PyDateTimeAPI_impl.0.get() = py_datetime_c_api;
|
||||
}
|
||||
|
||||
// skipped non-limited PyDateTime_TimeZone_UTC
|
||||
|
@ -502,61 +476,61 @@ pub unsafe fn PyDateTime_IMPORT() -> &'static PyDateTime_CAPI {
|
|||
#[inline]
|
||||
/// Check if `op` is a `PyDateTimeAPI.DateType` or subtype.
|
||||
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]
|
||||
/// Check if `op`'s type is exactly `PyDateTimeAPI.DateType`.
|
||||
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]
|
||||
/// Check if `op` is a `PyDateTimeAPI.DateTimeType` or subtype.
|
||||
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]
|
||||
/// Check if `op`'s type is exactly `PyDateTimeAPI.DateTimeType`.
|
||||
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]
|
||||
/// Check if `op` is a `PyDateTimeAPI.TimeType` or subtype.
|
||||
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]
|
||||
/// Check if `op`'s type is exactly `PyDateTimeAPI.TimeType`.
|
||||
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]
|
||||
/// Check if `op` is a `PyDateTimeAPI.DetaType` or subtype.
|
||||
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]
|
||||
/// Check if `op`'s type is exactly `PyDateTimeAPI.DeltaType`.
|
||||
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]
|
||||
/// Check if `op` is a `PyDateTimeAPI.TZInfoType` or subtype.
|
||||
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]
|
||||
/// Check if `op`'s type is exactly `PyDateTimeAPI.TZInfoType`.
|
||||
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
|
||||
|
@ -570,12 +544,14 @@ pub unsafe fn PyTZInfo_CheckExact(op: *mut PyObject) -> c_int {
|
|||
|
||||
#[cfg(not(PyPy))]
|
||||
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))]
|
||||
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)]
|
||||
|
@ -589,96 +565,13 @@ extern "C" {
|
|||
#[cfg(PyPy)]
|
||||
extern "C" {
|
||||
#[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)]
|
||||
pub struct _PyDateTimeAPI_impl {
|
||||
inner: GILOnceCell<&'static PyDateTime_CAPI>,
|
||||
}
|
||||
struct PyDateTimeAPISingleton(UnsafeCell<*mut PyDateTime_CAPI>);
|
||||
unsafe impl Sync for PyDateTimeAPISingleton {}
|
||||
|
||||
impl Deref for _PyDateTimeAPI_impl {
|
||||
type Target = PyDateTime_CAPI;
|
||||
|
||||
#[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();
|
||||
})
|
||||
}
|
||||
}
|
||||
static PyDateTimeAPI_impl: PyDateTimeAPISingleton =
|
||||
PyDateTimeAPISingleton(UnsafeCell::new(ptr::null_mut()));
|
|
@ -1,6 +1,6 @@
|
|||
use crate::ffi::methodobject::PyMethodDef;
|
||||
use crate::ffi::object::{PyObject, PyTypeObject};
|
||||
use crate::ffi::structmember::PyMemberDef;
|
||||
use crate::methodobject::PyMethodDef;
|
||||
use crate::object::{PyObject, PyTypeObject};
|
||||
use crate::structmember::PyMemberDef;
|
||||
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;
|
|
@ -1,5 +1,5 @@
|
|||
use crate::ffi::object::*;
|
||||
use crate::ffi::pyport::Py_ssize_t;
|
||||
use crate::object::*;
|
||||
use crate::pyport::Py_ssize_t;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
#[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"))]
|
||||
extern "C" {
|
|
@ -1,4 +1,4 @@
|
|||
use crate::ffi::object::PyObject;
|
||||
use crate::object::PyObject;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
extern "C" {
|
|
@ -1,4 +1,4 @@
|
|||
use crate::ffi::object::PyObject;
|
||||
use crate::object::PyObject;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
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 std::os::raw::c_char;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::ffi::object::*;
|
||||
use crate::object::*;
|
||||
use std::os::raw::{c_double, c_int};
|
||||
|
||||
#[cfg(Py_LIMITED_API)]
|
|
@ -1,6 +1,6 @@
|
|||
use std::os::raw::c_int;
|
||||
|
||||
use crate::ffi::object::{PyObject, PyTypeObject, Py_TYPE};
|
||||
use crate::object::{PyObject, PyTypeObject, Py_TYPE};
|
||||
|
||||
// skipped PyFunctionObject
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::ffi::object::*;
|
||||
use crate::ffi::pyport::Py_ssize_t;
|
||||
use crate::ffi::PyFrameObject;
|
||||
use crate::object::*;
|
||||
use crate::pyport::Py_ssize_t;
|
||||
use crate::PyFrameObject;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
#[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};
|
||||
|
||||
extern "C" {
|
|
@ -1,4 +1,4 @@
|
|||
use crate::ffi::object::*;
|
||||
use crate::object::*;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
#[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::ffi::pyport::Py_ssize_t;
|
||||
use crate::object::*;
|
||||
use crate::pyport::Py_ssize_t;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
#[cfg_attr(windows, link(name = "pythonXY"))]
|
|
@ -1,5 +1,5 @@
|
|||
use crate::ffi::object::*;
|
||||
use crate::ffi::pyport::Py_ssize_t;
|
||||
use crate::object::*;
|
||||
use crate::pyport::Py_ssize_t;
|
||||
use libc::size_t;
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
use std::os::raw::c_uchar;
|
|
@ -1,5 +1,5 @@
|
|||
use crate::ffi::object::*;
|
||||
use crate::ffi::pyport::Py_ssize_t;
|
||||
use crate::object::*;
|
||||
use crate::pyport::Py_ssize_t;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
// 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)]
|
||||
use crate::ffi::PyObject_TypeCheck;
|
||||
use crate::PyObject_TypeCheck;
|
||||
use std::mem;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
|
@ -35,8 +35,7 @@ pub type PyCFunction =
|
|||
pub type _PyCFunctionFast = unsafe extern "C" fn(
|
||||
slf: *mut PyObject,
|
||||
args: *mut *mut PyObject,
|
||||
nargs: crate::ffi::pyport::Py_ssize_t,
|
||||
kwnames: *mut PyObject,
|
||||
nargs: crate::pyport::Py_ssize_t,
|
||||
) -> *mut PyObject;
|
||||
|
||||
pub type PyCFunctionWithKeywords = unsafe extern "C" fn(
|
||||
|
@ -49,7 +48,7 @@ pub type PyCFunctionWithKeywords = unsafe extern "C" fn(
|
|||
pub type _PyCFunctionFastWithKeywords = unsafe extern "C" fn(
|
||||
slf: *mut PyObject,
|
||||
args: *const *mut PyObject,
|
||||
nargs: crate::ffi::pyport::Py_ssize_t,
|
||||
nargs: crate::pyport::Py_ssize_t,
|
||||
kwnames: *mut PyObject,
|
||||
) -> *mut PyObject;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::ffi::methodobject::PyMethodDef;
|
||||
use crate::ffi::moduleobject::PyModuleDef;
|
||||
use crate::ffi::object::PyObject;
|
||||
use crate::ffi::pyport::Py_ssize_t;
|
||||
use crate::methodobject::PyMethodDef;
|
||||
use crate::moduleobject::PyModuleDef;
|
||||
use crate::object::PyObject;
|
||||
use crate::pyport::Py_ssize_t;
|
||||
use std::os::raw::{c_char, c_int, c_long};
|
||||
|
||||
extern "C" {
|
|
@ -1,6 +1,6 @@
|
|||
use crate::ffi::methodobject::PyMethodDef;
|
||||
use crate::ffi::object::*;
|
||||
use crate::ffi::pyport::Py_ssize_t;
|
||||
use crate::methodobject::PyMethodDef;
|
||||
use crate::object::*;
|
||||
use crate::pyport::Py_ssize_t;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
|
||||
#[cfg_attr(windows, link(name = "pythonXY"))]
|
|
@ -1,6 +1,6 @@
|
|||
// FFI note: this file changed a lot between 3.6 and 3.10.
|
||||
// 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::os::raw::{c_char, c_int, c_uint, c_ulong, c_void};
|
||||
use std::ptr;
|
||||
|
@ -9,7 +9,7 @@ use std::ptr;
|
|||
opaque_struct!(PyTypeObject);
|
||||
|
||||
#[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_EXTRA_INIT: conditionally defined in PyObject_HEAD_INIT
|
|
@ -1,5 +1,5 @@
|
|||
use crate::ffi::object::*;
|
||||
use crate::ffi::pyport::Py_ssize_t;
|
||||
use crate::object::*;
|
||||
use crate::pyport::Py_ssize_t;
|
||||
use libc::size_t;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::ffi::object::PyObject;
|
||||
use crate::object::PyObject;
|
||||
|
||||
extern "C" {
|
||||
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};
|
||||
|
||||
#[cfg_attr(windows, link(name = "pythonXY"))]
|
|
@ -1,5 +1,5 @@
|
|||
use crate::ffi::object::*;
|
||||
use crate::ffi::pyport::Py_ssize_t;
|
||||
use crate::object::*;
|
||||
use crate::pyport::Py_ssize_t;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
#[repr(C)]
|
||||
|
@ -158,7 +158,7 @@ pub unsafe fn PyUnicodeDecodeError_Create(
|
|||
end: Py_ssize_t,
|
||||
_reason: *const c_char,
|
||||
) -> *mut PyObject {
|
||||
crate::ffi::PyObject_CallFunction(
|
||||
crate::PyObject_CallFunction(
|
||||
PyExc_UnicodeDecodeError,
|
||||
std::ffi::CStr::from_bytes_with_nul(b"sy#nns\0")
|
||||
.unwrap()
|
|
@ -1,5 +1,5 @@
|
|||
#[cfg(not(Py_LIMITED_API))]
|
||||
use crate::ffi::PyFrameObject;
|
||||
use crate::PyFrameObject;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
#[cfg(Py_LIMITED_API)]
|
|
@ -1,5 +1,5 @@
|
|||
#[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))]
|
||||
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 std::os::raw::{c_char, c_int};
|
|
@ -1,6 +1,6 @@
|
|||
#[cfg(not(PyPy))]
|
||||
use crate::ffi::moduleobject::PyModuleDef;
|
||||
use crate::ffi::object::PyObject;
|
||||
use crate::moduleobject::PyModuleDef;
|
||||
use crate::object::PyObject;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
#[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};
|
||||
|
||||
extern "C" {
|
|
@ -1,4 +1,4 @@
|
|||
use crate::ffi::object::*;
|
||||
use crate::object::*;
|
||||
#[cfg(all(not(Py_LIMITED_API), not(Py_3_10)))]
|
||||
use libc::FILE;
|
||||
#[cfg(any(Py_LIMITED_API, not(Py_3_10)))]
|
||||
|
@ -47,7 +47,7 @@ opaque_struct!(_node);
|
|||
#[inline]
|
||||
pub unsafe fn PyParser_SimpleParseString(s: *const c_char, b: c_int) -> *mut _node {
|
||||
#[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)))]
|
||||
|
@ -55,7 +55,7 @@ pub unsafe fn PyParser_SimpleParseString(s: *const c_char, b: c_int) -> *mut _no
|
|||
#[inline]
|
||||
pub unsafe fn PyParser_SimpleParseFile(fp: *mut FILE, s: *const c_char, b: c_int) -> *mut _node {
|
||||
#[allow(deprecated)]
|
||||
crate::ffi::PyParser_SimpleParseFileFlags(fp, s, b, 0)
|
||||
crate::PyParser_SimpleParseFileFlags(fp, s, b, 0)
|
||||
}
|
||||
|
||||
extern "C" {
|
|
@ -1,4 +1,4 @@
|
|||
use crate::ffi::object::*;
|
||||
use crate::object::*;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
#[cfg_attr(windows, link(name = "pythonXY"))]
|
|
@ -1,7 +1,7 @@
|
|||
use crate::ffi::object::*;
|
||||
use crate::object::*;
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
use crate::ffi::pyport::Py_hash_t;
|
||||
use crate::ffi::pyport::Py_ssize_t;
|
||||
use crate::pyport::Py_hash_t;
|
||||
use crate::pyport::Py_ssize_t;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
pub const PySet_MINSIZE: usize = 8;
|
||||
|
@ -120,7 +120,7 @@ pub unsafe fn PyAnySet_Check(ob: *mut PyObject) -> c_int {
|
|||
#[inline]
|
||||
#[cfg(Py_3_10)]
|
||||
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" {
|
|
@ -1,5 +1,5 @@
|
|||
use crate::ffi::object::*;
|
||||
use crate::ffi::pyport::Py_ssize_t;
|
||||
use crate::object::*;
|
||||
use crate::pyport::Py_ssize_t;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
extern "C" {
|
|
@ -1,5 +1,5 @@
|
|||
use crate::ffi::object::PyObject;
|
||||
use crate::ffi::pyport::Py_ssize_t;
|
||||
use crate::object::PyObject;
|
||||
use crate::pyport::Py_ssize_t;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
#[repr(C)]
|
|
@ -1,6 +1,6 @@
|
|||
use crate::ffi::object::{PyObject, PyTypeObject};
|
||||
use crate::object::{PyObject, PyTypeObject};
|
||||
#[cfg(not(PyPy))]
|
||||
use crate::ffi::pyport::Py_ssize_t;
|
||||
use crate::pyport::Py_ssize_t;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
#[repr(C)]
|
||||
|
@ -40,18 +40,18 @@ extern "C" {
|
|||
}
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
pub type PyStructSequence = crate::ffi::PyTupleObject;
|
||||
pub type PyStructSequence = crate::PyTupleObject;
|
||||
|
||||
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||
#[inline]
|
||||
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)))]
|
||||
#[inline]
|
||||
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" {
|
|
@ -1,4 +1,4 @@
|
|||
use crate::ffi::object::PyObject;
|
||||
use crate::object::PyObject;
|
||||
use libc::wchar_t;
|
||||
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;
|
||||
|
||||
extern "C" {
|
||||
#[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")]
|
||||
pub fn PyTraceBack_Print(arg1: *mut PyObject, arg2: *mut PyObject) -> c_int;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
use crate::ffi::object::*;
|
||||
use crate::ffi::pyport::Py_ssize_t;
|
||||
use crate::object::*;
|
||||
use crate::pyport::Py_ssize_t;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
#[cfg_attr(windows, link(name = "pythonXY"))]
|
|
@ -1,5 +1,5 @@
|
|||
use crate::ffi::object::*;
|
||||
use crate::ffi::pyport::Py_ssize_t;
|
||||
use crate::object::*;
|
||||
use crate::pyport::Py_ssize_t;
|
||||
use libc::wchar_t;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::ffi::object::PyObject;
|
||||
use crate::ffi::pyport::Py_ssize_t;
|
||||
use crate::object::PyObject;
|
||||
use crate::pyport::Py_ssize_t;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
extern "C" {
|
|
@ -1,4 +1,4 @@
|
|||
use crate::ffi::object::*;
|
||||
use crate::object::*;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
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.
|
||||
//!
|
||||
//! [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
|
||||
// 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]);
|
||||
};
|
||||
}
|
||||
#[cfg(all(not(Py_LIMITED_API), test))]
|
||||
mod tests;
|
||||
|
||||
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::*;
|
||||
// reexport raw bindings exposed in pyo3_ffi
|
||||
pub use pyo3_ffi::*;
|
||||
|
||||
/// Helper to enable #\[pymethods\] to see the workaround for __ipow__ on Python 3.7
|
||||
#[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::ffi;
|
||||
#[cfg(PyPy)]
|
||||
use crate::ffi::datetime::{PyDateTime_FromTimestamp, PyDate_FromTimestamp};
|
||||
use crate::ffi::PyDateTimeAPI;
|
||||
use crate::ffi::{PyDateTime_Check, PyDate_Check, PyDelta_Check, PyTZInfo_Check, PyTime_Check};
|
||||
use crate::ffi::{
|
||||
PyDateTime_CAPI, PyDateTime_FromTimestamp, PyDateTime_IMPORT, PyDate_FromTimestamp,
|
||||
};
|
||||
#[cfg(not(PyPy))]
|
||||
use crate::ffi::{PyDateTime_DATE_GET_FOLD, PyDateTime_TIME_GET_FOLD};
|
||||
use crate::ffi::{
|
||||
|
@ -26,8 +25,64 @@ use crate::ffi::{
|
|||
use crate::types::PyTuple;
|
||||
use crate::{AsPyPointer, PyAny, PyObject, Python, ToPyObject};
|
||||
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
|
||||
|
||||
|
@ -111,7 +166,7 @@ pub struct PyDate(PyAny);
|
|||
pyobject_native_type!(
|
||||
PyDate,
|
||||
crate::ffi::PyDateTime_Date,
|
||||
*PyDateTimeAPI.DateType,
|
||||
*ensure_datetime_api(Python::assume_gil_acquired()).DateType,
|
||||
#module=Some("datetime"),
|
||||
#checkfunction=PyDate_Check
|
||||
);
|
||||
|
@ -120,11 +175,11 @@ impl PyDate {
|
|||
/// Creates a new `datetime.date`.
|
||||
pub fn new(py: Python, year: i32, month: u8, day: u8) -> PyResult<&PyDate> {
|
||||
unsafe {
|
||||
let ptr = (PyDateTimeAPI.Date_FromDate)(
|
||||
let ptr = (ensure_datetime_api(py).Date_FromDate)(
|
||||
year,
|
||||
c_int::from(month),
|
||||
c_int::from(day),
|
||||
PyDateTimeAPI.DateType,
|
||||
ensure_datetime_api(py).DateType,
|
||||
);
|
||||
py.from_owned_ptr_or_err(ptr)
|
||||
}
|
||||
|
@ -136,14 +191,11 @@ impl PyDate {
|
|||
pub fn from_timestamp(py: Python, timestamp: i64) -> PyResult<&PyDate> {
|
||||
let time_tuple = PyTuple::new(py, &[timestamp]);
|
||||
|
||||
// safety ensure that the API is loaded
|
||||
let _api = ensure_datetime_api(py);
|
||||
|
||||
unsafe {
|
||||
#[cfg(PyPy)]
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -169,7 +221,7 @@ pub struct PyDateTime(PyAny);
|
|||
pyobject_native_type!(
|
||||
PyDateTime,
|
||||
crate::ffi::PyDateTime_DateTime,
|
||||
*PyDateTimeAPI.DateTimeType,
|
||||
*ensure_datetime_api(Python::assume_gil_acquired()).DateType,
|
||||
#module=Some("datetime"),
|
||||
#checkfunction=PyDateTime_Check
|
||||
);
|
||||
|
@ -187,8 +239,9 @@ impl PyDateTime {
|
|||
microsecond: u32,
|
||||
tzinfo: Option<&PyObject>,
|
||||
) -> PyResult<&'p PyDateTime> {
|
||||
let api = ensure_datetime_api(py);
|
||||
unsafe {
|
||||
let ptr = (PyDateTimeAPI.DateTime_FromDateAndTime)(
|
||||
let ptr = (api.DateTime_FromDateAndTime)(
|
||||
year,
|
||||
c_int::from(month),
|
||||
c_int::from(day),
|
||||
|
@ -197,7 +250,7 @@ impl PyDateTime {
|
|||
c_int::from(second),
|
||||
microsecond as c_int,
|
||||
opt_to_pyobj(py, tzinfo),
|
||||
PyDateTimeAPI.DateTimeType,
|
||||
api.DateTimeType,
|
||||
);
|
||||
py.from_owned_ptr_or_err(ptr)
|
||||
}
|
||||
|
@ -219,8 +272,9 @@ impl PyDateTime {
|
|||
tzinfo: Option<&PyObject>,
|
||||
fold: bool,
|
||||
) -> PyResult<&'p PyDateTime> {
|
||||
let api = ensure_datetime_api(py);
|
||||
unsafe {
|
||||
let ptr = (PyDateTimeAPI.DateTime_FromDateAndTimeAndFold)(
|
||||
let ptr = (api.DateTime_FromDateAndTimeAndFold)(
|
||||
year,
|
||||
c_int::from(month),
|
||||
c_int::from(day),
|
||||
|
@ -230,7 +284,7 @@ impl PyDateTime {
|
|||
microsecond as c_int,
|
||||
opt_to_pyobj(py, tzinfo),
|
||||
c_int::from(fold),
|
||||
PyDateTimeAPI.DateTimeType,
|
||||
api.DateTimeType,
|
||||
);
|
||||
py.from_owned_ptr_or_err(ptr)
|
||||
}
|
||||
|
@ -253,19 +307,11 @@ impl PyDateTime {
|
|||
|
||||
let args = PyTuple::new(py, &[timestamp, time_zone_info]);
|
||||
|
||||
// safety ensure API is loaded
|
||||
let _api = ensure_datetime_api(py);
|
||||
|
||||
unsafe {
|
||||
#[cfg(PyPy)]
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -314,7 +360,7 @@ pub struct PyTime(PyAny);
|
|||
pyobject_native_type!(
|
||||
PyTime,
|
||||
crate::ffi::PyDateTime_Time,
|
||||
*PyDateTimeAPI.TimeType,
|
||||
*ensure_datetime_api(Python::assume_gil_acquired()).TimeType,
|
||||
#module=Some("datetime"),
|
||||
#checkfunction=PyTime_Check
|
||||
);
|
||||
|
@ -329,14 +375,15 @@ impl PyTime {
|
|||
microsecond: u32,
|
||||
tzinfo: Option<&PyObject>,
|
||||
) -> PyResult<&'p PyTime> {
|
||||
let api = ensure_datetime_api(py);
|
||||
unsafe {
|
||||
let ptr = (PyDateTimeAPI.Time_FromTime)(
|
||||
let ptr = (api.Time_FromTime)(
|
||||
c_int::from(hour),
|
||||
c_int::from(minute),
|
||||
c_int::from(second),
|
||||
microsecond as c_int,
|
||||
opt_to_pyobj(py, tzinfo),
|
||||
PyDateTimeAPI.TimeType,
|
||||
api.TimeType,
|
||||
);
|
||||
py.from_owned_ptr_or_err(ptr)
|
||||
}
|
||||
|
@ -353,15 +400,16 @@ impl PyTime {
|
|||
tzinfo: Option<&PyObject>,
|
||||
fold: bool,
|
||||
) -> PyResult<&'p PyTime> {
|
||||
let api = ensure_datetime_api(py);
|
||||
unsafe {
|
||||
let ptr = (PyDateTimeAPI.Time_FromTimeAndFold)(
|
||||
let ptr = (api.Time_FromTimeAndFold)(
|
||||
c_int::from(hour),
|
||||
c_int::from(minute),
|
||||
c_int::from(second),
|
||||
microsecond as c_int,
|
||||
opt_to_pyobj(py, tzinfo),
|
||||
fold as c_int,
|
||||
PyDateTimeAPI.TimeType,
|
||||
api.TimeType,
|
||||
);
|
||||
py.from_owned_ptr_or_err(ptr)
|
||||
}
|
||||
|
@ -399,7 +447,7 @@ pub struct PyTzInfo(PyAny);
|
|||
pyobject_native_type!(
|
||||
PyTzInfo,
|
||||
crate::ffi::PyObject,
|
||||
*PyDateTimeAPI.TZInfoType,
|
||||
*ensure_datetime_api(Python::assume_gil_acquired()).TZInfoType,
|
||||
#module=Some("datetime"),
|
||||
#checkfunction=PyTZInfo_Check
|
||||
);
|
||||
|
@ -410,7 +458,7 @@ pub struct PyDelta(PyAny);
|
|||
pyobject_native_type!(
|
||||
PyDelta,
|
||||
crate::ffi::PyDateTime_Delta,
|
||||
*PyDateTimeAPI.DeltaType,
|
||||
*ensure_datetime_api(Python::assume_gil_acquired()).DeltaType,
|
||||
#module=Some("datetime"),
|
||||
#checkfunction=PyDelta_Check
|
||||
);
|
||||
|
@ -424,13 +472,14 @@ impl PyDelta {
|
|||
microseconds: i32,
|
||||
normalize: bool,
|
||||
) -> PyResult<&PyDelta> {
|
||||
let api = ensure_datetime_api(py);
|
||||
unsafe {
|
||||
let ptr = (PyDateTimeAPI.Delta_FromDelta)(
|
||||
let ptr = (api.Delta_FromDelta)(
|
||||
days as c_int,
|
||||
seconds as c_int,
|
||||
microseconds as c_int,
|
||||
normalize as c_int,
|
||||
PyDateTimeAPI.DeltaType,
|
||||
api.DeltaType,
|
||||
);
|
||||
py.from_owned_ptr_or_err(ptr)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::IntoPyDict;
|
||||
use pyo3_ffi::PyDateTime_IMPORT;
|
||||
|
||||
fn _get_subclasses<'p>(
|
||||
py: &'p Python,
|
||||
|
@ -57,7 +58,7 @@ fn test_date_check() {
|
|||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
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_only!(PyDate_Check, PyDate_CheckExact, 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 py = gil.python();
|
||||
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_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")
|
||||
.map_err(|e| e.print(py))
|
||||
.unwrap();
|
||||
unsafe { PyDateTime_IMPORT() }
|
||||
|
||||
assert_check_only!(PyDate_Check, PyDate_CheckExact, obj);
|
||||
assert_check_exact!(PyDateTime_Check, PyDateTime_CheckExact, obj);
|
||||
|
@ -93,6 +96,7 @@ fn test_delta_check() {
|
|||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
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_only!(PyDelta_Check, PyDelta_CheckExact, sub_obj);
|
||||
|
|
|
@ -107,6 +107,7 @@ fn llvm_cov_command(args: &[&str]) -> Command {
|
|||
"--package=pyo3-build-config",
|
||||
"--package=pyo3-macros-backend",
|
||||
"--package=pyo3-macros",
|
||||
"--package=pyo3-ffi",
|
||||
])
|
||||
.args(args);
|
||||
command
|
||||
|
|
Loading…
Reference in New Issue