Merge branch 'master' into iterator-example
This commit is contained in:
commit
c4d9ab227e
|
@ -3,41 +3,91 @@ name: Test
|
|||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
fmt:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v1
|
||||
- run: pip install black==19.10b0
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
- name: Check python formatting (black)
|
||||
run: black --check .
|
||||
- name: Check rust formatting (rustfmt)
|
||||
run: cargo fmt --all -- --check
|
||||
|
||||
clippy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
- run: make clippy
|
||||
|
||||
build:
|
||||
needs: [fmt] # don't wait for clippy as fails rarely and takes longer
|
||||
name: python${{ matrix.python-version }}-${{ matrix.platform.python-architecture }} ${{ matrix.platform.os }}
|
||||
runs-on: ${{ matrix.platform.os }}
|
||||
strategy:
|
||||
max-parallel: 12
|
||||
matrix:
|
||||
python-version: [3.5, 3.6, 3.7, 3.8]
|
||||
python-version: [3.5, 3.6, 3.7, 3.8, pypy3]
|
||||
platform: [
|
||||
{ os: "macOS-latest", python-architecture: "x64", rust-target: "x86_64-apple-darwin" },
|
||||
{ os: "windows-latest", python-architecture: "x64", rust-target: "x86_64-pc-windows-msvc" },
|
||||
{ os: "windows-latest", python-architecture: "x86", rust-target: "i686-pc-windows-msvc" },
|
||||
]
|
||||
exclude:
|
||||
# There is no 64-bit pypy on windows
|
||||
- python-version: pypy3
|
||||
platform: { os: "windows-latest", python-architecture: "x64" }
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install Rust
|
||||
architecture: ${{ matrix.platform.python-architecture }}
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
default: true
|
||||
target: ${{ matrix.platform.rust-target }}
|
||||
|
||||
- run: rustup set default-host ${{ matrix.platform.rust-target }}
|
||||
|
||||
- name: Build without default features
|
||||
run: cargo build --no-default-features --verbose
|
||||
|
||||
- name: Build with default features
|
||||
run: cargo build --verbose
|
||||
- name: Install test dependencies
|
||||
run: cargo build --features "num-bigint num-complex" --verbose
|
||||
|
||||
# Run tests (except on PyPy, because no embedding API).
|
||||
- if: matrix.python-version != 'pypy3'
|
||||
name: Test
|
||||
run: cargo test --features "num-bigint num-complex"
|
||||
|
||||
- name: Test proc-macro code
|
||||
run: cargo test --manifest-path=pyo3-derive-backend/Cargo.toml
|
||||
|
||||
- name: Install python test dependencies
|
||||
run: |
|
||||
python -m pip install -U pip setuptools
|
||||
pip install setuptools-rust pytest pytest-benchmark tox tox-venv
|
||||
- name: Test
|
||||
run: ci/actions/test.sh
|
||||
|
||||
- name: Test example extension modules
|
||||
shell: bash
|
||||
run: |
|
||||
for example_dir in examples/*; do
|
||||
cd $example_dir
|
||||
tox -c "tox.ini" -e py
|
||||
cd -
|
||||
done
|
||||
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
|
|
|
@ -16,8 +16,6 @@ jobs:
|
|||
python: "3.7"
|
||||
- name: Python 3.8
|
||||
python: "3.8"
|
||||
# Run clippy and rustfmt
|
||||
env: RUN_LINT=1
|
||||
- name: Python 3.9-dev
|
||||
python: "3.9-dev"
|
||||
- name: Nightly
|
||||
|
|
|
@ -21,6 +21,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||
- Rename `PYTHON_SYS_EXECUTABLE` to `PYO3_PYTHON`. The old name will continue to work but will be undocumented, and will be removed in a future release. [#1039](https://github.com/PyO3/pyo3/pull/1039)
|
||||
- `PyType::as_type_ptr` is no longer `unsafe`. [#1047](https://github.com/PyO3/pyo3/pull/1047)
|
||||
- Change `PyIterator::from_object` to return `PyResult<PyIterator>` instead of `Result<PyIterator, PyDowncastError>`. [#1051](https://github.com/PyO3/pyo3/pull/1051)
|
||||
- `IntoPy` is no longer implied by `FromPy`. [#1063](https://github.com/PyO3/pyo3/pull/1063)
|
||||
- `PyObject` is now just a type alias for `Py<PyAny>`. [#1063](https://github.com/PyO3/pyo3/pull/1063)
|
||||
- Implement `Send + Sync` for `PyErr`. `PyErr::new`, `PyErr::from_type`, `PyException::py_err` and `PyException::into` have had these bounds added to their arguments. [#1067](https://github.com/PyO3/pyo3/pull/1067)
|
||||
- Change `#[pyproto]` to return NotImplemented for operators for which Python can try a reversed operation. [1072](https://github.com/PyO3/pyo3/pull/1072)
|
||||
|
||||
|
@ -28,6 +30,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||
- Remove `PyString::as_bytes`. [#1023](https://github.com/PyO3/pyo3/pull/1023)
|
||||
- Remove `Python::register_any`. [#1023](https://github.com/PyO3/pyo3/pull/1023)
|
||||
- Remove `GILGuard::acquire` from the public API. Use `Python::acquire_gil` or `Python::with_gil`. [#1036](https://github.com/PyO3/pyo3/pull/1036)
|
||||
- Remove `FromPy`. [#1063](https://github.com/PyO3/pyo3/pull/1063)
|
||||
|
||||
### Fixed
|
||||
- Conversion from types with an `__index__` method to Rust BigInts. [#1027](https://github.com/PyO3/pyo3/pull/1027)
|
||||
|
@ -35,7 +38,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||
- Don't rely on the order of structmembers to compute offsets in PyCell. Related to
|
||||
[#1058](https://github.com/PyO3/pyo3/pull/1058). [#1059](https://github.com/PyO3/pyo3/pull/1059)
|
||||
- Allows `&Self` as a `#[pymethods]` argument again. [#1071](https://github.com/PyO3/pyo3/pull/1071)
|
||||
- Improve lifetime insertion in `#[pyproto]`. [#1093](https://github.com/PyO3/pyo3/pull/1093)
|
||||
- Fix best-effort build against PyPy 3.6. #[1092](https://github.com/PyO3/pyo3/pull/1092)
|
||||
- Improve lifetime elision in `#[pyproto]`. [#1093](https://github.com/PyO3/pyo3/pull/1093)
|
||||
|
||||
## [0.11.1] - 2020-06-30
|
||||
### Added
|
||||
|
|
|
@ -1,14 +1,62 @@
|
|||
# Contributing
|
||||
|
||||
Thank you for contributing to pyo3!
|
||||
Thank you for your interest in contributing to PyO3! All are welcome - please consider reading our [Code of Conduct](Code-of-Conduct.md) to keep our community positive and inclusive.
|
||||
|
||||
Here are some things you should check for submitting your pull request:
|
||||
If you are searching for ideas how to contribute, please read the "Getting started contributing" section. Once you've found an issue to contribute to, you may find the section "Writing pull requests" helpful.
|
||||
|
||||
- Run `cargo fmt` (This is checked by travis ci)
|
||||
- Run `cargo clippy` and check there are no hard errors (There are a bunch of existing warnings; This is also checked by travis)
|
||||
- If applicable, add an entry in the changelog
|
||||
- If applicable, add documentation to all new items and extend the guide
|
||||
- If applicable, add tests for all new or fixed functions
|
||||
- Run `cargo test`
|
||||
## Getting started contributing
|
||||
|
||||
You might want to run `tox` (`pip install tox`) locally to check compatibility with all supported python versions. If you're using linux or mac you might find the Makefile helpful for testing.
|
||||
Please join in with any part of PyO3 which interests you. We use Github issues to record all bugs and ideas. Feel free to request an issue to be assigned to you if you want to work on it.
|
||||
|
||||
The following sections also contain specific ideas on where to start contributing to PyO3.
|
||||
|
||||
### Help users identify bugs
|
||||
|
||||
The [PyO3 Gitter channel](https://gitter.im/PyO3/Lobby) is very active with users who are new to PyO3, and often completely new to Rust. Helping them debug is a great way to get experience with the PyO3 codebase.
|
||||
|
||||
Helping others often reveals bugs, documentation weaknesses, and missing APIs. It's a good idea to open Github issues for these immediately so the resolution can be designed and implemented!
|
||||
|
||||
### Implement issues ready for development
|
||||
|
||||
Issues where the solution is clear and work is not in progress use the [needs-implementer](https://github.com/PyO3/pyo3/issues?q=is%3Aissue+is%3Aopen+label%3Aneeds-implemeter) label.
|
||||
|
||||
Don't be afraid if the solution is not clear to you! The core PyO3 contributors will be happy to mentor you through any questions you have to help you write the solution.
|
||||
|
||||
### Help write great docs
|
||||
|
||||
PyO3 has a user guide (using mdbook) as well as the usual Rust API docs. The aim is for both of these to be detailed, easy to understand, and up-to-date. Pull requests are always welcome to fix typos, change wording, add examples, etc.
|
||||
|
||||
There are some specific areas of focus where help is currently needed for the documentation:
|
||||
- Issues requesting documentation improvements are tracked with the [documentation](https://github.com/PyO3/pyo3/issues?q=is%3Aissue+is%3Aopen+label%3Adocumentation) label.
|
||||
- Not all APIs had docs or examples when they were made. The goal is to have documentation on all PyO3 APIs ([#306](https://github.com/PyO3/pyo3/issues/306)). If you see an API lacking a doc, please write one and open a PR!
|
||||
- Not all `unsafe` APIs had safety notes when they made. We'd like to ensure all `unsafe` APIs are carefully explained ([#698](https://github.com/PyO3/pyo3/issues/698)). If you see an `unsafe` function missing safety notes, please write some and open a PR!
|
||||
|
||||
### Help design the next PyO3
|
||||
|
||||
Issues which don't yet have a clear solution use the [needs-design](https://github.com/PyO3/pyo3/issues?q=is%3Aissue+is%3Aopen+label%3Aneeds-design) label.
|
||||
|
||||
If any of these issues interest you, please join in with the conversation on the issue! All opinions are valued, and if you're interested in going further with e.g. draft PRs to experiment with API designs, even better!
|
||||
|
||||
### Review pull requests
|
||||
|
||||
Everybody is welcome to submit comments on open PRs. Please help ensure new PyO3 APIs are safe, performant, tidy, and easy to use!
|
||||
|
||||
## Writing pull requests
|
||||
|
||||
Here are a few things to note when you are writing PRs.
|
||||
|
||||
### Continuous Integration
|
||||
|
||||
The PyO3 repo uses a mixture of Github actions and Travis CI. PRs are blocked from merging if CI is not successful.
|
||||
|
||||
Formatting, linting and tests are checked for all Rust and Python code. Tests run with all supported Python versions with the latest stable Rust compiler, as well as for Python 3.8 with the minimum supported Rust version.
|
||||
|
||||
### Minimum supported Rust version
|
||||
|
||||
PyO3 aims to make use of up-to-date Rust language features to keep the implementation as efficient as possible.
|
||||
|
||||
However, there will always be support for at least the last few Rust compiler versions, so that users have time to update.
|
||||
|
||||
If your PR needs to bump the minimum supported Rust version, this is acceptable with the following conditions:
|
||||
- Any changes which require a more recent version than what is [currently available on stable Red Hat Enterprise Linux](https://access.redhat.com/documentation/en-us/red_hat_developer_tools/1/) will be postponed. (This is to allow package managers to update support for newer `rustc` versions; RHEL was arbitrarily picked because their update policy is clear.)
|
||||
- You might be asked to do extra work to tidy up other parts of the PyO3 codebase which can use the compiler version bump :)
|
||||
|
|
6
Makefile
6
Makefile
|
@ -1,8 +1,5 @@
|
|||
.PHONY: test test_py publish clippy lint fmt
|
||||
|
||||
# Constant used in clippy target
|
||||
CLIPPY_LINTS_TO_DENY := warnings
|
||||
|
||||
test: lint test_py
|
||||
cargo test
|
||||
|
||||
|
@ -15,8 +12,7 @@ fmt:
|
|||
|
||||
clippy:
|
||||
@touch src/lib.rs # Touching file to ensure that cargo clippy will re-check the project
|
||||
cargo clippy --features="default num-bigint num-complex" --tests -- \
|
||||
$(addprefix -D ,${CLIPPY_LINTS_TO_DENY})
|
||||
cargo clippy --features="default num-bigint num-complex" --tests -- -Dwarnings
|
||||
for example in examples/*; do (cd $$example/; cargo clippy) || exit 1; done
|
||||
|
||||
lint: fmt clippy
|
||||
|
|
|
@ -13,13 +13,15 @@
|
|||
|
||||
* API Documentation: [stable](https://docs.rs/pyo3/) | [master](https://pyo3.rs/master/doc)
|
||||
|
||||
* Contributing Notes: [github](https://github.com/PyO3/pyo3/blob/master/Contributing.md)
|
||||
|
||||
A comparison with rust-cpython can be found [in the guide](https://pyo3.rs/master/rust_cpython.html).
|
||||
|
||||
## Usage
|
||||
|
||||
PyO3 supports Python 3.5 and up. The minimum required Rust version is 1.39.0.
|
||||
|
||||
PyPy is also supported (via cpyext) for Python 3.5 only, targeted PyPy version is 7.0.0.
|
||||
Building with PyPy is also possible (via cpyext) for Python 3.6, targeted PyPy version is 7.3+.
|
||||
Please refer to the [pypy section in the guide](https://pyo3.rs/master/pypy.html).
|
||||
|
||||
You can either write a native Python module in Rust, or use Python from a Rust binary.
|
||||
|
|
73
build.rs
73
build.rs
|
@ -306,12 +306,7 @@ fn get_library_link_name(version: &PythonVersion, ld_version: &str) -> String {
|
|||
Some(minor) => format!("{}", minor),
|
||||
None => String::new(),
|
||||
};
|
||||
match version.implementation {
|
||||
PythonInterpreterKind::CPython => {
|
||||
format!("python{}{}", version.major, minor_or_empty_string)
|
||||
}
|
||||
PythonInterpreterKind::PyPy => format!("pypy{}-c", version.major),
|
||||
}
|
||||
format!("python{}{}", version.major, minor_or_empty_string)
|
||||
} else {
|
||||
match version.implementation {
|
||||
PythonInterpreterKind::CPython => format!("python{}", ld_version),
|
||||
|
@ -338,6 +333,11 @@ fn get_rustc_link_lib(config: &InterpreterConfig) -> Result<String> {
|
|||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn get_macos_linkmodel(config: &InterpreterConfig) -> Result<String> {
|
||||
// PyPy 3.6 ships with a shared library, but doesn't have Py_ENABLE_SHARED.
|
||||
if config.version.implementation == PythonInterpreterKind::PyPy {
|
||||
return Ok("shared".to_string());
|
||||
}
|
||||
|
||||
let script = r#"
|
||||
import sysconfig
|
||||
|
||||
|
@ -356,19 +356,11 @@ else:
|
|||
fn get_rustc_link_lib(config: &InterpreterConfig) -> Result<String> {
|
||||
// os x can be linked to a framework or static or dynamic, and
|
||||
// Py_ENABLE_SHARED is wrong; framework means shared library
|
||||
let link_name = get_library_link_name(&config.version, &config.ld_version);
|
||||
match get_macos_linkmodel(config)?.as_ref() {
|
||||
"static" => Ok(format!(
|
||||
"cargo:rustc-link-lib=static={}",
|
||||
get_library_link_name(&config.version, &config.ld_version)
|
||||
)),
|
||||
"shared" => Ok(format!(
|
||||
"cargo:rustc-link-lib={}",
|
||||
get_library_link_name(&config.version, &config.ld_version)
|
||||
)),
|
||||
"framework" => Ok(format!(
|
||||
"cargo:rustc-link-lib={}",
|
||||
get_library_link_name(&config.version, &config.ld_version)
|
||||
)),
|
||||
"static" => Ok(format!("cargo:rustc-link-lib=static={}", link_name,)),
|
||||
"shared" => Ok(format!("cargo:rustc-link-lib={}", link_name)),
|
||||
"framework" => Ok(format!("cargo:rustc-link-lib={}", link_name,)),
|
||||
other => bail!("unknown linkmodel {}", other),
|
||||
}
|
||||
}
|
||||
|
@ -382,6 +374,28 @@ fn get_rustc_link_lib(config: &InterpreterConfig) -> Result<String> {
|
|||
))
|
||||
}
|
||||
|
||||
fn find_interpreter() -> Result<PathBuf> {
|
||||
if let Some(exe) = env::var_os("PYO3_PYTHON") {
|
||||
Ok(exe.into())
|
||||
} else if let Some(exe) = env::var_os("PYTHON_SYS_EXECUTABLE") {
|
||||
// Backwards-compatible name for PYO3_PYTHON; this may be removed at some point in the future.
|
||||
Ok(exe.into())
|
||||
} else {
|
||||
["python", "python3"]
|
||||
.iter()
|
||||
.find(|bin| {
|
||||
if let Ok(out) = Command::new(bin).arg("--version").output() {
|
||||
// begin with `Python 3.X.X :: additional info`
|
||||
out.stdout.starts_with(b"Python 3") || out.stderr.starts_with(b"Python 3")
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.map(PathBuf::from)
|
||||
.ok_or_else(|| "Python 3.x interpreter not found".into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Locate a suitable python interpreter and extract config from it.
|
||||
///
|
||||
/// The following locations are checked in the order listed:
|
||||
|
@ -394,27 +408,7 @@ fn get_rustc_link_lib(config: &InterpreterConfig) -> Result<String> {
|
|||
///
|
||||
/// If none of the above works, an error is returned
|
||||
fn find_interpreter_and_get_config() -> Result<(InterpreterConfig, HashMap<String, String>)> {
|
||||
let python_interpreter = if let Some(exe) = env::var_os("PYO3_PYTHON") {
|
||||
exe.into()
|
||||
} else if let Some(exe) = env::var_os("PYTHON_SYS_EXECUTABLE") {
|
||||
// Backwards-compatible name for PYO3_PYTHON; this may be removed at some point in the future.
|
||||
exe.into()
|
||||
} else {
|
||||
PathBuf::from(
|
||||
["python", "python3"]
|
||||
.iter()
|
||||
.find(|bin| {
|
||||
if let Ok(out) = Command::new(bin).arg("--version").output() {
|
||||
// begin with `Python 3.X.X :: additional info`
|
||||
out.stdout.starts_with(b"Python 3") || out.stderr.starts_with(b"Python 3")
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.ok_or("Python 3.x interpreter not found")?,
|
||||
)
|
||||
};
|
||||
|
||||
let python_interpreter = find_interpreter()?;
|
||||
let interpreter_config = get_config_from_interpreter(&python_interpreter)?;
|
||||
if interpreter_config.version.major == 3 {
|
||||
return Ok((interpreter_config, get_config_vars(&python_interpreter)?));
|
||||
|
@ -426,7 +420,6 @@ fn find_interpreter_and_get_config() -> Result<(InterpreterConfig, HashMap<Strin
|
|||
/// Extract compilation vars from the specified interpreter.
|
||||
fn get_config_from_interpreter(interpreter: &Path) -> Result<InterpreterConfig> {
|
||||
let script = r#"
|
||||
import json
|
||||
import platform
|
||||
import struct
|
||||
import sys
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e -u -o pipefail
|
||||
|
||||
cargo test --features "${FEATURES:-} num-bigint num-complex"
|
||||
(cd pyo3-derive-backend; cargo test)
|
||||
|
||||
for example_dir in examples/*; do
|
||||
cd $example_dir
|
||||
tox -c "tox.ini" -e py
|
||||
cd -
|
||||
done
|
|
@ -10,11 +10,6 @@ else
|
|||
cargo build;
|
||||
fi
|
||||
|
||||
if [[ $RUN_LINT == 1 ]]; then
|
||||
pip install --pre black==19.3b0
|
||||
make lint
|
||||
fi
|
||||
|
||||
for example_dir in examples/*; do
|
||||
cd $example_dir
|
||||
if [[ $FEATURES == *"pypy"* ]]; then
|
||||
|
|
|
@ -266,7 +266,7 @@ impl SubSubClass {
|
|||
```
|
||||
|
||||
You can also inherit native types such as `PyDict`, if they implement
|
||||
[`PySizedLayout`](https://pyo3.rs/master/doc/pyo3/type_object/trait.PySizedLayout.html).
|
||||
[`PySizedLayout`](https://docs.rs/pyo3/latest/pyo3/type_object/trait.PySizedLayout.html).
|
||||
|
||||
However, because of some technical problems, we don't currently provide safe upcasting methods for types
|
||||
that inherit native types. Even in such cases, you can unsafely get a base class by raw pointer conversion.
|
||||
|
@ -1094,11 +1094,11 @@ impl pyo3::pyclass::PyClassSend for MyClass {
|
|||
[`PyTypeInfo`]: https://docs.rs/pyo3/latest/pyo3/type_object/trait.PyTypeInfo.html
|
||||
[`PyTypeObject`]: https://docs.rs/pyo3/latest/pyo3/type_object/trait.PyTypeObject.html
|
||||
|
||||
[`PyCell`]: https://pyo3.rs/master/doc/pyo3/pycell/struct.PyCell.html
|
||||
[`PyClass`]: https://pyo3.rs/master/doc/pyo3/pyclass/trait.PyClass.html
|
||||
[`PyRef`]: https://pyo3.rs/master/doc/pyo3/pycell/struct.PyRef.html
|
||||
[`PyRefMut`]: https://pyo3.rs/master/doc/pyo3/pycell/struct.PyRefMut.html
|
||||
[`PyClassInitializer<T>`]: https://pyo3.rs/master/doc/pyo3/pyclass_init/struct.PyClassInitializer.html
|
||||
[`PyCell`]: https://docs.rs/pyo3/latest/pyo3/pycell/struct.PyCell.html
|
||||
[`PyClass`]: https://docs.rs/pyo3/latest/pyo3/pyclass/trait.PyClass.html
|
||||
[`PyRef`]: https://docs.rs/pyo3/latest/pyo3/pycell/struct.PyRef.html
|
||||
[`PyRefMut`]: https://docs.rs/pyo3/latest/pyo3/pycell/struct.PyRefMut.html
|
||||
[`PyClassInitializer<T>`]: https://docs.rs/pyo3/latest/pyo3/pyclass_init/struct.PyClassInitializer.html
|
||||
|
||||
[`RefCell`]: https://doc.rust-lang.org/std/cell/struct.RefCell.html
|
||||
|
||||
|
|
|
@ -44,13 +44,13 @@ The table below contains the Python type and the corresponding function argument
|
|||
There are also a few special types related to the GIL and Rust-defined `#[pyclass]`es which may come in useful:
|
||||
|
||||
| What | Description |
|
||||
| ------------- | ------------------------------- |
|
||||
| ------------- | ------------------------------------- |
|
||||
| `Python` | A GIL token, used to pass to PyO3 constructors to prove ownership of the GIL |
|
||||
| `PyObject` | A Python object isolated from the GIL lifetime. This can be sent to other threads. To call Python APIs using this object, it must be used with `AsPyRef::as_ref` to get a `&PyAny` reference. |
|
||||
| `Py<T>` | Same as above, for a specific Python type or `#[pyclass]` T. |
|
||||
| `Py<T>` | A Python object isolated from the GIL lifetime. This can be sent to other threads. To call Python APIs using this object, it must be used with `AsPyRef::as_ref` to get an `&T` reference bound to the GIL. |
|
||||
| `PyObject` | An alias for `Py<PyAny>` |
|
||||
| `&PyCell<T>` | A `#[pyclass]` value owned by Python. |
|
||||
| `PyRef<T>` | A `#[pyclass]` borrowed immutably. |
|
||||
| `PyRefMut<T>` | A `#[pyclass]` borrowed mutably. |
|
||||
| `PyRef<T>` | A `#[pyclass]` borrowed immutably. |
|
||||
| `PyRefMut<T>` | A `#[pyclass]` borrowed mutably. |
|
||||
|
||||
For more detail on accepting `#[pyclass]` values as function arguments, see [the section of this guide on Python Classes](class.md).
|
||||
|
||||
|
@ -119,6 +119,28 @@ mutable references, you have to extract the PyO3 reference wrappers [`PyRef`]
|
|||
and [`PyRefMut`]. They work like the reference wrappers of
|
||||
`std::cell::RefCell` and ensure (at runtime) that Rust borrows are allowed.
|
||||
|
||||
### `IntoPy<T>`
|
||||
|
||||
This trait defines the to-python conversion for a Rust type. It is usually implemented as
|
||||
`IntoPy<PyObject>`, which is the trait needed for returning a value from `#[pyfunction]` and
|
||||
`#[pymethods]`.
|
||||
|
||||
All types in PyO3 implement this trait, as does a `#[pyclass]` which doesn't use `extends`.
|
||||
|
||||
Occasionally you may choose to implement this for custom types which are mapped to Python types
|
||||
_without_ having a unique python type.
|
||||
|
||||
```
|
||||
use pyo3::prelude::*;
|
||||
|
||||
struct MyPyObjectWrapper(PyObject);
|
||||
|
||||
impl IntoPy<PyObject> for MyPyObjectWrapper {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### The `ToPyObject` trait
|
||||
|
||||
|
@ -216,27 +238,15 @@ fn main() {
|
|||
}
|
||||
```
|
||||
|
||||
### `FromPy<T>` and `IntoPy<T>`
|
||||
|
||||
Many conversions in PyO3 can't use `std::convert::From` because they need a GIL token.
|
||||
The [`FromPy`] trait offers an `from_py` method that works just like `from`, except for taking a `Python<'_>` argument.
|
||||
I.e. `FromPy<T>` could be converting a Rust object into a Python object even though it is called [`FromPy`] - it doesn't say anything about which side of the conversion is a Python object.
|
||||
|
||||
Just like `From<T>`, if you implement `FromPy<T>` you gain a blanket implementation of [`IntoPy`] for free.
|
||||
|
||||
Eventually, traits such as [`ToPyObject`] will be replaced by this trait and a [`FromPy`] trait will be added that will implement
|
||||
[`IntoPy`], just like with `From` and `Into`.
|
||||
|
||||
[`IntoPy`]: https://docs.rs/pyo3/latest/pyo3/conversion/trait.IntoPy.html
|
||||
[`FromPy`]: https://docs.rs/pyo3/latest/pyo3/conversion/trait.FromPy.html
|
||||
[`FromPyObject`]: https://docs.rs/pyo3/latest/pyo3/conversion/trait.FromPyObject.html
|
||||
[`ToPyObject`]: https://docs.rs/pyo3/latest/pyo3/conversion/trait.ToPyObject.html
|
||||
[`PyObject`]: https://docs.rs/pyo3/latest/pyo3/struct.PyObject.html
|
||||
[`PyObject`]: https://docs.rs/pyo3/latest/pyo3/type.PyObject.html
|
||||
[`PyTuple`]: https://docs.rs/pyo3/latest/pyo3/types/struct.PyTuple.html
|
||||
[`PyAny`]: https://docs.rs/pyo3/latest/pyo3/struct.PyAny.html
|
||||
[`IntoPyDict`]: https://docs.rs/pyo3/latest/pyo3/types/trait.IntoPyDict.html
|
||||
|
||||
[`PyRef`]: https://pyo3.rs/master/doc/pyo3/pycell/struct.PyRef.html
|
||||
[`PyRefMut`]: https://pyo3.rs/master/doc/pyo3/pycell/struct.PyRefMut.html
|
||||
[`PyRef`]: https://docs.rs/pyo3/latest/pyo3/pycell/struct.PyRef.html
|
||||
[`PyRefMut`]: https://docs.rs/pyo3/latest/pyo3/pycell/struct.PyRefMut.html
|
||||
|
||||
[^1]: Requires the `num-complex` optional feature.
|
||||
|
|
|
@ -192,15 +192,12 @@ mod io {
|
|||
pyo3::import_exception!(io, UnsupportedOperation);
|
||||
}
|
||||
|
||||
fn tell(file: PyObject) -> PyResult<u64> {
|
||||
fn tell(file: &PyAny) -> PyResult<u64> {
|
||||
use pyo3::exceptions::*;
|
||||
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
||||
match file.call_method0(py, "tell") {
|
||||
match file.call_method0("tell") {
|
||||
Err(_) => Err(io::UnsupportedOperation::py_err("not supported: tell")),
|
||||
Ok(x) => x.extract::<u64>(py),
|
||||
Ok(x) => x.extract::<u64>(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -187,5 +187,5 @@ If you have a static function, you can expose it with `#[pyfunction]` and use [`
|
|||
[`PyAny::call`]: https://docs.rs/pyo3/latest/pyo3/struct.PyAny.html#tymethod.call
|
||||
[`PyAny::call0`]: https://docs.rs/pyo3/latest/pyo3/struct.PyAny.html#tymethod.call0
|
||||
[`PyAny::call1`]: https://docs.rs/pyo3/latest/pyo3/struct.PyAny.html#tymethod.call1
|
||||
[`PyObject`]: https://docs.rs/pyo3/latest/pyo3/struct.PyObject.html
|
||||
[`PyObject`]: https://docs.rs/pyo3/latest/pyo3/type.PyObject.html
|
||||
[`wrap_pyfunction!`]: https://docs.rs/pyo3/latest/pyo3/macro.wrap_pyfunction.html
|
||||
|
|
|
@ -3,6 +3,65 @@
|
|||
This guide can help you upgrade code through breaking changes from one PyO3 version to the next.
|
||||
For a detailed list of all changes, see [CHANGELOG.md](https://github.com/PyO3/pyo3/blob/master/CHANGELOG.md)
|
||||
|
||||
## from 0.11.* to 0.12
|
||||
|
||||
### `FromPy` has been removed
|
||||
To simplify the PyO3 public conversion trait hierarchy, the `FromPy` has been removed. In PyO3
|
||||
`0.11` there were two ways to define the to-Python conversion for a type: `FromPy<T> for PyObject`,
|
||||
and `IntoPy<PyObject> for T`.
|
||||
|
||||
Now, the canonical implementation is always `IntoPy`, so downstream crates may need to adjust
|
||||
accordingly.
|
||||
|
||||
Before:
|
||||
|
||||
```rust,ignore
|
||||
# use pyo3::prelude::*;
|
||||
struct MyPyObjectWrapper(PyObject);
|
||||
|
||||
impl FromPy<MyPyObjectWrapper> for PyObject {
|
||||
fn from_py(other: MyPyObjectWrapper, _py: Python) -> Self {
|
||||
other.0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
After
|
||||
|
||||
```rust
|
||||
# use pyo3::prelude::*;
|
||||
struct MyPyObjectWrapper(PyObject);
|
||||
|
||||
impl IntoPy<PyObject> for MyPyObjectWrapper {
|
||||
fn into_py(self, _py: Python) -> PyObject {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Similarly, code which was using the `FromPy` trait can be trivially rewritten to use `IntoPy`.
|
||||
|
||||
Before:
|
||||
|
||||
```rust,ignore
|
||||
# use pyo3::prelude::*;
|
||||
# Python::with_gil(|py| {
|
||||
let obj = PyObject::from_py(1.234, py);
|
||||
# })
|
||||
```
|
||||
|
||||
After:
|
||||
```rust
|
||||
# use pyo3::prelude::*;
|
||||
# Python::with_gil(|py| {
|
||||
let obj: PyObject = 1.234.into_py(py);
|
||||
# })
|
||||
```
|
||||
|
||||
### `PyObject` is now a type alias of `Py<PyAny>`
|
||||
This should change very little from a usage perspective. If you implemented traits for both
|
||||
`PyObject` and `Py<T>`, you may find you can just remove the `PyObject` implementation.
|
||||
|
||||
## from 0.10.* to 0.11
|
||||
|
||||
### Stable Rust
|
||||
|
@ -46,7 +105,7 @@ There can be two fixes:
|
|||
```
|
||||
|
||||
In situations where you cannot change your `#[pyclass]` to automatically implement `Send`
|
||||
(e.g., when it contains a raw pointer), you can use `unsafe impl Send`.
|
||||
(e.g., when it contains a raw pointer), you can use `unsafe impl Send`.
|
||||
In such cases, care should be taken to ensure the struct is actually thread safe.
|
||||
See [the Rustnomicon](https://doc.rust-lang.org/nomicon/send-and-sync.html) for more.
|
||||
|
||||
|
@ -216,7 +275,7 @@ However, for `#[pyproto]` and some functions, you need to manually fix the code.
|
|||
In 0.8 object creation was done with `PyRef::new` and `PyRefMut::new`.
|
||||
In 0.9 these have both been removed.
|
||||
To upgrade code, please use
|
||||
[`PyCell::new`](https://pyo3.rs/master/doc/pyo3/pycell/struct.PyCell.html#method.new) instead.
|
||||
[`PyCell::new`](https://docs.rs/pyo3/latest/pyo3/pycell/struct.PyCell.html#method.new) instead.
|
||||
If you need [`PyRef`] or [`PyRefMut`], just call `.borrow()` or `.borrow_mut()`
|
||||
on the newly-created `PyCell`.
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ PyO3 is built for an extension module or not.
|
|||
|
||||
## Want to access Python APIs? Then use `PyModule::import`.
|
||||
|
||||
[`Pymodule::import`](https://pyo3.rs/master/doc/pyo3/types/struct.PyModule.html#method.import) can
|
||||
[`Pymodule::import`](https://docs.rs/pyo3/latest/pyo3/types/struct.PyModule.html#method.import) can
|
||||
be used to get handle to a Python module from Rust. You can use this to import and use any Python
|
||||
module available in your environment.
|
||||
|
||||
|
@ -24,7 +24,7 @@ fn main() -> PyResult<()> {
|
|||
|
||||
## Want to run just an expression? Then use `eval`.
|
||||
|
||||
[`Python::eval`](https://pyo3.rs/master/doc/pyo3/struct.Python.html#method.eval) is
|
||||
[`Python::eval`](https://docs.rs/pyo3/latest/pyo3/struct.Python.html#method.eval) is
|
||||
a method to execute a [Python expression](https://docs.python.org/3.7/reference/expressions.html)
|
||||
and return the evaluated value as a `&PyAny` object.
|
||||
|
||||
|
@ -94,7 +94,7 @@ assert userdata.as_tuple() == userdata_as_tuple
|
|||
|
||||
## You have a Python file or Python function? Then use `PyModule::from_code`.
|
||||
|
||||
[PyModule::from_code](https://pyo3.rs/master/doc/pyo3/types/struct.PyModule.html#method.from_code)
|
||||
[PyModule::from_code](https://docs.rs/pyo3/latest/pyo3/types/struct.PyModule.html#method.from_code)
|
||||
can be used to generate a Python module which can then be used just as if it was imported with
|
||||
`PyModule::import`.
|
||||
|
||||
|
@ -124,5 +124,5 @@ def leaky_relu(x, slope=0.01):
|
|||
# }
|
||||
```
|
||||
|
||||
[`Python::run`]: https://pyo3.rs/master/doc/pyo3/struct.Python.html#method.run
|
||||
[`py_run!`]: https://pyo3.rs/master/doc/pyo3/macro.py_run.html
|
||||
[`Python::run`]: https://docs.rs/pyo3/latest/pyo3/struct.Python.html#method.run
|
||||
[`py_run!`]: https://docs.rs/pyo3/latest/pyo3/macro.py_run.html
|
||||
|
|
|
@ -24,7 +24,7 @@ In PyO3, holding the GIL is modeled by acquiring a token of the type
|
|||
* It provides some global API for the Python interpreter, such as
|
||||
[`eval`][eval].
|
||||
* It can be passed to functions that require a proof of holding the GIL,
|
||||
such as [`PyObject::clone_ref`][clone_ref].
|
||||
such as [`Py::clone_ref`][clone_ref].
|
||||
* Its lifetime can be used to create Rust references that implicitly guarantee
|
||||
holding the GIL, such as [`&'py PyAny`][PyAny].
|
||||
|
||||
|
@ -71,10 +71,7 @@ let obj: &PyAny = PyList::empty(py);
|
|||
// Convert to &ConcreteType using PyAny::downcast
|
||||
let _: &PyList = obj.downcast().unwrap();
|
||||
|
||||
// Convert to PyObject using .into() or .to_object(py)
|
||||
let _: PyObject = obj.into();
|
||||
|
||||
// Convert to Py<PyAny> using .into() or Py::from
|
||||
// Convert to Py<PyAny> (aka PyObject) using .into()
|
||||
let _: Py<PyAny> = obj.into();
|
||||
|
||||
// Convert to Py<ConcreteType> using PyAny::extract
|
||||
|
@ -115,51 +112,24 @@ let _: &PyAny = list;
|
|||
// For more explicit &PyAny conversion, use .as_ref()
|
||||
let _: &PyAny = list.as_ref();
|
||||
|
||||
// To convert to PyObject use .into() or .to_object(py)
|
||||
let _: PyObject = list.into();
|
||||
|
||||
// To convert to Py<T> use .into() or Py::from()
|
||||
let _: Py<PyList> = list.into();
|
||||
|
||||
// To convert to PyObject use .into() or .to_object(py)
|
||||
let _: PyObject = list.into();
|
||||
```
|
||||
|
||||
### `PyObject`
|
||||
### `Py<T>`
|
||||
|
||||
**Represents:** a GIL independent reference to a Python object of unspecified
|
||||
type.
|
||||
**Represents:** a GIL independent reference to a Python object. This can be a Python native type
|
||||
(like `PyTuple`), or a `pyclass` type implemented in Rust. The most commonly-used variant,
|
||||
`Py<PyAny>`, is also known as `PyObject`.
|
||||
|
||||
**Used:** Whenever you want to carry around references to "some" Python object,
|
||||
without caring about a GIL lifetime. For example, storing Python object
|
||||
references in a Rust struct that outlives the Python-Rust FFI boundary,
|
||||
or returning objects from functions implemented in Rust back to Python.
|
||||
**Used:** Whenever you want to carry around references to a Python object without caring about a
|
||||
GIL lifetime. For example, storing Python object references in a Rust struct that outlives the
|
||||
Python-Rust FFI boundary, or returning objects from functions implemented in Rust back to Python.
|
||||
|
||||
Can be cloned using Python reference counts with `.clone_ref()`.
|
||||
|
||||
**Conversions:**
|
||||
|
||||
```rust
|
||||
# use pyo3::prelude::*;
|
||||
# use pyo3::types::PyList;
|
||||
# let gil = Python::acquire_gil();
|
||||
# let py = gil.python();
|
||||
let obj: PyObject = PyList::empty(py).into();
|
||||
|
||||
// Convert to &PyAny using AsPyRef::as_ref
|
||||
let _: &PyAny = obj.as_ref(py);
|
||||
|
||||
// Convert to &ConcreteType using PyObject::cast_as
|
||||
let _: &PyList = obj.cast_as(py).unwrap();
|
||||
|
||||
// Convert to Py<ConcreteType> using PyObject::extract
|
||||
let _: Py<PyList> = obj.extract(py).unwrap();
|
||||
```
|
||||
|
||||
### `Py<SomeType>`
|
||||
|
||||
**Represents:** a GIL independent reference to a Python object of known type.
|
||||
This can be a Python native type (like `PyTuple`), or a `pyclass` type
|
||||
implemented in Rust.
|
||||
|
||||
**Used:** Like `PyObject`, but with a known inner type.
|
||||
Can be cloned using Python reference counts with `.clone()`.
|
||||
|
||||
**Conversions:**
|
||||
|
||||
|
@ -178,10 +148,6 @@ let _: &PyList = list.as_ref(py);
|
|||
let _: PyObject = list.into();
|
||||
```
|
||||
|
||||
**Note:** `PyObject` is semantically equivalent to `Py<PyAny>` and might be
|
||||
merged with it in the future.
|
||||
|
||||
|
||||
### `PyCell<SomeType>`
|
||||
|
||||
**Represents:** a reference to a Rust object (instance of `PyClass`) which is
|
||||
|
@ -248,8 +214,8 @@ This trait marks structs that mirror native Python types, such as `PyList`.
|
|||
|
||||
|
||||
[eval]: https://docs.rs/pyo3/latest/pyo3/struct.Python.html#method.eval
|
||||
[clone_ref]: https://docs.rs/pyo3/latest/pyo3/struct.PyObject.html#method.clone_ref
|
||||
[pyo3::types]: https://pyo3.rs/master/doc/pyo3/types/index.html
|
||||
[clone_ref]: https://docs.rs/pyo3/latest/pyo3/struct.Py.html#method.clone_ref
|
||||
[pyo3::types]: https://docs.rs/pyo3/latest/pyo3/types/index.html
|
||||
[PyAny]: https://docs.rs/pyo3/latest/pyo3/types/struct.PyAny.html
|
||||
[PyList_append]: https://docs.rs/pyo3/latest/pyo3/types/struct.PyList.html#method.append
|
||||
[RefCell]: https://doc.rust-lang.org/std/cell/struct.RefCell.html
|
||||
|
|
|
@ -190,7 +190,7 @@ pub fn add_fn_to_module(
|
|||
};
|
||||
|
||||
let function = unsafe {
|
||||
pyo3::PyObject::from_owned_ptr_or_panic(
|
||||
pyo3::PyObject::from_owned_ptr(
|
||||
py,
|
||||
pyo3::ffi::PyCFunction_New(
|
||||
Box::into_raw(Box::new(_def.as_method_def())),
|
||||
|
|
|
@ -2,10 +2,11 @@
|
|||
|
||||
//! Conversions between various states of Rust and Python types and their wrappers.
|
||||
use crate::err::{self, PyDowncastError, PyResult};
|
||||
use crate::object::PyObject;
|
||||
use crate::type_object::PyTypeInfo;
|
||||
use crate::types::PyTuple;
|
||||
use crate::{ffi, gil, Py, PyAny, PyCell, PyClass, PyNativeType, PyRef, PyRefMut, Python};
|
||||
use crate::{
|
||||
ffi, gil, Py, PyAny, PyCell, PyClass, PyNativeType, PyObject, PyRef, PyRefMut, Python,
|
||||
};
|
||||
use std::ptr::NonNull;
|
||||
|
||||
/// This trait represents that **we can do zero-cost conversion from the object
|
||||
|
@ -132,42 +133,22 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Similar to [std::convert::From], just that it requires a gil token.
|
||||
pub trait FromPy<T>: Sized {
|
||||
/// Performs the conversion.
|
||||
fn from_py(_: T, py: Python) -> Self;
|
||||
}
|
||||
|
||||
/// Similar to [std::convert::Into], just that it requires a gil token.
|
||||
///
|
||||
/// `IntoPy<PyObject>` (aka `IntoPy<Py<PyAny>>`) should be implemented to define a conversion from
|
||||
/// Rust to Python which can be used by most of PyO3's methods.
|
||||
pub trait IntoPy<T>: Sized {
|
||||
/// Performs the conversion.
|
||||
fn into_py(self, py: Python) -> T;
|
||||
}
|
||||
|
||||
// From implies Into
|
||||
impl<T, U> IntoPy<U> for T
|
||||
where
|
||||
U: FromPy<T>,
|
||||
{
|
||||
fn into_py(self, py: Python) -> U {
|
||||
U::from_py(self, py)
|
||||
}
|
||||
}
|
||||
|
||||
// From (and thus Into) is reflexive
|
||||
impl<T> FromPy<T> for T {
|
||||
fn from_py(t: T, _: Python) -> T {
|
||||
t
|
||||
}
|
||||
}
|
||||
|
||||
/// `FromPyObject` is implemented by various types that can be extracted from
|
||||
/// a Python object reference.
|
||||
///
|
||||
/// Normal usage is through the helper methods `PyObject::extract` or
|
||||
/// `PyAny::extract`:
|
||||
/// Normal usage is through the helper methods `Py::extract` or `PyAny::extract`:
|
||||
///
|
||||
/// ```let obj: PyObject = ...;
|
||||
/// ```rust,ignore
|
||||
/// let obj: Py<PyAny> = ...;
|
||||
/// let value: &TargetType = obj.extract(py)?;
|
||||
///
|
||||
/// let any: &PyAny = ...;
|
||||
|
@ -184,9 +165,6 @@ impl<T> FromPy<T> for T {
|
|||
/// Since which case applies depends on the runtime type of the Python object,
|
||||
/// both the `obj` and `prepared` variables must outlive the resulting string slice.
|
||||
///
|
||||
/// In cases where the result does not depend on the `'prepared` lifetime,
|
||||
/// the inherent method `PyObject::extract()` can be used.
|
||||
///
|
||||
/// The trait's conversion method takes a `&PyAny` argument but is called
|
||||
/// `FromPyObject` for historical reasons.
|
||||
pub trait FromPyObject<'source>: Sized {
|
||||
|
@ -234,19 +212,19 @@ impl ToPyObject for () {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromPy<()> for PyObject {
|
||||
fn from_py(_: (), py: Python) -> Self {
|
||||
impl IntoPy<PyObject> for () {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
py.None()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> FromPy<&'a T> for PyObject
|
||||
impl<T> IntoPy<PyObject> for &'_ T
|
||||
where
|
||||
T: AsPyPointer,
|
||||
{
|
||||
#[inline]
|
||||
fn from_py(other: &'a T, py: Python) -> PyObject {
|
||||
unsafe { PyObject::from_borrowed_ptr(py, other.as_ptr()) }
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -404,9 +382,9 @@ where
|
|||
}
|
||||
|
||||
/// Converts `()` to an empty Python tuple.
|
||||
impl FromPy<()> for Py<PyTuple> {
|
||||
fn from_py(_: (), py: Python) -> Py<PyTuple> {
|
||||
Py::from_py(PyTuple::empty(py), py)
|
||||
impl IntoPy<Py<PyTuple>> for () {
|
||||
fn into_py(self, py: Python) -> Py<PyTuple> {
|
||||
PyTuple::empty(py).into()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
16
src/err.rs
16
src/err.rs
|
@ -6,8 +6,8 @@ use crate::type_object::PyTypeObject;
|
|||
use crate::types::PyType;
|
||||
use crate::{exceptions, ffi};
|
||||
use crate::{
|
||||
AsPyPointer, FromPy, FromPyPointer, IntoPy, IntoPyPointer, Py, PyAny, PyNativeType, PyObject,
|
||||
Python, ToBorrowedObject, ToPyObject,
|
||||
AsPyPointer, FromPyPointer, IntoPy, IntoPyPointer, Py, PyAny, PyNativeType, PyObject, Python,
|
||||
ToBorrowedObject, ToPyObject,
|
||||
};
|
||||
use libc::c_int;
|
||||
use std::borrow::Cow;
|
||||
|
@ -438,15 +438,15 @@ impl std::fmt::Debug for PyErr {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromPy<PyErr> for PyObject {
|
||||
fn from_py(other: PyErr, py: Python) -> Self {
|
||||
other.instance(py).into()
|
||||
impl IntoPy<PyObject> for PyErr {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
self.instance(py).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromPy<PyErr> for Py<exceptions::PyBaseException> {
|
||||
fn from_py(other: PyErr, py: Python) -> Self {
|
||||
other.instance(py).into()
|
||||
impl IntoPy<Py<exceptions::PyBaseException>> for PyErr {
|
||||
fn into_py(self, py: Python) -> Py<exceptions::PyBaseException> {
|
||||
self.instance(py).into()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -443,9 +443,11 @@ impl_native_exception!(
|
|||
);
|
||||
impl_native_exception!(PyTimeoutError, TimeoutError, PyExc_TimeoutError);
|
||||
|
||||
#[cfg(not(all(windows, PyPy)))]
|
||||
impl_native_exception!(PyEnvironmentError, EnvironmentError, PyExc_EnvironmentError);
|
||||
#[cfg(not(all(windows, PyPy)))]
|
||||
impl_native_exception!(PyIOError, IOError, PyExc_IOError);
|
||||
#[cfg(target_os = "windows")]
|
||||
#[cfg(all(windows, not(PyPy)))]
|
||||
impl_native_exception!(PyWindowsError, WindowsError, PyExc_WindowsError);
|
||||
|
||||
impl PyUnicodeDecodeError {
|
||||
|
|
|
@ -4,6 +4,7 @@ use std::os::raw::{c_char, c_int};
|
|||
|
||||
#[cfg_attr(windows, link(name = "pythonXY"))]
|
||||
extern "C" {
|
||||
#[cfg_attr(PyPy, link_name = "PyPyDict_Type")]
|
||||
pub static mut PyDict_Type: PyTypeObject;
|
||||
pub static mut PyDictIterKey_Type: PyTypeObject;
|
||||
pub static mut PyDictIterValue_Type: PyTypeObject;
|
||||
|
|
|
@ -81,6 +81,7 @@ extern "C" {
|
|||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
extern "C" {
|
||||
#[cfg(not(PyPy))]
|
||||
pub fn _PyLong_NumBits(obj: *mut PyObject) -> c_int;
|
||||
|
||||
#[cfg_attr(PyPy, link_name = "_PyPyLong_FromByteArray")]
|
||||
|
@ -91,6 +92,7 @@ extern "C" {
|
|||
is_signed: c_int,
|
||||
) -> *mut PyObject;
|
||||
|
||||
#[cfg(not(PyPy))]
|
||||
pub fn _PyLong_AsByteArray(
|
||||
v: *mut PyLongObject,
|
||||
bytes: *mut c_uchar,
|
||||
|
|
|
@ -2,9 +2,9 @@ use super::PyObject;
|
|||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(PyPy, link_name = "PyMarshal_WriteObjectToString")]
|
||||
#[cfg_attr(PyPy, link_name = "PyPyMarshal_WriteObjectToString")]
|
||||
pub fn PyMarshal_WriteObjectToString(object: *mut PyObject, version: c_int) -> *mut PyObject;
|
||||
|
||||
#[cfg_attr(PyPy, link_name = "PyMarshal_ReadObjectFromString")]
|
||||
#[cfg_attr(PyPy, link_name = "PyPyMarshal_ReadObjectFromString")]
|
||||
pub fn PyMarshal_ReadObjectFromString(data: *const c_char, len: isize) -> *mut PyObject;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,9 @@ extern "C" {
|
|||
#[cfg_attr(PyPy, link_name = "PyPyModule_GetDict")]
|
||||
pub fn PyModule_GetDict(arg1: *mut PyObject) -> *mut PyObject;
|
||||
pub fn PyModule_GetNameObject(arg1: *mut PyObject) -> *mut PyObject;
|
||||
#[cfg_attr(PyPy, link_name = "PyPyModule_GetName")]
|
||||
pub fn PyModule_GetName(arg1: *mut PyObject) -> *const c_char;
|
||||
#[cfg(not(all(windows, PyPy)))]
|
||||
pub fn PyModule_GetFilename(arg1: *mut PyObject) -> *const c_char;
|
||||
pub fn PyModule_GetFilenameObject(arg1: *mut PyObject) -> *mut PyObject;
|
||||
#[cfg_attr(PyPy, link_name = "PyPyModule_GetDef")]
|
||||
|
|
|
@ -202,6 +202,7 @@ extern "C" {
|
|||
#[cfg_attr(PyPy, link_name = "PyPyExc_ImportError")]
|
||||
pub static mut PyExc_ImportError: *mut PyObject;
|
||||
#[cfg(Py_3_6)]
|
||||
#[cfg_attr(PyPy, link_name = "PyPyExc_ModuleNotFoundError")]
|
||||
pub static mut PyExc_ModuleNotFoundError: *mut PyObject;
|
||||
#[cfg_attr(PyPy, link_name = "PyPyExc_IndexError")]
|
||||
pub static mut PyExc_IndexError: *mut PyObject;
|
||||
|
@ -281,9 +282,11 @@ extern "C" {
|
|||
#[cfg_attr(PyPy, link_name = "PyPyExc_TimeoutError")]
|
||||
pub static mut PyExc_TimeoutError: *mut PyObject;
|
||||
|
||||
#[cfg(not(all(windows, PyPy)))]
|
||||
pub static mut PyExc_EnvironmentError: *mut PyObject;
|
||||
#[cfg(not(all(windows, PyPy)))]
|
||||
pub static mut PyExc_IOError: *mut PyObject;
|
||||
#[cfg(windows)]
|
||||
#[cfg(all(windows, not(PyPy)))]
|
||||
pub static mut PyExc_WindowsError: *mut PyObject;
|
||||
|
||||
pub static mut PyExc_RecursionErrorInst: *mut PyObject;
|
||||
|
|
|
@ -84,7 +84,7 @@ extern "C" {
|
|||
pub fn PySet_Pop(set: *mut PyObject) -> *mut PyObject;
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[cfg_attr(PyPy, link_name = "_PySet_NextEntry")]
|
||||
#[cfg_attr(PyPy, link_name = "_PyPySet_NextEntry")]
|
||||
pub fn _PySet_NextEntry(
|
||||
set: *mut PyObject,
|
||||
pos: *mut Py_ssize_t,
|
||||
|
|
|
@ -3,6 +3,7 @@ use crate::ffi::pyport::Py_ssize_t;
|
|||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(PyPy, link_name = "PyPyErr_WarnEx")]
|
||||
pub fn PyErr_WarnEx(
|
||||
category: *mut PyObject,
|
||||
message: *const c_char,
|
||||
|
|
365
src/instance.rs
365
src/instance.rs
|
@ -1,9 +1,10 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
use crate::err::{PyErr, PyResult};
|
||||
use crate::conversion::{PyTryFrom, ToBorrowedObject};
|
||||
use crate::err::{PyDowncastError, PyErr, PyResult};
|
||||
use crate::gil;
|
||||
use crate::object::PyObject;
|
||||
use crate::pycell::{PyBorrowError, PyBorrowMutError, PyCell};
|
||||
use crate::type_object::PyBorrowFlagLayout;
|
||||
use crate::types::{PyDict, PyTuple};
|
||||
use crate::{
|
||||
ffi, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, PyAny, PyClass, PyClassInitializer,
|
||||
PyRef, PyRefMut, PyTypeInfo, Python, ToPyObject,
|
||||
|
@ -114,59 +115,6 @@ where
|
|||
}
|
||||
|
||||
impl<T> Py<T> {
|
||||
/// Creates a `Py<T>` instance for the given FFI pointer.
|
||||
///
|
||||
/// This moves ownership over the pointer into the `Py<T>`.
|
||||
/// Undefined behavior if the pointer is NULL or invalid.
|
||||
#[inline]
|
||||
pub unsafe fn from_owned_ptr(_py: Python, ptr: *mut ffi::PyObject) -> Py<T> {
|
||||
debug_assert!(
|
||||
!ptr.is_null() && ffi::Py_REFCNT(ptr) > 0,
|
||||
format!("REFCNT: {:?} - {:?}", ptr, ffi::Py_REFCNT(ptr))
|
||||
);
|
||||
Py(NonNull::new_unchecked(ptr), PhantomData)
|
||||
}
|
||||
|
||||
/// Creates a `Py<T>` instance for the given FFI pointer.
|
||||
///
|
||||
/// Panics if the pointer is NULL.
|
||||
/// Undefined behavior if the pointer is invalid.
|
||||
#[inline]
|
||||
pub unsafe fn from_owned_ptr_or_panic(_py: Python, ptr: *mut ffi::PyObject) -> Py<T> {
|
||||
match NonNull::new(ptr) {
|
||||
Some(nonnull_ptr) => Py(nonnull_ptr, PhantomData),
|
||||
None => {
|
||||
crate::err::panic_after_error(_py);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct `Py<T>` from the result of a Python FFI call that
|
||||
///
|
||||
/// Returns a new reference (owned pointer).
|
||||
/// Returns `Err(PyErr)` if the pointer is NULL.
|
||||
/// Unsafe because the pointer might be invalid.
|
||||
pub unsafe fn from_owned_ptr_or_err(py: Python, ptr: *mut ffi::PyObject) -> PyResult<Py<T>> {
|
||||
match NonNull::new(ptr) {
|
||||
Some(nonnull_ptr) => Ok(Py(nonnull_ptr, PhantomData)),
|
||||
None => Err(PyErr::fetch(py)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a `Py<T>` instance for the given Python FFI pointer.
|
||||
///
|
||||
/// Calls `Py_INCREF()` on the ptr.
|
||||
/// Undefined behavior if the pointer is NULL or invalid.
|
||||
#[inline]
|
||||
pub unsafe fn from_borrowed_ptr(_py: Python, ptr: *mut ffi::PyObject) -> Py<T> {
|
||||
debug_assert!(
|
||||
!ptr.is_null() && ffi::Py_REFCNT(ptr) > 0,
|
||||
format!("REFCNT: {:?} - {:?}", ptr, ffi::Py_REFCNT(ptr))
|
||||
);
|
||||
ffi::Py_INCREF(ptr);
|
||||
Py(NonNull::new_unchecked(ptr), PhantomData)
|
||||
}
|
||||
|
||||
/// Gets the reference count of the `ffi::PyObject` pointer.
|
||||
#[inline]
|
||||
pub fn get_refcnt(&self, _py: Python) -> isize {
|
||||
|
@ -179,10 +127,228 @@ impl<T> Py<T> {
|
|||
unsafe { Py::from_borrowed_ptr(py, self.0.as_ptr()) }
|
||||
}
|
||||
|
||||
/// Returns the inner pointer without decreasing the refcount.
|
||||
/// Returns whether the object is considered to be None.
|
||||
///
|
||||
/// This will eventually move into its own trait.
|
||||
pub(crate) fn into_non_null(self) -> NonNull<ffi::PyObject> {
|
||||
/// This is equivalent to the Python expression `self is None`.
|
||||
pub fn is_none(&self, _py: Python) -> bool {
|
||||
unsafe { ffi::Py_None() == self.as_ptr() }
|
||||
}
|
||||
|
||||
/// Returns whether the object is considered to be true.
|
||||
///
|
||||
/// This is equivalent to the Python expression `bool(self)`.
|
||||
pub fn is_true(&self, py: Python) -> PyResult<bool> {
|
||||
let v = unsafe { ffi::PyObject_IsTrue(self.as_ptr()) };
|
||||
if v == -1 {
|
||||
Err(PyErr::fetch(py))
|
||||
} else {
|
||||
Ok(v != 0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extracts some type from the Python object.
|
||||
///
|
||||
/// This is a wrapper function around `FromPyObject::extract()`.
|
||||
pub fn extract<'p, D>(&'p self, py: Python<'p>) -> PyResult<D>
|
||||
where
|
||||
D: FromPyObject<'p>,
|
||||
{
|
||||
FromPyObject::extract(unsafe { py.from_borrowed_ptr(self.as_ptr()) })
|
||||
}
|
||||
|
||||
/// Retrieves an attribute value.
|
||||
///
|
||||
/// This is equivalent to the Python expression `self.attr_name`.
|
||||
pub fn getattr<N>(&self, py: Python, attr_name: N) -> PyResult<PyObject>
|
||||
where
|
||||
N: ToPyObject,
|
||||
{
|
||||
attr_name.with_borrowed_ptr(py, |attr_name| unsafe {
|
||||
PyObject::from_owned_ptr_or_err(py, ffi::PyObject_GetAttr(self.as_ptr(), attr_name))
|
||||
})
|
||||
}
|
||||
|
||||
/// Calls the object.
|
||||
///
|
||||
/// This is equivalent to the Python expression `self(*args, **kwargs)`.
|
||||
pub fn call(
|
||||
&self,
|
||||
py: Python,
|
||||
args: impl IntoPy<Py<PyTuple>>,
|
||||
kwargs: Option<&PyDict>,
|
||||
) -> PyResult<PyObject> {
|
||||
let args = args.into_py(py).into_ptr();
|
||||
let kwargs = kwargs.into_ptr();
|
||||
let result = unsafe {
|
||||
PyObject::from_owned_ptr_or_err(py, ffi::PyObject_Call(self.as_ptr(), args, kwargs))
|
||||
};
|
||||
unsafe {
|
||||
ffi::Py_XDECREF(args);
|
||||
ffi::Py_XDECREF(kwargs);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Calls the object with only positional arguments.
|
||||
///
|
||||
/// This is equivalent to the Python expression `self(*args)`.
|
||||
pub fn call1(&self, py: Python, args: impl IntoPy<Py<PyTuple>>) -> PyResult<PyObject> {
|
||||
self.call(py, args, None)
|
||||
}
|
||||
|
||||
/// Calls the object without arguments.
|
||||
///
|
||||
/// This is equivalent to the Python expression `self()`.
|
||||
pub fn call0(&self, py: Python) -> PyResult<PyObject> {
|
||||
self.call(py, (), None)
|
||||
}
|
||||
|
||||
/// Calls a method on the object.
|
||||
///
|
||||
/// This is equivalent to the Python expression `self.name(*args, **kwargs)`.
|
||||
pub fn call_method(
|
||||
&self,
|
||||
py: Python,
|
||||
name: &str,
|
||||
args: impl IntoPy<Py<PyTuple>>,
|
||||
kwargs: Option<&PyDict>,
|
||||
) -> PyResult<PyObject> {
|
||||
name.with_borrowed_ptr(py, |name| unsafe {
|
||||
let args = args.into_py(py).into_ptr();
|
||||
let kwargs = kwargs.into_ptr();
|
||||
let ptr = ffi::PyObject_GetAttr(self.as_ptr(), name);
|
||||
if ptr.is_null() {
|
||||
return Err(PyErr::fetch(py));
|
||||
}
|
||||
let result = PyObject::from_owned_ptr_or_err(py, ffi::PyObject_Call(ptr, args, kwargs));
|
||||
ffi::Py_DECREF(ptr);
|
||||
ffi::Py_XDECREF(args);
|
||||
ffi::Py_XDECREF(kwargs);
|
||||
result
|
||||
})
|
||||
}
|
||||
|
||||
/// Calls a method on the object with only positional arguments.
|
||||
///
|
||||
/// This is equivalent to the Python expression `self.name(*args)`.
|
||||
pub fn call_method1(
|
||||
&self,
|
||||
py: Python,
|
||||
name: &str,
|
||||
args: impl IntoPy<Py<PyTuple>>,
|
||||
) -> PyResult<PyObject> {
|
||||
self.call_method(py, name, args, None)
|
||||
}
|
||||
|
||||
/// Calls a method on the object with no arguments.
|
||||
///
|
||||
/// This is equivalent to the Python expression `self.name()`.
|
||||
pub fn call_method0(&self, py: Python, name: &str) -> PyResult<PyObject> {
|
||||
self.call_method(py, name, (), None)
|
||||
}
|
||||
|
||||
/// Create a `Py<T>` instance by taking ownership of the given FFI pointer.
|
||||
///
|
||||
/// # Safety
|
||||
/// `ptr` must be a pointer to a Python object of type T.
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if `ptr` is null.
|
||||
#[inline]
|
||||
pub unsafe fn from_owned_ptr(py: Python, ptr: *mut ffi::PyObject) -> Py<T> {
|
||||
match NonNull::new(ptr) {
|
||||
Some(nonnull_ptr) => Py(nonnull_ptr, PhantomData),
|
||||
None => crate::err::panic_after_error(py),
|
||||
}
|
||||
}
|
||||
|
||||
/// Deprecated alias for [`from_owned_ptr`](#method.from_owned_ptr).
|
||||
#[inline]
|
||||
#[deprecated = "this is a deprecated alias for Py::from_owned_ptr"]
|
||||
pub unsafe fn from_owned_ptr_or_panic(py: Python, ptr: *mut ffi::PyObject) -> Py<T> {
|
||||
Py::from_owned_ptr(py, ptr)
|
||||
}
|
||||
|
||||
/// Create a `Py<T>` instance by taking ownership of the given FFI pointer.
|
||||
///
|
||||
/// If `ptr` is null then the current Python exception is fetched as a `PyErr`.
|
||||
///
|
||||
/// # Safety
|
||||
/// If non-null, `ptr` must be a pointer to a Python object of type T.
|
||||
#[inline]
|
||||
pub unsafe fn from_owned_ptr_or_err(py: Python, ptr: *mut ffi::PyObject) -> PyResult<Py<T>> {
|
||||
match NonNull::new(ptr) {
|
||||
Some(nonnull_ptr) => Ok(Py(nonnull_ptr, PhantomData)),
|
||||
None => Err(PyErr::fetch(py)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `Py<T>` instance by taking ownership of the given FFI pointer.
|
||||
///
|
||||
/// If `ptr` is null then `None` is returned.
|
||||
///
|
||||
/// # Safety
|
||||
/// If non-null, `ptr` must be a pointer to a Python object of type T.
|
||||
#[inline]
|
||||
pub unsafe fn from_owned_ptr_or_opt(_py: Python, ptr: *mut ffi::PyObject) -> Option<Self> {
|
||||
match NonNull::new(ptr) {
|
||||
Some(nonnull_ptr) => Some(Py(nonnull_ptr, PhantomData)),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `Py<T>` instance by creating a new reference from the given FFI pointer.
|
||||
///
|
||||
/// # Safety
|
||||
/// `ptr` must be a pointer to a Python object of type T.
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if `ptr` is null.
|
||||
#[inline]
|
||||
pub unsafe fn from_borrowed_ptr(py: Python, ptr: *mut ffi::PyObject) -> Py<T> {
|
||||
match Self::from_borrowed_ptr_or_opt(py, ptr) {
|
||||
Some(slf) => slf,
|
||||
None => crate::err::panic_after_error(py),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `Py<T>` instance by creating a new reference from the given FFI pointer.
|
||||
///
|
||||
/// If `ptr` is null then the current Python exception is fetched as a `PyErr`.
|
||||
///
|
||||
/// # Safety
|
||||
/// `ptr` must be a pointer to a Python object of type T.
|
||||
#[inline]
|
||||
pub unsafe fn from_borrowed_ptr_or_err(py: Python, ptr: *mut ffi::PyObject) -> PyResult<Self> {
|
||||
Self::from_borrowed_ptr_or_opt(py, ptr).ok_or_else(|| PyErr::fetch(py))
|
||||
}
|
||||
|
||||
/// Create a `Py<T>` instance by creating a new reference from the given FFI pointer.
|
||||
///
|
||||
/// If `ptr` is null then `None` is returned.
|
||||
///
|
||||
/// # Safety
|
||||
/// `ptr` must be a pointer to a Python object of type T.
|
||||
#[inline]
|
||||
pub unsafe fn from_borrowed_ptr_or_opt(_py: Python, ptr: *mut ffi::PyObject) -> Option<Self> {
|
||||
NonNull::new(ptr).map(|nonnull_ptr| {
|
||||
ffi::Py_INCREF(ptr);
|
||||
Py(nonnull_ptr, PhantomData)
|
||||
})
|
||||
}
|
||||
|
||||
/// For internal conversions.
|
||||
///
|
||||
/// # Safety
|
||||
/// `ptr` must point to a Python object of type T.
|
||||
#[inline]
|
||||
unsafe fn from_non_null(ptr: NonNull<ffi::PyObject>) -> Self {
|
||||
Self(ptr, PhantomData)
|
||||
}
|
||||
|
||||
/// Returns the inner pointer without decreasing the refcount.
|
||||
#[inline]
|
||||
fn into_non_null(self) -> NonNull<ffi::PyObject> {
|
||||
let pointer = self.0;
|
||||
mem::forget(self);
|
||||
pointer
|
||||
|
@ -253,7 +419,7 @@ impl<T> IntoPy<PyObject> for Py<T> {
|
|||
/// Consumes `self` without calling `Py_DECREF()`.
|
||||
#[inline]
|
||||
fn into_py(self, _py: Python) -> PyObject {
|
||||
unsafe { PyObject::from_not_null(self.into_non_null()) }
|
||||
unsafe { PyObject::from_non_null(self.into_non_null()) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -274,16 +440,25 @@ impl<T> IntoPyPointer for Py<T> {
|
|||
}
|
||||
}
|
||||
|
||||
// Native types `&T` can be converted to `Py<T>`
|
||||
impl<'a, T> std::convert::From<&'a T> for Py<T>
|
||||
impl<T> std::convert::From<&'_ T> for PyObject
|
||||
where
|
||||
T: AsPyPointer + PyNativeType,
|
||||
{
|
||||
fn from(obj: &'a T) -> Self {
|
||||
fn from(obj: &T) -> Self {
|
||||
unsafe { Py::from_borrowed_ptr(obj.py(), obj.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::convert::From<Py<T>> for PyObject
|
||||
where
|
||||
T: AsRef<PyAny>,
|
||||
{
|
||||
fn from(other: Py<T>) -> Self {
|
||||
let Py(ptr, _) = other;
|
||||
Py(ptr, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
// `&PyCell<T>` can be converted to `Py<T>`
|
||||
impl<'a, T> std::convert::From<&PyCell<T>> for Py<T>
|
||||
where
|
||||
|
@ -337,31 +512,6 @@ impl<T> Drop for Py<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> std::convert::From<Py<T>> for PyObject {
|
||||
#[inline]
|
||||
fn from(ob: Py<T>) -> Self {
|
||||
unsafe { PyObject::from_not_null(ob.into_non_null()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> std::convert::From<&'a T> for PyObject
|
||||
where
|
||||
T: AsPyPointer + PyNativeType,
|
||||
{
|
||||
fn from(ob: &'a T) -> Self {
|
||||
unsafe { Py::<T>::from_borrowed_ptr(ob.py(), ob.as_ptr()) }.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> std::convert::From<&'a mut T> for PyObject
|
||||
where
|
||||
T: AsPyPointer + PyNativeType,
|
||||
{
|
||||
fn from(ob: &'a mut T) -> Self {
|
||||
unsafe { Py::<T>::from_borrowed_ptr(ob.py(), ob.as_ptr()) }.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> FromPyObject<'a> for Py<T>
|
||||
where
|
||||
T: PyTypeInfo,
|
||||
|
@ -406,16 +556,49 @@ impl<T> std::fmt::Debug for Py<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// A commonly-used alias for `Py<PyAny>`.
|
||||
///
|
||||
/// This is an owned reference a Python object without any type information. This value can also be
|
||||
/// safely sent between threads.
|
||||
///
|
||||
/// See the documentation for [`Py`](struct.Py.html).
|
||||
pub type PyObject = Py<PyAny>;
|
||||
|
||||
impl PyObject {
|
||||
/// Casts the PyObject to a concrete Python object type.
|
||||
///
|
||||
/// This can cast only to native Python types, not types implemented in Rust. For a more
|
||||
/// flexible alternative, see [`Py::extract`](struct.Py.html#method.extract).
|
||||
pub fn cast_as<'p, D>(&'p self, py: Python<'p>) -> Result<&'p D, PyDowncastError>
|
||||
where
|
||||
D: PyTryFrom<'p>,
|
||||
{
|
||||
D::try_from(unsafe { py.from_borrowed_ptr::<PyAny>(self.as_ptr()) })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::Py;
|
||||
use crate::ffi;
|
||||
use super::{Py, PyObject};
|
||||
use crate::types::PyDict;
|
||||
use crate::{AsPyPointer, Python};
|
||||
use crate::{ffi, AsPyPointer, Python};
|
||||
|
||||
#[test]
|
||||
fn test_call_for_non_existing_method() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let obj: PyObject = PyDict::new(py).into();
|
||||
assert!(obj.call_method0(py, "asdf").is_err());
|
||||
assert!(obj
|
||||
.call_method(py, "nonexistent_method", (1,), None)
|
||||
.is_err());
|
||||
assert!(obj.call_method0(py, "nonexistent_method").is_err());
|
||||
assert!(obj.call_method1(py, "nonexistent_method", (1,)).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn py_from_dict() {
|
||||
let dict = {
|
||||
let dict: Py<PyDict> = {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let native = PyDict::new(py);
|
||||
|
|
|
@ -135,13 +135,12 @@
|
|||
|
||||
pub use crate::class::*;
|
||||
pub use crate::conversion::{
|
||||
AsPyPointer, FromPy, FromPyObject, FromPyPointer, IntoPy, IntoPyPointer, PyTryFrom, PyTryInto,
|
||||
AsPyPointer, FromPyObject, FromPyPointer, IntoPy, IntoPyPointer, PyTryFrom, PyTryInto,
|
||||
ToBorrowedObject, ToPyObject,
|
||||
};
|
||||
pub use crate::err::{PyDowncastError, PyErr, PyErrArguments, PyErrValue, PyResult};
|
||||
pub use crate::gil::{GILGuard, GILPool};
|
||||
pub use crate::instance::{AsPyRef, Py, PyNativeType};
|
||||
pub use crate::object::PyObject;
|
||||
pub use crate::instance::{AsPyRef, Py, PyNativeType, PyObject};
|
||||
pub use crate::pycell::{PyCell, PyRef, PyRefMut};
|
||||
pub use crate::pyclass::PyClass;
|
||||
pub use crate::pyclass_init::PyClassInitializer;
|
||||
|
@ -184,7 +183,6 @@ mod instance;
|
|||
#[macro_use]
|
||||
mod internal_tricks;
|
||||
pub mod marshal;
|
||||
mod object;
|
||||
pub mod once_cell;
|
||||
pub mod panic;
|
||||
pub mod prelude;
|
||||
|
|
347
src/object.rs
347
src/object.rs
|
@ -1,347 +0,0 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
use crate::err::{PyDowncastError, PyErr, PyResult};
|
||||
use crate::ffi;
|
||||
use crate::gil;
|
||||
use crate::instance::{AsPyRef, PyNativeType};
|
||||
use crate::types::{PyAny, PyDict, PyTuple};
|
||||
use crate::{AsPyPointer, Py, Python};
|
||||
use crate::{FromPyObject, IntoPy, IntoPyPointer, PyTryFrom, ToBorrowedObject, ToPyObject};
|
||||
use std::ptr::NonNull;
|
||||
|
||||
/// A Python object of any type.
|
||||
///
|
||||
/// The Python object's lifetime is managed by Python's garbage collector,
|
||||
/// so to access the object API, a `Python<'py>` GIL token is required.
|
||||
///
|
||||
/// See [the guide](https://pyo3.rs/master/types.html) for an explanation
|
||||
/// of the different Python object types.
|
||||
///
|
||||
/// Technically, it is a safe wrapper around `NonNull<ffi::PyObject>`.
|
||||
#[derive(Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct PyObject(NonNull<ffi::PyObject>);
|
||||
|
||||
// `PyObject` is thread-safe, any Python related operations require a Python<'p> token.
|
||||
unsafe impl Send for PyObject {}
|
||||
unsafe impl Sync for PyObject {}
|
||||
|
||||
impl PyObject {
|
||||
/// For internal conversions
|
||||
pub(crate) unsafe fn from_not_null(ptr: NonNull<ffi::PyObject>) -> PyObject {
|
||||
PyObject(ptr)
|
||||
}
|
||||
|
||||
/// Creates a `PyObject` instance for the given FFI pointer.
|
||||
/// This moves ownership over the pointer into the `PyObject`.
|
||||
/// Undefined behavior if the pointer is NULL or invalid.
|
||||
#[inline]
|
||||
pub unsafe fn from_owned_ptr(_py: Python, ptr: *mut ffi::PyObject) -> PyObject {
|
||||
debug_assert!(
|
||||
!ptr.is_null() && ffi::Py_REFCNT(ptr) > 0,
|
||||
format!("REFCNT: {:?} - {:?}", ptr, ffi::Py_REFCNT(ptr))
|
||||
);
|
||||
PyObject(NonNull::new_unchecked(ptr))
|
||||
}
|
||||
|
||||
/// Creates a `PyObject` instance for the given FFI pointer.
|
||||
/// Panics if the pointer is NULL.
|
||||
/// Undefined behavior if the pointer is invalid.
|
||||
#[inline]
|
||||
pub unsafe fn from_owned_ptr_or_panic(py: Python, ptr: *mut ffi::PyObject) -> PyObject {
|
||||
match NonNull::new(ptr) {
|
||||
Some(nonnull_ptr) => PyObject(nonnull_ptr),
|
||||
None => {
|
||||
crate::err::panic_after_error(py);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs a `PyObject` from the result of a Python FFI call that
|
||||
/// returns a new reference (owned pointer).
|
||||
/// Returns `Err(PyErr)` if the pointer is NULL.
|
||||
pub unsafe fn from_owned_ptr_or_err(py: Python, ptr: *mut ffi::PyObject) -> PyResult<PyObject> {
|
||||
match NonNull::new(ptr) {
|
||||
Some(nonnull_ptr) => Ok(PyObject(nonnull_ptr)),
|
||||
None => Err(PyErr::fetch(py)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs a `PyObject` from the result of a Python FFI call that
|
||||
/// returns a new reference (owned pointer).
|
||||
/// Returns `None` if the pointer is NULL.
|
||||
pub unsafe fn from_owned_ptr_or_opt(_py: Python, ptr: *mut ffi::PyObject) -> Option<PyObject> {
|
||||
match NonNull::new(ptr) {
|
||||
Some(nonnull_ptr) => Some(PyObject(nonnull_ptr)),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a `PyObject` instance for the given Python FFI pointer.
|
||||
/// Calls `Py_INCREF()` on the ptr.
|
||||
/// Undefined behavior if the pointer is NULL or invalid.
|
||||
#[inline]
|
||||
pub unsafe fn from_borrowed_ptr(_py: Python, ptr: *mut ffi::PyObject) -> PyObject {
|
||||
debug_assert!(
|
||||
!ptr.is_null() && ffi::Py_REFCNT(ptr) > 0,
|
||||
format!("REFCNT: {:?} - {:?}", ptr, ffi::Py_REFCNT(ptr))
|
||||
);
|
||||
ffi::Py_INCREF(ptr);
|
||||
PyObject(NonNull::new_unchecked(ptr))
|
||||
}
|
||||
|
||||
/// Creates a `PyObject` instance for the given Python FFI pointer.
|
||||
/// Calls `Py_INCREF()` on the ptr.
|
||||
/// Returns `Err(PyErr)` if the pointer is NULL.
|
||||
pub unsafe fn from_borrowed_ptr_or_err(
|
||||
py: Python,
|
||||
ptr: *mut ffi::PyObject,
|
||||
) -> PyResult<PyObject> {
|
||||
if ptr.is_null() {
|
||||
Err(PyErr::fetch(py))
|
||||
} else {
|
||||
Ok(PyObject::from_borrowed_ptr(py, ptr))
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a `PyObject` instance for the given Python FFI pointer.
|
||||
/// Calls `Py_INCREF()` on the ptr.
|
||||
/// Returns `None` if the pointer is NULL.
|
||||
pub unsafe fn from_borrowed_ptr_or_opt(
|
||||
py: Python,
|
||||
ptr: *mut ffi::PyObject,
|
||||
) -> Option<PyObject> {
|
||||
if ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(PyObject::from_borrowed_ptr(py, ptr))
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the reference count of the ffi::PyObject pointer.
|
||||
pub fn get_refcnt(&self, _py: Python) -> isize {
|
||||
unsafe { ffi::Py_REFCNT(self.0.as_ptr()) }
|
||||
}
|
||||
|
||||
/// Clones self by calling `Py_INCREF()` on the ptr.
|
||||
pub fn clone_ref(&self, py: Python) -> Self {
|
||||
unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) }
|
||||
}
|
||||
|
||||
/// Returns whether the object is considered to be None.
|
||||
///
|
||||
/// This is equivalent to the Python expression `self is None`.
|
||||
pub fn is_none(&self, _py: Python) -> bool {
|
||||
unsafe { ffi::Py_None() == self.as_ptr() }
|
||||
}
|
||||
|
||||
/// Returns whether the object is considered to be true.
|
||||
///
|
||||
/// This is equivalent to the Python expression `bool(self)`.
|
||||
pub fn is_true(&self, py: Python) -> PyResult<bool> {
|
||||
let v = unsafe { ffi::PyObject_IsTrue(self.as_ptr()) };
|
||||
if v == -1 {
|
||||
Err(PyErr::fetch(py))
|
||||
} else {
|
||||
Ok(v != 0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Casts the PyObject to a concrete Python object type.
|
||||
///
|
||||
/// This can cast only to native Python types, not types implemented in Rust.
|
||||
pub fn cast_as<'p, D>(&'p self, py: Python<'p>) -> Result<&'p D, PyDowncastError>
|
||||
where
|
||||
D: PyTryFrom<'p>,
|
||||
{
|
||||
D::try_from(self.as_ref(py))
|
||||
}
|
||||
|
||||
/// Extracts some type from the Python object.
|
||||
///
|
||||
/// This is a wrapper function around `FromPyObject::extract()`.
|
||||
pub fn extract<'p, D>(&'p self, py: Python<'p>) -> PyResult<D>
|
||||
where
|
||||
D: FromPyObject<'p>,
|
||||
{
|
||||
FromPyObject::extract(self.as_ref(py))
|
||||
}
|
||||
|
||||
/// Retrieves an attribute value.
|
||||
///
|
||||
/// This is equivalent to the Python expression `self.attr_name`.
|
||||
pub fn getattr<N>(&self, py: Python, attr_name: N) -> PyResult<PyObject>
|
||||
where
|
||||
N: ToPyObject,
|
||||
{
|
||||
attr_name.with_borrowed_ptr(py, |attr_name| unsafe {
|
||||
PyObject::from_owned_ptr_or_err(py, ffi::PyObject_GetAttr(self.as_ptr(), attr_name))
|
||||
})
|
||||
}
|
||||
|
||||
/// Calls the object.
|
||||
///
|
||||
/// This is equivalent to the Python expression `self(*args, **kwargs)`.
|
||||
pub fn call(
|
||||
&self,
|
||||
py: Python,
|
||||
args: impl IntoPy<Py<PyTuple>>,
|
||||
kwargs: Option<&PyDict>,
|
||||
) -> PyResult<PyObject> {
|
||||
let args = args.into_py(py).into_ptr();
|
||||
let kwargs = kwargs.into_ptr();
|
||||
let result = unsafe {
|
||||
PyObject::from_owned_ptr_or_err(py, ffi::PyObject_Call(self.as_ptr(), args, kwargs))
|
||||
};
|
||||
unsafe {
|
||||
ffi::Py_XDECREF(args);
|
||||
ffi::Py_XDECREF(kwargs);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Calls the object with only positional arguments.
|
||||
///
|
||||
/// This is equivalent to the Python expression `self(*args)`.
|
||||
pub fn call1(&self, py: Python, args: impl IntoPy<Py<PyTuple>>) -> PyResult<PyObject> {
|
||||
self.call(py, args, None)
|
||||
}
|
||||
|
||||
/// Calls the object without arguments.
|
||||
///
|
||||
/// This is equivalent to the Python expression `self()`.
|
||||
pub fn call0(&self, py: Python) -> PyResult<PyObject> {
|
||||
self.call(py, (), None)
|
||||
}
|
||||
|
||||
/// Calls a method on the object.
|
||||
///
|
||||
/// This is equivalent to the Python expression `self.name(*args, **kwargs)`.
|
||||
pub fn call_method(
|
||||
&self,
|
||||
py: Python,
|
||||
name: &str,
|
||||
args: impl IntoPy<Py<PyTuple>>,
|
||||
kwargs: Option<&PyDict>,
|
||||
) -> PyResult<PyObject> {
|
||||
name.with_borrowed_ptr(py, |name| unsafe {
|
||||
let args = args.into_py(py).into_ptr();
|
||||
let kwargs = kwargs.into_ptr();
|
||||
let ptr = ffi::PyObject_GetAttr(self.as_ptr(), name);
|
||||
if ptr.is_null() {
|
||||
return Err(PyErr::fetch(py));
|
||||
}
|
||||
let result = PyObject::from_owned_ptr_or_err(py, ffi::PyObject_Call(ptr, args, kwargs));
|
||||
ffi::Py_DECREF(ptr);
|
||||
ffi::Py_XDECREF(args);
|
||||
ffi::Py_XDECREF(kwargs);
|
||||
result
|
||||
})
|
||||
}
|
||||
|
||||
/// Calls a method on the object with only positional arguments.
|
||||
///
|
||||
/// This is equivalent to the Python expression `self.name(*args)`.
|
||||
pub fn call_method1(
|
||||
&self,
|
||||
py: Python,
|
||||
name: &str,
|
||||
args: impl IntoPy<Py<PyTuple>>,
|
||||
) -> PyResult<PyObject> {
|
||||
self.call_method(py, name, args, None)
|
||||
}
|
||||
|
||||
/// Calls a method on the object with no arguments.
|
||||
///
|
||||
/// This is equivalent to the Python expression `self.name()`.
|
||||
pub fn call_method0(&self, py: Python, name: &str) -> PyResult<PyObject> {
|
||||
self.call_method(py, name, (), None)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsPyRef for PyObject {
|
||||
type Target = PyAny;
|
||||
fn as_ref<'p>(&'p self, _py: Python<'p>) -> &'p PyAny {
|
||||
unsafe { &*(self.as_ptr() as *const PyAny) }
|
||||
}
|
||||
}
|
||||
|
||||
impl ToPyObject for PyObject {
|
||||
#[inline]
|
||||
fn to_object(&self, py: Python) -> PyObject {
|
||||
unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsPyPointer for PyObject {
|
||||
/// Gets the underlying FFI pointer, returns a borrowed pointer.
|
||||
#[inline]
|
||||
fn as_ptr(&self) -> *mut ffi::PyObject {
|
||||
self.0.as_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoPyPointer for PyObject {
|
||||
/// Gets the underlying FFI pointer, returns a owned pointer.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
fn into_ptr(self) -> *mut ffi::PyObject {
|
||||
let ptr = self.0.as_ptr();
|
||||
std::mem::forget(self); // Avoid Drop
|
||||
ptr
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for PyObject {
|
||||
/// Checks for pointer identity, not equivalent to Python's `__eq__`.
|
||||
#[inline]
|
||||
fn eq(&self, o: &PyObject) -> bool {
|
||||
self.0 == o.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromPyObject<'a> for PyObject {
|
||||
#[inline]
|
||||
/// Extracts `Self` from the source `PyObject`.
|
||||
fn extract(ob: &'a PyAny) -> PyResult<Self> {
|
||||
unsafe { Ok(PyObject::from_borrowed_ptr(ob.py(), ob.as_ptr())) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for PyObject {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
gil::register_incref(self.0);
|
||||
}
|
||||
Self(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Dropping a `PyObject` instance decrements the reference count on the object by 1.
|
||||
impl Drop for PyObject {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
gil::register_decref(self.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::types::PyDict;
|
||||
use crate::PyObject;
|
||||
use crate::Python;
|
||||
|
||||
#[test]
|
||||
fn test_call_for_non_existing_method() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let obj: PyObject = PyDict::new(py).into();
|
||||
assert!(obj.call_method0(py, "asdf").is_err());
|
||||
assert!(obj
|
||||
.call_method(py, "nonexistent_method", (1,), None)
|
||||
.is_err());
|
||||
assert!(obj.call_method0(py, "nonexistent_method").is_err());
|
||||
assert!(obj.call_method1(py, "nonexistent_method", (1,)).is_err());
|
||||
}
|
||||
}
|
|
@ -12,12 +12,11 @@
|
|||
|
||||
pub use crate::err::{PyErr, PyResult};
|
||||
pub use crate::gil::GILGuard;
|
||||
pub use crate::instance::{AsPyRef, Py};
|
||||
pub use crate::object::PyObject;
|
||||
pub use crate::instance::{AsPyRef, Py, PyObject};
|
||||
pub use crate::pycell::{PyCell, PyRef, PyRefMut};
|
||||
pub use crate::pyclass_init::PyClassInitializer;
|
||||
pub use crate::python::Python;
|
||||
pub use crate::{FromPy, FromPyObject, IntoPy, IntoPyPointer, PyTryFrom, PyTryInto, ToPyObject};
|
||||
pub use crate::{FromPyObject, IntoPy, IntoPyPointer, PyTryFrom, PyTryInto, ToPyObject};
|
||||
// PyModule is only part of the prelude because we need it for the pymodule function
|
||||
pub use crate::types::{PyAny, PyModule};
|
||||
#[cfg(feature = "macros")]
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::pyclass_init::PyClassInitializer;
|
|||
use crate::pyclass_slots::{PyClassDict, PyClassWeakRef};
|
||||
use crate::type_object::{PyBorrowFlagLayout, PyLayout, PySizedLayout, PyTypeInfo};
|
||||
use crate::types::PyAny;
|
||||
use crate::{ffi, FromPy, PyErr, PyNativeType, PyObject, PyResult, Python};
|
||||
use crate::{ffi, IntoPy, PyErr, PyNativeType, PyObject, PyResult, Python};
|
||||
use std::cell::{Cell, UnsafeCell};
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
@ -572,9 +572,9 @@ impl<'p, T: PyClass> Drop for PyRef<'p, T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'p, T: PyClass> FromPy<PyRef<'p, T>> for PyObject {
|
||||
fn from_py(pyref: PyRef<'p, T>, py: Python<'_>) -> PyObject {
|
||||
unsafe { PyObject::from_borrowed_ptr(py, pyref.inner.as_ptr()) }
|
||||
impl<T: PyClass> IntoPy<PyObject> for PyRef<'_, T> {
|
||||
fn into_py(self, py: Python<'_>) -> PyObject {
|
||||
unsafe { PyObject::from_borrowed_ptr(py, self.inner.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -670,9 +670,9 @@ impl<'p, T: PyClass> Drop for PyRefMut<'p, T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'p, T: PyClass> FromPy<PyRefMut<'p, T>> for PyObject {
|
||||
fn from_py(pyref: PyRefMut<'p, T>, py: Python<'_>) -> PyObject {
|
||||
unsafe { PyObject::from_borrowed_ptr(py, pyref.inner.as_ptr()) }
|
||||
impl<T: PyClass> IntoPy<PyObject> for PyRefMut<'_, T> {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
unsafe { PyObject::from_borrowed_ptr(py, self.inner.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
use crate::{
|
||||
ffi, AsPyPointer, FromPy, FromPyObject, PyAny, PyObject, PyResult, PyTryFrom, Python,
|
||||
ffi, AsPyPointer, FromPyObject, IntoPy, PyAny, PyObject, PyResult, PyTryFrom, Python,
|
||||
ToPyObject,
|
||||
};
|
||||
|
||||
|
@ -41,10 +41,10 @@ impl ToPyObject for bool {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromPy<bool> for PyObject {
|
||||
impl IntoPy<PyObject> for bool {
|
||||
#[inline]
|
||||
fn from_py(other: bool, py: Python) -> Self {
|
||||
PyBool::new(py, other).into()
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
PyBool::new(py, self).into()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -166,9 +166,8 @@ impl PyByteArray {
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::exceptions;
|
||||
use crate::object::PyObject;
|
||||
use crate::types::PyByteArray;
|
||||
use crate::Python;
|
||||
use crate::{PyObject, Python};
|
||||
|
||||
#[test]
|
||||
fn test_len() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
ffi, AsPyPointer, FromPy, FromPyObject, PyAny, PyObject, PyResult, PyTryFrom, Python,
|
||||
ffi, AsPyPointer, FromPyObject, IntoPy, PyAny, PyObject, PyResult, PyTryFrom, Python,
|
||||
ToPyObject,
|
||||
};
|
||||
use std::ops::Index;
|
||||
|
@ -93,9 +93,9 @@ impl<I: SliceIndex<[u8]>> Index<I> for PyBytes {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> FromPy<&'a [u8]> for PyObject {
|
||||
fn from_py(other: &'a [u8], py: Python) -> Self {
|
||||
PyBytes::new(py, other).to_object(py)
|
||||
impl<'a> IntoPy<PyObject> for &'a [u8] {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
PyBytes::new(py, self).to_object(py)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,8 +33,7 @@ impl PyComplex {
|
|||
unsafe { ffi::PyComplex_ImagAsDouble(self.as_ptr()) }
|
||||
}
|
||||
/// Returns `|self|`.
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[cfg(not(PyPy))]
|
||||
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||
pub fn abs(&self) -> c_double {
|
||||
unsafe {
|
||||
let val = (*(self.as_ptr() as *mut ffi::PyComplexObject)).cval;
|
||||
|
@ -42,8 +41,7 @@ impl PyComplex {
|
|||
}
|
||||
}
|
||||
/// Returns `self ** other`
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[cfg(not(PyPy))]
|
||||
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||
pub fn pow(&self, other: &PyComplex) -> &PyComplex {
|
||||
unsafe {
|
||||
self.py()
|
||||
|
@ -52,8 +50,7 @@ impl PyComplex {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[cfg(not(PyPy))]
|
||||
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||
#[inline(always)]
|
||||
unsafe fn complex_operation(
|
||||
l: &PyComplex,
|
||||
|
@ -65,8 +62,7 @@ unsafe fn complex_operation(
|
|||
ffi::PyComplex_FromCComplex(operation(l_val, r_val))
|
||||
}
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[cfg(not(PyPy))]
|
||||
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||
impl<'py> Add for &'py PyComplex {
|
||||
type Output = &'py PyComplex;
|
||||
fn add(self, other: &'py PyComplex) -> &'py PyComplex {
|
||||
|
@ -77,8 +73,7 @@ impl<'py> Add for &'py PyComplex {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[cfg(not(PyPy))]
|
||||
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||
impl<'py> Sub for &'py PyComplex {
|
||||
type Output = &'py PyComplex;
|
||||
fn sub(self, other: &'py PyComplex) -> &'py PyComplex {
|
||||
|
@ -89,8 +84,7 @@ impl<'py> Sub for &'py PyComplex {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[cfg(not(PyPy))]
|
||||
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||
impl<'py> Mul for &'py PyComplex {
|
||||
type Output = &'py PyComplex;
|
||||
fn mul(self, other: &'py PyComplex) -> &'py PyComplex {
|
||||
|
@ -101,8 +95,7 @@ impl<'py> Mul for &'py PyComplex {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[cfg(not(PyPy))]
|
||||
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||
impl<'py> Div for &'py PyComplex {
|
||||
type Output = &'py PyComplex;
|
||||
fn div(self, other: &'py PyComplex) -> &'py PyComplex {
|
||||
|
@ -113,8 +106,7 @@ impl<'py> Div for &'py PyComplex {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[cfg(not(PyPy))]
|
||||
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||
impl<'py> Neg for &'py PyComplex {
|
||||
type Output = &'py PyComplex;
|
||||
fn neg(self) -> &'py PyComplex {
|
||||
|
@ -129,7 +121,7 @@ impl<'py> Neg for &'py PyComplex {
|
|||
#[cfg(feature = "num-complex")]
|
||||
mod complex_conversion {
|
||||
use super::*;
|
||||
use crate::{FromPyObject, PyAny, PyErr, PyObject, PyResult, ToPyObject};
|
||||
use crate::{FromPyObject, PyErr, PyNativeType, PyObject, PyResult, ToPyObject};
|
||||
use num_complex::Complex;
|
||||
|
||||
impl PyComplex {
|
||||
|
@ -157,11 +149,11 @@ mod complex_conversion {
|
|||
unsafe {
|
||||
let raw_obj =
|
||||
ffi::PyComplex_FromDoubles(self.re as c_double, self.im as c_double);
|
||||
PyObject::from_owned_ptr_or_panic(py, raw_obj)
|
||||
PyObject::from_owned_ptr(py, raw_obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||
#[allow(clippy::float_cmp)] // The comparison is for an error value
|
||||
impl<'source> FromPyObject<'source> for Complex<$float> {
|
||||
fn extract(obj: &'source PyAny) -> PyResult<Complex<$float>> {
|
||||
|
@ -175,7 +167,7 @@ mod complex_conversion {
|
|||
}
|
||||
}
|
||||
}
|
||||
#[cfg(Py_LIMITED_API)]
|
||||
#[cfg(any(Py_LIMITED_API, PyPy))]
|
||||
#[allow(clippy::float_cmp)] // The comparison is for an error value
|
||||
impl<'source> FromPyObject<'source> for Complex<$float> {
|
||||
fn extract(obj: &'source PyAny) -> PyResult<Complex<$float>> {
|
||||
|
@ -239,7 +231,7 @@ mod test {
|
|||
assert_approx_eq!(complex.imag(), 1.2);
|
||||
}
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||
#[test]
|
||||
fn test_add() {
|
||||
let gil = Python::acquire_gil();
|
||||
|
@ -251,7 +243,7 @@ mod test {
|
|||
assert_approx_eq!(res.imag(), 3.8);
|
||||
}
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||
#[test]
|
||||
fn test_sub() {
|
||||
let gil = Python::acquire_gil();
|
||||
|
@ -263,7 +255,7 @@ mod test {
|
|||
assert_approx_eq!(res.imag(), -1.4);
|
||||
}
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||
#[test]
|
||||
fn test_mul() {
|
||||
let gil = Python::acquire_gil();
|
||||
|
@ -275,7 +267,7 @@ mod test {
|
|||
assert_approx_eq!(res.imag(), 9.0);
|
||||
}
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||
#[test]
|
||||
fn test_div() {
|
||||
let gil = Python::acquire_gil();
|
||||
|
@ -287,7 +279,7 @@ mod test {
|
|||
assert_approx_eq!(res.imag(), -0.850_515_463_917_525_7);
|
||||
}
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||
#[test]
|
||||
fn test_neg() {
|
||||
let gil = Python::acquire_gil();
|
||||
|
@ -298,7 +290,7 @@ mod test {
|
|||
assert_approx_eq!(res.imag(), -1.2);
|
||||
}
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||
#[test]
|
||||
fn test_abs() {
|
||||
let gil = Python::acquire_gil();
|
||||
|
@ -307,7 +299,7 @@ mod test {
|
|||
assert_approx_eq!(val.abs(), 3.231_098_884_280_702_2);
|
||||
}
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||
#[test]
|
||||
fn test_pow() {
|
||||
let gil = Python::acquire_gil();
|
||||
|
|
|
@ -25,9 +25,8 @@ use crate::ffi::{
|
|||
PyDateTime_TIME_GET_HOUR, PyDateTime_TIME_GET_MICROSECOND, PyDateTime_TIME_GET_MINUTE,
|
||||
PyDateTime_TIME_GET_SECOND,
|
||||
};
|
||||
use crate::object::PyObject;
|
||||
use crate::types::PyTuple;
|
||||
use crate::{AsPyPointer, PyAny, Python, ToPyObject};
|
||||
use crate::{AsPyPointer, PyAny, PyObject, Python, ToPyObject};
|
||||
use std::os::raw::c_int;
|
||||
#[cfg(not(PyPy))]
|
||||
use std::ptr;
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
use crate::err::{self, PyErr, PyResult};
|
||||
use crate::instance::PyNativeType;
|
||||
use crate::object::PyObject;
|
||||
use crate::types::{PyAny, PyList};
|
||||
#[cfg(not(PyPy))]
|
||||
use crate::IntoPyPointer;
|
||||
use crate::{
|
||||
ffi, AsPyPointer, FromPyObject, IntoPy, PyTryFrom, Python, ToBorrowedObject, ToPyObject,
|
||||
ffi, AsPyPointer, FromPyObject, IntoPy, PyNativeType, PyObject, PyTryFrom, Python,
|
||||
ToBorrowedObject, ToPyObject,
|
||||
};
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::ptr::NonNull;
|
||||
|
@ -360,7 +359,9 @@ mod test {
|
|||
use crate::conversion::IntoPy;
|
||||
use crate::instance::AsPyRef;
|
||||
use crate::types::dict::IntoPyDict;
|
||||
use crate::types::{PyDict, PyList, PyTuple};
|
||||
#[cfg(not(PyPy))]
|
||||
use crate::types::PyList;
|
||||
use crate::types::{PyDict, PyTuple};
|
||||
use crate::PyObject;
|
||||
use crate::Python;
|
||||
use crate::{PyTryFrom, ToPyObject};
|
||||
|
@ -380,6 +381,7 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(PyPy))]
|
||||
fn test_from_sequence() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
@ -394,6 +396,7 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(PyPy))]
|
||||
fn test_from_sequence_err() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
//
|
||||
// based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython
|
||||
use crate::{
|
||||
ffi, AsPyPointer, FromPy, FromPyObject, PyAny, PyErr, PyNativeType, PyObject, PyResult, Python,
|
||||
ffi, AsPyPointer, FromPyObject, IntoPy, PyAny, PyErr, PyNativeType, PyObject, PyResult, Python,
|
||||
ToPyObject,
|
||||
};
|
||||
use std::os::raw::c_double;
|
||||
|
@ -11,7 +11,7 @@ use std::os::raw::c_double;
|
|||
///
|
||||
/// You can usually avoid directly working with this type
|
||||
/// by using [`ToPyObject`](trait.ToPyObject.html)
|
||||
/// and [extract](struct.PyObject.html#method.extract)
|
||||
/// and [extract](struct.PyAny.html#method.extract)
|
||||
/// with `f32`/`f64`.
|
||||
#[repr(transparent)]
|
||||
pub struct PyFloat(PyAny);
|
||||
|
@ -41,9 +41,9 @@ impl ToPyObject for f64 {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromPy<f64> for PyObject {
|
||||
fn from_py(other: f64, py: Python) -> Self {
|
||||
PyFloat::new(py, other).into()
|
||||
impl IntoPy<PyObject> for f64 {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
PyFloat::new(py, self).into()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,9 +67,9 @@ impl ToPyObject for f32 {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromPy<f32> for PyObject {
|
||||
fn from_py(other: f32, py: Python) -> Self {
|
||||
PyFloat::new(py, f64::from(other)).into()
|
||||
impl IntoPy<PyObject> for f32 {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
PyFloat::new(py, f64::from(self)).into()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -173,7 +173,7 @@ where
|
|||
let obj = e.to_object(py).into_ptr();
|
||||
ffi::PyList_SetItem(ptr, i as Py_ssize_t, obj);
|
||||
}
|
||||
PyObject::from_owned_ptr_or_panic(py, ptr)
|
||||
PyObject::from_owned_ptr(py, ptr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -218,7 +218,7 @@ where
|
|||
let obj = e.into_py(py).into_ptr();
|
||||
ffi::PyList_SetItem(ptr, i as Py_ssize_t, obj);
|
||||
}
|
||||
PyObject::from_owned_ptr_or_panic(py, ptr)
|
||||
PyObject::from_owned_ptr(py, ptr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,6 +62,23 @@ macro_rules! pyobject_native_type_named (
|
|||
self.as_ptr() == o.as_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
impl<$($type_param,)*> $crate::IntoPy<$crate::Py<$name>> for &'_ $name {
|
||||
#[inline]
|
||||
fn into_py(self, py: $crate::Python) -> $crate::Py<$name> {
|
||||
use $crate::AsPyPointer;
|
||||
unsafe { $crate::Py::from_borrowed_ptr(py, self.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<$($type_param,)*> From<&'_ $name> for $crate::Py<$name> {
|
||||
#[inline]
|
||||
fn from(other: &$name) -> Self {
|
||||
use $crate::AsPyPointer;
|
||||
use $crate::PyNativeType;
|
||||
unsafe { $crate::Py::from_borrowed_ptr(other.py(), other.as_ptr()) }
|
||||
}
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
|
|
|
@ -6,12 +6,11 @@ use crate::err::{PyErr, PyResult};
|
|||
use crate::exceptions;
|
||||
use crate::ffi;
|
||||
use crate::instance::PyNativeType;
|
||||
use crate::object::PyObject;
|
||||
use crate::pyclass::PyClass;
|
||||
use crate::type_object::PyTypeObject;
|
||||
use crate::types::PyTuple;
|
||||
use crate::types::{PyAny, PyDict, PyList};
|
||||
use crate::{AsPyPointer, IntoPy, Py, Python, ToPyObject};
|
||||
use crate::{AsPyPointer, IntoPy, Py, PyObject, Python, ToPyObject};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::os::raw::c_char;
|
||||
use std::str;
|
||||
|
@ -117,6 +116,7 @@ impl PyModule {
|
|||
/// Returns the module's filename.
|
||||
///
|
||||
/// May fail if the module does not have a `__file__` attribute.
|
||||
#[cfg(not(all(windows, PyPy)))]
|
||||
pub fn filename(&self) -> PyResult<&str> {
|
||||
unsafe { self.str_from_ptr(ffi::PyModule_GetFilename(self.as_ptr())) }
|
||||
}
|
||||
|
|
267
src/types/num.rs
267
src/types/num.rs
|
@ -8,7 +8,7 @@ use crate::{
|
|||
};
|
||||
use std::convert::TryFrom;
|
||||
use std::i64;
|
||||
use std::os::raw::{c_int, c_long, c_uchar};
|
||||
use std::os::raw::c_long;
|
||||
|
||||
fn err_if_invalid_value<T: PartialEq>(
|
||||
py: Python,
|
||||
|
@ -46,66 +46,11 @@ macro_rules! int_fits_larger_int {
|
|||
};
|
||||
}
|
||||
|
||||
// manual implementation for 128bit integers
|
||||
#[cfg(target_endian = "little")]
|
||||
const IS_LITTLE_ENDIAN: c_int = 1;
|
||||
#[cfg(not(target_endian = "little"))]
|
||||
const IS_LITTLE_ENDIAN: c_int = 0;
|
||||
|
||||
// for 128bit Integers
|
||||
macro_rules! int_convert_128 {
|
||||
($rust_type: ty, $byte_size: expr, $is_signed: expr) => {
|
||||
impl ToPyObject for $rust_type {
|
||||
#[inline]
|
||||
fn to_object(&self, py: Python) -> PyObject {
|
||||
(*self).into_py(py)
|
||||
}
|
||||
}
|
||||
impl IntoPy<PyObject> for $rust_type {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
unsafe {
|
||||
let bytes = self.to_ne_bytes();
|
||||
let obj = ffi::_PyLong_FromByteArray(
|
||||
bytes.as_ptr() as *const c_uchar,
|
||||
$byte_size,
|
||||
IS_LITTLE_ENDIAN,
|
||||
$is_signed,
|
||||
);
|
||||
PyObject::from_owned_ptr_or_panic(py, obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'source> FromPyObject<'source> for $rust_type {
|
||||
fn extract(ob: &'source PyAny) -> PyResult<$rust_type> {
|
||||
unsafe {
|
||||
let num = ffi::PyNumber_Index(ob.as_ptr());
|
||||
if num.is_null() {
|
||||
return Err(PyErr::fetch(ob.py()));
|
||||
}
|
||||
let mut buffer = [0; $byte_size];
|
||||
let ok = ffi::_PyLong_AsByteArray(
|
||||
num as *mut ffi::PyLongObject,
|
||||
buffer.as_mut_ptr(),
|
||||
$byte_size,
|
||||
IS_LITTLE_ENDIAN,
|
||||
$is_signed,
|
||||
);
|
||||
if ok == -1 {
|
||||
Err(PyErr::fetch(ob.py()))
|
||||
} else {
|
||||
Ok(<$rust_type>::from_ne_bytes(buffer))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Represents a Python `int` object.
|
||||
///
|
||||
/// You can usually avoid directly working with this type
|
||||
/// by using [`ToPyObject`](trait.ToPyObject.html)
|
||||
/// and [extract](struct.PyObject.html#method.extract)
|
||||
/// and [extract](struct.PyAny.html#method.extract)
|
||||
/// with the primitive Rust integer types.
|
||||
#[repr(transparent)]
|
||||
pub struct PyLong(PyAny);
|
||||
|
@ -117,17 +62,13 @@ macro_rules! int_fits_c_long {
|
|||
impl ToPyObject for $rust_type {
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy::cast_lossless))]
|
||||
fn to_object(&self, py: Python) -> PyObject {
|
||||
unsafe {
|
||||
PyObject::from_owned_ptr_or_panic(py, ffi::PyLong_FromLong(*self as c_long))
|
||||
}
|
||||
unsafe { PyObject::from_owned_ptr(py, ffi::PyLong_FromLong(*self as c_long)) }
|
||||
}
|
||||
}
|
||||
impl IntoPy<PyObject> for $rust_type {
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy::cast_lossless))]
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
unsafe {
|
||||
PyObject::from_owned_ptr_or_panic(py, ffi::PyLong_FromLong(self as c_long))
|
||||
}
|
||||
unsafe { PyObject::from_owned_ptr(py, ffi::PyLong_FromLong(self as c_long)) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,13 +97,13 @@ macro_rules! int_convert_u64_or_i64 {
|
|||
impl ToPyObject for $rust_type {
|
||||
#[inline]
|
||||
fn to_object(&self, py: Python) -> PyObject {
|
||||
unsafe { PyObject::from_owned_ptr_or_panic(py, $pylong_from_ll_or_ull(*self)) }
|
||||
unsafe { PyObject::from_owned_ptr(py, $pylong_from_ll_or_ull(*self)) }
|
||||
}
|
||||
}
|
||||
impl IntoPy<PyObject> for $rust_type {
|
||||
#[inline]
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
unsafe { PyObject::from_owned_ptr_or_panic(py, $pylong_from_ll_or_ull(self)) }
|
||||
unsafe { PyObject::from_owned_ptr(py, $pylong_from_ll_or_ull(self)) }
|
||||
}
|
||||
}
|
||||
impl<'source> FromPyObject<'source> for $rust_type {
|
||||
|
@ -216,17 +157,139 @@ int_convert_u64_or_i64!(
|
|||
ffi::PyLong_AsUnsignedLongLong
|
||||
);
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
int_convert_128!(i128, 16, 1);
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
int_convert_128!(u128, 16, 0);
|
||||
// manual implementation for 128bit integers
|
||||
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||
mod int128_conversion {
|
||||
use crate::{
|
||||
ffi, AsPyPointer, FromPyObject, IntoPy, PyAny, PyErr, PyNativeType, PyObject, PyResult,
|
||||
Python, ToPyObject,
|
||||
};
|
||||
use std::os::raw::{c_int, c_uchar};
|
||||
|
||||
#[cfg(all(feature = "num-bigint", not(Py_LIMITED_API)))]
|
||||
#[cfg(target_endian = "little")]
|
||||
const IS_LITTLE_ENDIAN: c_int = 1;
|
||||
#[cfg(not(target_endian = "little"))]
|
||||
const IS_LITTLE_ENDIAN: c_int = 0;
|
||||
|
||||
// for 128bit Integers
|
||||
macro_rules! int_convert_128 {
|
||||
($rust_type: ty, $byte_size: expr, $is_signed: expr) => {
|
||||
impl ToPyObject for $rust_type {
|
||||
#[inline]
|
||||
fn to_object(&self, py: Python) -> PyObject {
|
||||
(*self).into_py(py)
|
||||
}
|
||||
}
|
||||
impl IntoPy<PyObject> for $rust_type {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
unsafe {
|
||||
let bytes = self.to_ne_bytes();
|
||||
let obj = ffi::_PyLong_FromByteArray(
|
||||
bytes.as_ptr() as *const c_uchar,
|
||||
$byte_size,
|
||||
IS_LITTLE_ENDIAN,
|
||||
$is_signed,
|
||||
);
|
||||
PyObject::from_owned_ptr(py, obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'source> FromPyObject<'source> for $rust_type {
|
||||
fn extract(ob: &'source PyAny) -> PyResult<$rust_type> {
|
||||
unsafe {
|
||||
let num = ffi::PyNumber_Index(ob.as_ptr());
|
||||
if num.is_null() {
|
||||
return Err(PyErr::fetch(ob.py()));
|
||||
}
|
||||
let mut buffer = [0; $byte_size];
|
||||
let ok = ffi::_PyLong_AsByteArray(
|
||||
num as *mut ffi::PyLongObject,
|
||||
buffer.as_mut_ptr(),
|
||||
$byte_size,
|
||||
IS_LITTLE_ENDIAN,
|
||||
$is_signed,
|
||||
);
|
||||
if ok == -1 {
|
||||
Err(PyErr::fetch(ob.py()))
|
||||
} else {
|
||||
Ok(<$rust_type>::from_ne_bytes(buffer))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int_convert_128!(i128, 16, 1);
|
||||
int_convert_128!(u128, 16, 0);
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{Python, ToPyObject};
|
||||
|
||||
#[test]
|
||||
fn test_i128_max() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let v = std::i128::MAX;
|
||||
let obj = v.to_object(py);
|
||||
assert_eq!(v, obj.extract::<i128>(py).unwrap());
|
||||
assert_eq!(v as u128, obj.extract::<u128>(py).unwrap());
|
||||
assert!(obj.extract::<u64>(py).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i128_min() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let v = std::i128::MIN;
|
||||
let obj = v.to_object(py);
|
||||
assert_eq!(v, obj.extract::<i128>(py).unwrap());
|
||||
assert!(obj.extract::<i64>(py).is_err());
|
||||
assert!(obj.extract::<u128>(py).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_u128_max() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let v = std::u128::MAX;
|
||||
let obj = v.to_object(py);
|
||||
assert_eq!(v, obj.extract::<u128>(py).unwrap());
|
||||
assert!(obj.extract::<i128>(py).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_u128_overflow() {
|
||||
use crate::{exceptions, ffi, PyObject};
|
||||
use std::os::raw::c_uchar;
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let overflow_bytes: [c_uchar; 20] = [255; 20];
|
||||
unsafe {
|
||||
let obj = ffi::_PyLong_FromByteArray(
|
||||
overflow_bytes.as_ptr() as *const c_uchar,
|
||||
20,
|
||||
super::IS_LITTLE_ENDIAN,
|
||||
0,
|
||||
);
|
||||
let obj = PyObject::from_owned_ptr(py, obj);
|
||||
let err = obj.extract::<u128>(py).unwrap_err();
|
||||
assert!(err.is_instance::<exceptions::PyOverflowError>(py));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "num-bigint", not(any(Py_LIMITED_API, PyPy))))]
|
||||
mod bigint_conversion {
|
||||
use super::*;
|
||||
use crate::{err, Py};
|
||||
use num_bigint::{BigInt, BigUint};
|
||||
use std::os::raw::{c_int, c_uchar};
|
||||
|
||||
#[cfg(not(all(windows, PyPy)))]
|
||||
unsafe fn extract(ob: &PyLong, buffer: &mut [c_uchar], is_signed: c_int) -> PyResult<()> {
|
||||
err::error_on_minusone(
|
||||
ob.py(),
|
||||
|
@ -252,7 +315,7 @@ mod bigint_conversion {
|
|||
1,
|
||||
$is_signed,
|
||||
);
|
||||
PyObject::from_owned_ptr_or_panic(py, obj)
|
||||
PyObject::from_owned_ptr(py, obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -497,64 +560,6 @@ mod test {
|
|||
assert!(obj.extract::<i64>(py).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
fn test_i128_max() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let v = std::i128::MAX;
|
||||
let obj = v.to_object(py);
|
||||
assert_eq!(v, obj.extract::<i128>(py).unwrap());
|
||||
assert_eq!(v as u128, obj.extract::<u128>(py).unwrap());
|
||||
assert!(obj.extract::<u64>(py).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
fn test_i128_min() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let v = std::i128::MIN;
|
||||
let obj = v.to_object(py);
|
||||
assert_eq!(v, obj.extract::<i128>(py).unwrap());
|
||||
assert!(obj.extract::<i64>(py).is_err());
|
||||
assert!(obj.extract::<u128>(py).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
fn test_u128_max() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let v = std::u128::MAX;
|
||||
let obj = v.to_object(py);
|
||||
assert_eq!(v, obj.extract::<u128>(py).unwrap());
|
||||
assert!(obj.extract::<i128>(py).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
fn test_u128_overflow() {
|
||||
use crate::exceptions;
|
||||
use crate::ffi;
|
||||
use crate::object::PyObject;
|
||||
use std::os::raw::c_uchar;
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let overflow_bytes: [c_uchar; 20] = [255; 20];
|
||||
unsafe {
|
||||
let obj = ffi::_PyLong_FromByteArray(
|
||||
overflow_bytes.as_ptr() as *const c_uchar,
|
||||
20,
|
||||
super::IS_LITTLE_ENDIAN,
|
||||
0,
|
||||
);
|
||||
let obj = PyObject::from_owned_ptr_or_panic(py, obj);
|
||||
let err = obj.extract::<u128>(py).unwrap_err();
|
||||
assert!(err.is_instance::<exceptions::PyOverflowError>(py));
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! test_common (
|
||||
($test_mod_name:ident, $t:ty) => (
|
||||
mod $test_mod_name {
|
||||
|
@ -605,8 +610,8 @@ mod test {
|
|||
test_common!(u64, u64);
|
||||
test_common!(isize, isize);
|
||||
test_common!(usize, usize);
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||
test_common!(i128, i128);
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||
test_common!(u128, u128);
|
||||
}
|
||||
|
|
|
@ -397,11 +397,10 @@ impl<'v> PyTryFrom<'v> for PySequence {
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::instance::AsPyRef;
|
||||
use crate::object::PyObject;
|
||||
use crate::types::PySequence;
|
||||
use crate::AsPyPointer;
|
||||
use crate::Python;
|
||||
use crate::{PyTryFrom, ToPyObject};
|
||||
use crate::{PyObject, PyTryFrom, ToPyObject};
|
||||
|
||||
fn get_object() -> PyObject {
|
||||
// Convenience function for getting a single unique object
|
||||
|
@ -556,6 +555,7 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(PyPy))]
|
||||
fn test_seq_count() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
use crate::err::{self, PyErr, PyResult};
|
||||
use crate::{
|
||||
ffi, AsPyPointer, FromPy, FromPyObject, IntoPy, PyAny, PyNativeType, PyObject, Python,
|
||||
ffi, AsPyPointer, FromPyObject, IntoPy, PyAny, PyNativeType, PyObject, Python,
|
||||
ToBorrowedObject, ToPyObject,
|
||||
};
|
||||
use std::cmp;
|
||||
|
@ -185,15 +185,15 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<K, S> FromPy<HashSet<K, S>> for PyObject
|
||||
impl<K, S> IntoPy<PyObject> for HashSet<K, S>
|
||||
where
|
||||
K: IntoPy<PyObject> + Eq + hash::Hash,
|
||||
S: hash::BuildHasher + Default,
|
||||
{
|
||||
fn from_py(src: HashSet<K, S>, py: Python) -> Self {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
let set = PySet::empty(py).expect("Failed to construct empty set");
|
||||
{
|
||||
for val in src {
|
||||
for val in self {
|
||||
set.add(val.into_py(py)).expect("Failed to add to set");
|
||||
}
|
||||
}
|
||||
|
@ -212,14 +212,14 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<K> FromPy<BTreeSet<K>> for PyObject
|
||||
impl<K> IntoPy<PyObject> for BTreeSet<K>
|
||||
where
|
||||
K: IntoPy<PyObject> + cmp::Ord + ToPyObject,
|
||||
K: IntoPy<PyObject> + cmp::Ord,
|
||||
{
|
||||
fn from_py(src: BTreeSet<K>, py: Python) -> Self {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
let set = PySet::empty(py).expect("Failed to construct empty set");
|
||||
{
|
||||
for val in src {
|
||||
for val in self {
|
||||
set.add(val.into_py(py)).expect("Failed to add to set");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use crate::types::PyBytes;
|
||||
use crate::{
|
||||
ffi, AsPyPointer, FromPy, FromPyObject, IntoPy, PyAny, PyErr, PyNativeType, PyObject, PyResult,
|
||||
ffi, AsPyPointer, FromPyObject, IntoPy, PyAny, PyErr, PyNativeType, PyObject, PyResult,
|
||||
PyTryFrom, Python, ToPyObject,
|
||||
};
|
||||
use std::borrow::Cow;
|
||||
|
@ -112,9 +112,9 @@ impl ToPyObject for String {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromPy<String> for PyObject {
|
||||
fn from_py(other: String, py: Python) -> Self {
|
||||
PyString::new(py, &other).into()
|
||||
impl IntoPy<PyObject> for String {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
PyString::new(py, &self).into()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -147,9 +147,8 @@ impl<'source> FromPyObject<'source> for String {
|
|||
mod test {
|
||||
use super::PyString;
|
||||
use crate::instance::AsPyRef;
|
||||
use crate::object::PyObject;
|
||||
use crate::Python;
|
||||
use crate::{FromPyObject, PyTryFrom, ToPyObject};
|
||||
use crate::{FromPyObject, PyObject, PyTryFrom, ToPyObject};
|
||||
|
||||
#[test]
|
||||
fn test_non_bmp() {
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
use crate::ffi::{self, Py_ssize_t};
|
||||
use crate::{
|
||||
exceptions, AsPyPointer, FromPy, FromPyObject, IntoPy, IntoPyPointer, Py, PyAny, PyErr,
|
||||
PyNativeType, PyObject, PyResult, PyTryFrom, Python, ToPyObject,
|
||||
exceptions, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, Py, PyAny, PyErr, PyNativeType,
|
||||
PyObject, PyResult, PyTryFrom, Python, ToPyObject,
|
||||
};
|
||||
use std::slice;
|
||||
|
||||
|
@ -129,12 +129,6 @@ impl<'a> IntoIterator for &'a PyTuple {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> FromPy<&'a PyTuple> for Py<PyTuple> {
|
||||
fn from_py(tuple: &'a PyTuple, _py: Python) -> Py<PyTuple> {
|
||||
unsafe { Py::from_borrowed_ptr(tuple.py(), tuple.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
fn wrong_tuple_length(t: &PyTuple, expected_length: usize) -> PyErr {
|
||||
let msg = format!(
|
||||
"Expected tuple of length {}, but got tuple of length {}.",
|
||||
|
@ -150,7 +144,7 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
|
|||
unsafe {
|
||||
let ptr = ffi::PyTuple_New($length);
|
||||
$(ffi::PyTuple_SetItem(ptr, $n, self.$n.to_object(py).into_ptr());)+
|
||||
PyObject::from_owned_ptr_or_panic(py, ptr)
|
||||
PyObject::from_owned_ptr(py, ptr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -159,7 +153,7 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
|
|||
unsafe {
|
||||
let ptr = ffi::PyTuple_New($length);
|
||||
$(ffi::PyTuple_SetItem(ptr, $n, self.$n.into_py(py).into_ptr());)+
|
||||
PyObject::from_owned_ptr_or_panic(py, ptr)
|
||||
PyObject::from_owned_ptr(py, ptr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -169,7 +163,7 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
|
|||
unsafe {
|
||||
let ptr = ffi::PyTuple_New($length);
|
||||
$(ffi::PyTuple_SetItem(ptr, $n, self.$n.into_py(py).into_ptr());)+
|
||||
Py::from_owned_ptr_or_panic(py, ptr)
|
||||
Py::from_owned_ptr(py, ptr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use pyo3::prelude::*;
|
||||
use pyo3::py_run;
|
||||
use pyo3::types::PyType;
|
||||
use pyo3::{py_run, PyClass};
|
||||
|
||||
mod common;
|
||||
|
||||
|
@ -166,59 +167,76 @@ fn class_with_object_field() {
|
|||
|
||||
#[pyclass(unsendable)]
|
||||
struct UnsendableBase {
|
||||
rc: std::rc::Rc<usize>,
|
||||
value: std::rc::Rc<usize>,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl UnsendableBase {
|
||||
#[new]
|
||||
fn new(value: usize) -> UnsendableBase {
|
||||
Self {
|
||||
value: std::rc::Rc::new(value),
|
||||
}
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn value(&self) -> usize {
|
||||
*self.rc.as_ref()
|
||||
*self.value
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass(extends=UnsendableBase)]
|
||||
struct UnsendableChild {}
|
||||
|
||||
#[pymethods]
|
||||
impl UnsendableChild {
|
||||
#[new]
|
||||
fn new(value: usize) -> (UnsendableChild, UnsendableBase) {
|
||||
(UnsendableChild {}, UnsendableBase::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
fn test_unsendable<T: PyClass + 'static>() -> PyResult<()> {
|
||||
let obj = std::thread::spawn(|| -> PyResult<_> {
|
||||
Python::with_gil(|py| {
|
||||
let obj: Py<T> = PyType::new::<T>(py).call1((5,))?.extract()?;
|
||||
|
||||
// Accessing the value inside this thread should not panic
|
||||
let caught_panic =
|
||||
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| -> PyResult<_> {
|
||||
assert_eq!(obj.as_ref(py).getattr("value")?.extract::<usize>()?, 5);
|
||||
Ok(())
|
||||
}))
|
||||
.is_err();
|
||||
|
||||
assert_eq!(caught_panic, false);
|
||||
Ok(obj)
|
||||
})
|
||||
})
|
||||
.join()
|
||||
.unwrap()?;
|
||||
|
||||
// This access must panic
|
||||
Python::with_gil(|py| {
|
||||
obj.borrow(py);
|
||||
});
|
||||
|
||||
panic!("Borrowing unsendable from receiving thread did not panic.");
|
||||
}
|
||||
|
||||
/// If a class is marked as `unsendable`, it panics when accessed by another thread.
|
||||
#[test]
|
||||
fn panic_unsendable() {
|
||||
if option_env!("RUSTFLAGS")
|
||||
.map(|s| s.contains("-Cpanic=abort"))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let base = || UnsendableBase {
|
||||
rc: std::rc::Rc::new(0),
|
||||
};
|
||||
let unsendable_base = PyCell::new(py, base()).unwrap();
|
||||
let unsendable_child = PyCell::new(py, (UnsendableChild {}, base())).unwrap();
|
||||
|
||||
let source = pyo3::indoc::indoc!(
|
||||
r#"
|
||||
def value():
|
||||
return unsendable.value()
|
||||
|
||||
import concurrent.futures
|
||||
executor = concurrent.futures.ThreadPoolExecutor(max_workers=1)
|
||||
future = executor.submit(value)
|
||||
try:
|
||||
result = future.result()
|
||||
assert False, 'future must panic'
|
||||
except BaseException as e:
|
||||
assert str(e) == 'test_class_basics::UnsendableBase is unsendable, but sent to another thread!'
|
||||
"#
|
||||
);
|
||||
let globals = PyModule::import(py, "__main__").unwrap().dict();
|
||||
let test = |unsendable| {
|
||||
globals.set_item("unsendable", unsendable).unwrap();
|
||||
py.run(source, Some(globals), None)
|
||||
.map_err(|e| e.print(py))
|
||||
.unwrap();
|
||||
};
|
||||
test(unsendable_base.as_ref());
|
||||
test(unsendable_child.as_ref());
|
||||
#[should_panic(
|
||||
expected = "test_class_basics::UnsendableBase is unsendable, but sent to another thread!"
|
||||
)]
|
||||
fn panic_unsendable_base() {
|
||||
test_unsendable::<UnsendableBase>().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(
|
||||
expected = "test_class_basics::UnsendableBase is unsendable, but sent to another thread!"
|
||||
)]
|
||||
fn panic_unsendable_child() {
|
||||
test_unsendable::<UnsendableChild>().unwrap();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue