Merge branch 'master' into parse_args_msg
This commit is contained in:
commit
96b71bfb76
16
.travis.yml
16
.travis.yml
|
@ -5,8 +5,6 @@ dist: xenial
|
|||
cache:
|
||||
pip: true
|
||||
cargo: true
|
||||
directories:
|
||||
- "$HOME/kcov"
|
||||
|
||||
matrix:
|
||||
include:
|
||||
|
@ -20,10 +18,10 @@ matrix:
|
|||
python: "3.8-dev"
|
||||
- name: Minimum nightly
|
||||
python: "3.7"
|
||||
# Keep this synced up with build.rs
|
||||
env: TRAVIS_RUST_VERSION=nightly-2019-02-07
|
||||
# Tested via anaconda PyPy (since travis's PyPy version is too old)
|
||||
- name: PyPy3.5 7.0
|
||||
# Keep this synced up with build.rs and ensure that the nightly version does have clippy available
|
||||
# https://static.rust-lang.org/dist/YYYY-MM-DD/clippy-nightly-x86_64-unknown-linux-gnu.tar.gz exists
|
||||
env: TRAVIS_RUST_VERSION=nightly-2019-07-19
|
||||
- name: PyPy3.5 7.0 # Tested via anaconda PyPy (since travis's PyPy version is too old)
|
||||
python: "3.7"
|
||||
env: FEATURES="pypy" PATH="$PATH:/opt/anaconda/envs/pypy3/bin"
|
||||
allow_failures:
|
||||
|
@ -47,11 +45,15 @@ addons:
|
|||
|
||||
before_install:
|
||||
- source ./ci/travis/setup.sh
|
||||
- curl -L https://github.com/mozilla/grcov/releases/latest/download/grcov-linux-x86_64.tar.bz2 | tar jxf -
|
||||
|
||||
install:
|
||||
- pip install setuptools-rust pytest pytest-benchmark tox tox-venv
|
||||
- pip install setuptools-rust pytest pytest-benchmark tox
|
||||
|
||||
script:
|
||||
# Options for grcov
|
||||
- if ! [[ $FEATURES == *"pypy"* ]]; then export CARGO_INCREMENTAL=0; fi
|
||||
- if ! [[ $FEATURES == *"pypy"* ]]; then export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Zno-landing-pads"; fi
|
||||
- ./ci/travis/test.sh
|
||||
|
||||
after_success:
|
||||
|
|
14
CHANGELOG.md
14
CHANGELOG.md
|
@ -5,12 +5,20 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
## [0.8.0]
|
||||
|
||||
### Added
|
||||
|
||||
* `module` argument to `pyclass` macro. [#499](https://github.com/PyO3/pyo3/pull/499)
|
||||
* `py_run!` macro [#512](https://github.com/PyO3/pyo3/pull/512)
|
||||
* Use existing fields and methods before calling custom __getattr__. [#505](https://github.com/PyO3/pyo3/pull/512)
|
||||
* `PyBytes` can now be indexed just like `Vec<u8>`
|
||||
* Implement `IntoPy<PyObject>` for `PyRef` and `PyRefMut`.
|
||||
|
||||
## Removed
|
||||
|
||||
* `IntoPyObject` was replaced with `IntoPy<PyObject>`
|
||||
* `#[pyclass(subclass)]` is hidden a `unsound-subclass` feature because it's causing segmentation faults.
|
||||
|
||||
### Fixed
|
||||
|
||||
|
@ -18,6 +26,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||
|
||||
### Changed
|
||||
|
||||
* Implementing the Using the `gc` parameter for `pyclass` (e.g. `#[pyclass(gc)]`) without implementing the `class::PyGCProtocol` trait is now a compile-time error. Failing to implement this trait could lead to segfaults. [#532](https://github.com/PyO3/pyo3/pull/532)
|
||||
* `PyByteArray::data` has been replaced with `PyDataArray::to_vec` because returning a `&[u8]` is unsound. (See [this comment](https://github.com/PyO3/pyo3/issues/373#issuecomment-512332696) for a great write-up for why that was unsound)
|
||||
* Replace `mashup` with `paste`.
|
||||
|
||||
## [0.7.0] - 2018-05-26
|
||||
|
||||
### Added
|
||||
|
|
29
Cargo.toml
29
Cargo.toml
|
@ -19,25 +19,25 @@ travis-ci = { repository = "PyO3/pyo3", branch = "master" }
|
|||
appveyor = { repository = "fafhrd91/pyo3" }
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.54"
|
||||
spin = "0.5.0"
|
||||
num-traits = "0.2.6"
|
||||
libc = "0.2.62"
|
||||
spin = "0.5.1"
|
||||
num-traits = "0.2.8"
|
||||
pyo3cls = { path = "pyo3cls", version = "=0.7.0" }
|
||||
mashup = "0.1.9"
|
||||
num-complex = { version = "0.2.1", optional = true }
|
||||
inventory = "0.1.3"
|
||||
indoc = "0.3.3"
|
||||
unindent = "0.1.3"
|
||||
num-complex = { version = "0.2.3", optional = true }
|
||||
inventory = "0.1.4"
|
||||
indoc = "0.3.4"
|
||||
unindent = "0.1.4"
|
||||
paste = "0.1.6"
|
||||
|
||||
[dev-dependencies]
|
||||
assert_approx_eq = "1.1.0"
|
||||
trybuild = "1.0"
|
||||
trybuild = "1.0.11"
|
||||
|
||||
[build-dependencies]
|
||||
regex = "1.1.6"
|
||||
version_check = "0.1.5"
|
||||
serde = { version = "1.0.91", features = ["derive"] }
|
||||
serde_json = "1.0.39"
|
||||
regex = "1.2.1"
|
||||
version_check = "0.9.1"
|
||||
serde = { version = "1.0.99", features = ["derive"] }
|
||||
serde_json = "1.0.40"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
@ -55,6 +55,9 @@ extension-module = []
|
|||
# are welcome.
|
||||
# abi3 = []
|
||||
|
||||
# Activate subclassing support
|
||||
unsound-subclass = ["pyo3cls/unsound-subclass"]
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"pyo3cls",
|
||||
|
|
20
Makefile
20
Makefile
|
@ -1,8 +1,12 @@
|
|||
.PHONY: test test_py3 publish
|
||||
.PHONY: test test_py3 publish clippy lint fmt
|
||||
|
||||
# Constants used in clippy target
|
||||
CLIPPY_LINTS_TO_DENY := warnings
|
||||
CLIPPY_LINTS_TO_ALLOW := clippy::new_ret_no_self
|
||||
|
||||
test:
|
||||
cargo test
|
||||
cargo clippy
|
||||
${MAKE} clippy
|
||||
tox
|
||||
for example in examples/*; do tox -e py -c $$example/tox.ini; done
|
||||
|
||||
|
@ -10,6 +14,18 @@ test_py3:
|
|||
tox -e py3
|
||||
for example in examples/*; do tox -e py3 -c $$example/tox.ini; done
|
||||
|
||||
fmt:
|
||||
cargo fmt --all -- --check
|
||||
|
||||
clippy:
|
||||
@touch src/lib.rs # Touching file to ensure that cargo clippy will re-check the project
|
||||
cargo clippy --all-features --all-targets -- \
|
||||
$(addprefix -D ,${CLIPPY_LINTS_TO_DENY}) \
|
||||
$(addprefix -A ,${CLIPPY_LINTS_TO_ALLOW})
|
||||
|
||||
lint: fmt clippy
|
||||
@true
|
||||
|
||||
publish: test
|
||||
cargo publish --manifest-path pyo3-derive-backend/Cargo.toml
|
||||
cargo publish --manifest-path pyo3cls/Cargo.toml
|
||||
|
|
|
@ -16,7 +16,7 @@ A comparison with rust-cpython can be found [in the guide](https://pyo3.rs/maste
|
|||
|
||||
## Usage
|
||||
|
||||
PyO3 supports Python 3.5 and up. The minimum required Rust version is 1.34.0-nightly 2019-02-06.
|
||||
PyO3 supports Python 3.5 and up. The minimum required Rust version is 1.37.0-nightly 2019-07-19.
|
||||
|
||||
PyPy is also supported (via cpyext) for Python 3.5 only, targeted PyPy version is 7.0.0.
|
||||
Please refer to the guide for installation instruction against PyPy.
|
||||
|
@ -129,6 +129,7 @@ fn main() -> PyResult<()> {
|
|||
* Contains an example of building wheels on Azure Pipelines
|
||||
* [fastuuid](https://github.com/thedrow/fastuuid/) _Python bindings to Rust's UUID library_
|
||||
* [python-ext-wasm](https://github.com/wasmerio/python-ext-wasm) _Python library to run WebAssembly binaries_
|
||||
* [dict-derive](https://github.com/gperinazzo/dict-derive) _Derive FromPyObject to automatically transform Python dicts into Rust structs_
|
||||
|
||||
## License
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ use test::Bencher;
|
|||
fn iter_dict(b: &mut Bencher) {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
const LEN: usize = 1_000_00;
|
||||
const LEN: usize = 100_000;
|
||||
let dict = (0..LEN as u64).map(|i| (i, i * 2)).into_py_dict(py);
|
||||
let mut sum = 0;
|
||||
b.iter(|| {
|
||||
|
|
57
build.rs
57
build.rs
|
@ -11,13 +11,13 @@ use std::path::Path;
|
|||
use std::process::exit;
|
||||
use std::process::Command;
|
||||
use std::process::Stdio;
|
||||
use version_check::{is_min_date, is_min_version, supports_features};
|
||||
use version_check::{Channel, Date, Version};
|
||||
|
||||
/// Specifies the minimum nightly version needed to compile pyo3.
|
||||
/// Keep this synced up with the travis ci config,
|
||||
/// But note that this is the rustc version which can be lower than the nightly version
|
||||
const MIN_DATE: &'static str = "2019-02-06";
|
||||
const MIN_VERSION: &'static str = "1.34.0-nightly";
|
||||
const MIN_DATE: &'static str = "2019-07-18";
|
||||
const MIN_VERSION: &'static str = "1.37.0-nightly";
|
||||
|
||||
/// Information returned from python interpreter
|
||||
#[derive(Deserialize, Debug)]
|
||||
|
@ -537,42 +537,25 @@ fn configure(interpreter_config: &InterpreterConfig) -> Result<(String), String>
|
|||
}
|
||||
|
||||
fn check_rustc_version() {
|
||||
let ok_channel = supports_features();
|
||||
let ok_version = is_min_version(MIN_VERSION);
|
||||
let ok_date = is_min_date(MIN_DATE);
|
||||
let channel = Channel::read().expect("Failed to determine rustc channel");
|
||||
if !channel.supports_features() {
|
||||
panic!("Error: pyo3 requires a nightly or dev version of Rust.");
|
||||
}
|
||||
|
||||
let print_version_err = |version: &str, date: &str| {
|
||||
eprintln!(
|
||||
"Installed version is: {} ({}). Minimum required: {} ({}).",
|
||||
version, date, MIN_VERSION, MIN_DATE
|
||||
);
|
||||
};
|
||||
let actual_version = Version::read().expect("Failed to determine the rustc version");
|
||||
if !actual_version.at_least(MIN_VERSION) {
|
||||
panic!(
|
||||
"pyo3 requires at least rustc {}, while the current version is {}",
|
||||
MIN_VERSION, actual_version
|
||||
)
|
||||
}
|
||||
|
||||
match (ok_channel, ok_version, ok_date) {
|
||||
(Some(ok_channel), Some((ok_version, version)), Some((ok_date, date))) => {
|
||||
if !ok_channel {
|
||||
eprintln!("Error: pyo3 requires a nightly or dev version of Rust.");
|
||||
print_version_err(&*version, &*date);
|
||||
panic!("Aborting compilation due to incompatible compiler.")
|
||||
}
|
||||
|
||||
if !ok_version || !ok_date {
|
||||
eprintln!("Error: pyo3 requires a more recent version of rustc.");
|
||||
eprintln!("Use `rustup update` or your preferred method to update Rust");
|
||||
print_version_err(&*version, &*date);
|
||||
panic!("Aborting compilation due to incompatible compiler.")
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
println!(
|
||||
"cargo:warning={}",
|
||||
"pyo3 was unable to check rustc compatibility."
|
||||
);
|
||||
println!(
|
||||
"cargo:warning={}",
|
||||
"Build may fail due to incompatible rustc version."
|
||||
);
|
||||
}
|
||||
let actual_date = Date::read().expect("Failed to determine the rustc date");
|
||||
if !actual_date.at_least(MIN_DATE) {
|
||||
panic!(
|
||||
"pyo3 requires at least rustc {}, while the current rustc date is {}",
|
||||
MIN_DATE, actual_date
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,27 +8,9 @@ if [[ $FEATURES == *"pypy"* ]]; then
|
|||
exit 0
|
||||
fi
|
||||
|
||||
### Run kcov ###################################################################
|
||||
### Run grcov ##################################################################
|
||||
|
||||
rm -f target/debug/pyo3*.d
|
||||
rm -f target/debug/test_*.d
|
||||
rm -f target/debug/test_doc-*
|
||||
zip -0 ccov.zip `find . \( -name "pyo3*.gc*" \) -print`;
|
||||
./grcov ccov.zip -s . -t lcov --llvm --branch --ignore-not-existing --ignore-dir "/*" -o lcov.info;
|
||||
bash <(curl -s https://codecov.io/bash) -f lcov.info;
|
||||
|
||||
# Note: On travis this is run with -P1 because it started failing with
|
||||
# `-P $(nproc)`. kcov can probably be run in parallel if used with different CI
|
||||
FILES=$(find . -path ./target/debug/pyo3\* -or -path ./target/debug/test_\*)
|
||||
echo $FILES | xargs -n1 -P1 sh -c '
|
||||
dir="target/cov/$(basename $@)"
|
||||
mkdir -p $dir
|
||||
echo "Collecting coverage data of $(basename $@)"
|
||||
kcov \
|
||||
--exclude-path=./tests \
|
||||
--exclude-region="#[cfg(test)]:#[cfg(testkcovstopmarker)]" \
|
||||
--exclude-pattern=/.cargo,/usr/lib \
|
||||
--verify $dir "$@" 2>&1 >/dev/null
|
||||
' _
|
||||
|
||||
### Upload coverage ############################################################
|
||||
|
||||
echo "Uploading code coverage"
|
||||
curl -SsL https://codecov.io/bash | bash
|
||||
|
|
|
@ -37,18 +37,3 @@ PYTHON_LIB=$($PYTHON_BINARY -c "import sysconfig; print(sysconfig.get_config_var
|
|||
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$PYTHON_LIB:$HOME/rust/lib"
|
||||
|
||||
echo ${LD_LIBRARY_PATH}
|
||||
|
||||
### Setup kcov #################################################################
|
||||
|
||||
if [ ! -f "$HOME/.cargo/bin/kcov" ]; then
|
||||
if [ ! -d "$HOME/kcov/.git" ]; then
|
||||
git clone --depth=1 https://github.com/SimonKagstrom/kcov \
|
||||
--branch=v36 "$HOME/kcov"
|
||||
fi
|
||||
|
||||
cd $HOME/kcov
|
||||
cmake .
|
||||
make
|
||||
install src/kcov $HOME/.cargo/bin/kcov
|
||||
cd $TRAVIS_BUILD_DIR
|
||||
fi
|
|
@ -11,8 +11,7 @@ else
|
|||
fi
|
||||
|
||||
if [ "$TRAVIS_JOB_NAME" = "Minimum nightly" ]; then
|
||||
cargo fmt --all -- --check
|
||||
cargo clippy --features "$FEATURES num-complex"
|
||||
make lint
|
||||
fi
|
||||
|
||||
for example_dir in examples/*; do
|
||||
|
|
2
codecov.yml
Normal file
2
codecov.yml
Normal file
|
@ -0,0 +1,2 @@
|
|||
comment: off
|
||||
|
|
@ -9,7 +9,7 @@ edition = "2018"
|
|||
|
||||
[dependencies.pyo3]
|
||||
path = "../../"
|
||||
features = ["extension-module"]
|
||||
features = ["extension-module", "unsound-subclass"]
|
||||
|
||||
[lib]
|
||||
name = "rustapi_module"
|
||||
|
|
|
@ -4,7 +4,7 @@ import platform
|
|||
|
||||
import pytest
|
||||
import rustapi_module.datetime as rdt
|
||||
from hypothesis import given
|
||||
from hypothesis import given, example
|
||||
from hypothesis import strategies as st
|
||||
from hypothesis.strategies import dates, datetimes
|
||||
|
||||
|
@ -34,13 +34,8 @@ UTC = _get_utc()
|
|||
MAX_SECONDS = int(pdt.timedelta.max.total_seconds())
|
||||
MIN_SECONDS = int(pdt.timedelta.min.total_seconds())
|
||||
|
||||
try:
|
||||
MAX_DAYS = pdt.timedelta.max // pdt.timedelta(days=1)
|
||||
MIN_DAYS = pdt.timedelta.min // pdt.timedelta(days=1)
|
||||
except Exception:
|
||||
# Python 2 compatibility
|
||||
MAX_DAYS = MAX_SECONDS // pdt.timedelta(days=1).total_seconds()
|
||||
MIN_DAYS = MIN_SECONDS // pdt.timedelta(days=1).total_seconds()
|
||||
MAX_DAYS = pdt.timedelta.max // pdt.timedelta(days=1)
|
||||
MIN_DAYS = pdt.timedelta.min // pdt.timedelta(days=1)
|
||||
|
||||
MAX_MICROSECONDS = int(pdt.timedelta.max.total_seconds() * 1e6)
|
||||
MIN_MICROSECONDS = int(pdt.timedelta.min.total_seconds() * 1e6)
|
||||
|
@ -48,11 +43,10 @@ MIN_MICROSECONDS = int(pdt.timedelta.min.total_seconds() * 1e6)
|
|||
IS_X86 = platform.architecture()[0] == '32bit'
|
||||
IS_WINDOWS = sys.platform == 'win32'
|
||||
if IS_WINDOWS:
|
||||
MIN_DATETIME_FROM_TIMESTAMP = pdt.datetime.fromtimestamp(86400)
|
||||
if IS_X86:
|
||||
MIN_DATETIME_FROM_TIMESTAMP = pdt.datetime.fromtimestamp(86400)
|
||||
MAX_DATETIME_FROM_TIMESTAMP = pdt.datetime.fromtimestamp(32536789199)
|
||||
else:
|
||||
MIN_DATETIME_FROM_TIMESTAMP = pdt.datetime.fromtimestamp(0)
|
||||
MAX_DATETIME_FROM_TIMESTAMP = pdt.datetime.fromtimestamp(32536799999)
|
||||
else:
|
||||
if IS_X86:
|
||||
|
@ -65,14 +59,6 @@ else:
|
|||
PYPY = platform.python_implementation() == "PyPy"
|
||||
HAS_FOLD = getattr(pdt.datetime, "fold", False)
|
||||
|
||||
# Helper functions
|
||||
get_timestamp = getattr(pdt.datetime, "timestamp", None)
|
||||
if get_timestamp is None:
|
||||
|
||||
def get_timestamp(dt):
|
||||
# Python 2 compatibility
|
||||
return (dt - pdt.datetime(1970, 1, 1)).total_seconds()
|
||||
|
||||
|
||||
xfail_date_bounds = pytest.mark.xfail(
|
||||
sys.version_info < (3, 6),
|
||||
|
@ -103,9 +89,9 @@ def test_invalid_date_fails():
|
|||
MAX_DATETIME_FROM_TIMESTAMP.date()))
|
||||
def test_date_from_timestamp(d):
|
||||
if PYPY and d < pdt.date(1900, 1, 1):
|
||||
pytest.xfail("get_timestamp will raise on PyPy with dates before 1900")
|
||||
pytest.xfail("pdt.datetime.timestamp will raise on PyPy with dates before 1900")
|
||||
|
||||
ts = get_timestamp(pdt.datetime.combine(d, pdt.time(0)))
|
||||
ts = pdt.datetime.timestamp(pdt.datetime.combine(d, pdt.time(0)))
|
||||
assert rdt.date_from_timestamp(int(ts)) == pdt.date.fromtimestamp(ts)
|
||||
|
||||
|
||||
|
@ -241,11 +227,12 @@ def test_datetime_typeerror():
|
|||
|
||||
@given(dt=st.datetimes(MIN_DATETIME_FROM_TIMESTAMP,
|
||||
MAX_DATETIME_FROM_TIMESTAMP))
|
||||
@example(dt=pdt.datetime(1970, 1, 2, 0, 0))
|
||||
def test_datetime_from_timestamp(dt):
|
||||
if PYPY and dt < pdt.datetime(1900, 1, 1):
|
||||
pytest.xfail("get_timestamp will raise on PyPy with dates before 1900")
|
||||
pytest.xfail("pdt.datetime.timestamp will raise on PyPy with dates before 1900")
|
||||
|
||||
ts = get_timestamp(dt)
|
||||
ts = pdt.datetime.timestamp(dt)
|
||||
assert rdt.datetime_from_timestamp(ts) == pdt.datetime.fromtimestamp(ts)
|
||||
|
||||
|
||||
|
|
|
@ -102,10 +102,10 @@ so that they can benefit from a freelist. `XXX` is a number of items for the fre
|
|||
If a custom class contains references to other Python objects that can be collected, the `PyGCProtocol` trait has to be implemented.
|
||||
* `weakref` - Adds support for Python weak references.
|
||||
* `extends=BaseType` - Use a custom base class. The base `BaseType` must implement `PyTypeInfo`.
|
||||
* `subclass` - Allows Python classes to inherit from this class.
|
||||
* `dict` - Adds `__dict__` support, so that the instances of this type have a dictionary containing arbitrary instance variables.
|
||||
* `module="XXX"` - Set the name of the module the class will be shown as defined in. If not given, the class
|
||||
will be a virtual member of the `builtins` module.
|
||||
* `subclass` - Allows Python classes to inherit from this class. This feature is hidden behind a `unsound-subclass` feature because it is currently causing segmentation faults
|
||||
|
||||
## Constructor
|
||||
|
||||
|
@ -323,7 +323,7 @@ impl MyClass {
|
|||
```
|
||||
|
||||
Calls to these methods are protected by the GIL, so both `&self` and `&mut self` can be used.
|
||||
The return type must be `PyResult<T>` or `T` for some `T` that implements `IntoPyObject`;
|
||||
The return type must be `PyResult<T>` or `T` for some `T` that implements `IntoPy<PyObject>`;
|
||||
the latter is allowed if the method cannot raise Python exceptions.
|
||||
|
||||
A `Python` parameter can be specified as part of method signature, in this case the `py` argument
|
||||
|
@ -376,13 +376,13 @@ Declares a class method callable from Python.
|
|||
This may be the type object of a derived class.
|
||||
* The first parameter implicitly has type `&PyType`.
|
||||
* For details on `parameter-list`, see the documentation of `Method arguments` section.
|
||||
* The return type must be `PyResult<T>` or `T` for some `T` that implements `IntoPyObject`.
|
||||
* The return type must be `PyResult<T>` or `T` for some `T` that implements `IntoPy<PyObject>`.
|
||||
|
||||
## Static methods
|
||||
|
||||
To create a static method for a custom class, the method needs to be annotated with the
|
||||
`#[staticmethod]` attribute. The return type must be `T` or `PyResult<T>` for some `T` that implements
|
||||
`IntoPyObject`.
|
||||
`IntoPy<PyObject>`.
|
||||
|
||||
```rust
|
||||
# use pyo3::prelude::*;
|
||||
|
@ -460,8 +460,8 @@ use pyo3::types::{PyDict, PyTuple};
|
|||
#
|
||||
#[pymethods]
|
||||
impl MyClass {
|
||||
#[args(arg1=true, args="*", arg2=10, kwargs="**")]
|
||||
fn method(&self, arg1: bool, args: &PyTuple, arg2: i32, kwargs: Option<&PyDict>) -> PyResult<i32> {
|
||||
#[args(arg1=true, args="*", arg2=10, args3="\"Hello\"", kwargs="**")]
|
||||
fn method(&self, arg1: bool, args: &PyTuple, arg2: i32, arg3: &str, kwargs: Option<&PyDict>) -> PyResult<i32> {
|
||||
Ok(1)
|
||||
}
|
||||
}
|
||||
|
@ -483,7 +483,7 @@ The [`PyObjectProtocol`](https://docs.rs/pyo3/0.7.0/pyo3/class/basic/trait.PyObj
|
|||
|
||||
To customize object attribute access, define the following methods:
|
||||
|
||||
* `fn __getattr__(&self, name: FromPyObject) -> PyResult<impl IntoPyObject>`
|
||||
* `fn __getattr__(&self, name: FromPyObject) -> PyResult<impl IntoPy<PyObject>>`
|
||||
* `fn __setattr__(&mut self, name: FromPyObject, value: FromPyObject) -> PyResult<()>`
|
||||
* `fn __delattr__(&mut self, name: FromPyObject) -> PyResult<()>`
|
||||
|
||||
|
@ -573,17 +573,24 @@ impl PyGCProtocol for ClassWithGCSupport {
|
|||
|
||||
Special protocol trait implementations have to be annotated with the `#[pyproto]` attribute.
|
||||
|
||||
It is also possible to enable GC for custom class using the `gc` parameter of the `pyclass` attribute.
|
||||
It is also possible to enable GC for custom classes using the `gc` parameter of the `pyclass` attribute.
|
||||
i.e. `#[pyclass(gc)]`. In that case instances of custom class participate in Python garbage
|
||||
collection, and it is possible to track them with `gc` module methods.
|
||||
collection, and it is possible to track them with `gc` module methods. When using the `gc` parameter,
|
||||
it is *required* to implement the `PyGCProtocol` trait, failure to do so will result in an error
|
||||
at compile time:
|
||||
|
||||
```compile_fail
|
||||
#[pyclass(gc)]
|
||||
struct GCTracked {} // Fails because it does not implement PyGCProtocol
|
||||
```
|
||||
|
||||
### Iterator Types
|
||||
|
||||
Iterators can be defined using the
|
||||
[`PyIterProtocol`](https://docs.rs/pyo3/0.7.0/pyo3/class/iter/trait.PyIterProtocol.html) trait.
|
||||
It includes two methods `__iter__` and `__next__`:
|
||||
* `fn __iter__(slf: PyRefMut<Self>) -> PyResult<impl IntoPyObject>`
|
||||
* `fn __next__(slf: PyRefMut<Self>) -> PyResult<Option<impl IntoPyObject>>`
|
||||
* `fn __iter__(slf: PyRefMut<Self>) -> PyResult<impl IntoPy<PyObject>>`
|
||||
* `fn __next__(slf: PyRefMut<Self>) -> PyResult<Option<impl IntoPy<PyObject>>>`
|
||||
|
||||
Returning `Ok(None)` from `__next__` indicates that that there are no further items.
|
||||
|
||||
|
|
|
@ -6,10 +6,10 @@ PyO3 provides some handy traits to convert between Python types and Rust types.
|
|||
|
||||
The easiest way to convert a Python object to a Rust value is using `.extract()?`.
|
||||
|
||||
## `ToPyObject` and `IntoPyObject` trait
|
||||
## `ToPyObject` trait
|
||||
|
||||
[`ToPyObject`] trait is a conversion trait that allows various objects to be
|
||||
converted into [`PyObject`][PyObject]. [`IntoPyObject`][IntoPyObject] serves the
|
||||
converted into [`PyObject`][PyObject]. `IntoPy<PyObject>` serves the
|
||||
same purpose, except that it consumes `self`.
|
||||
|
||||
## `FromPyObject` and `RefFromPyObject` trait
|
||||
|
@ -104,10 +104,9 @@ fn main() {
|
|||
|
||||
Many conversions in PyO3 can't use `std::convert::Into` because they need a GIL token. That's why the `IntoPy<T>` trait offers an `into_py` method that works just like `into`, except for taking a `Python<'_>` argument.
|
||||
|
||||
Eventually, traits such as `IntoPyObject` will be replaced by this trait and a `FromPy` trait will be added that will implement `IntoPy`, just like with `From` and `Into`.
|
||||
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`.
|
||||
|
||||
[`ToPyObject`]: https://docs.rs/pyo3/0.7.0/pyo3/trait.ToPyObject.html
|
||||
[IntoPyObject]: https://docs.rs/pyo3/0.7.0/pyo3/trait.IntoPyObject.html
|
||||
[PyObject]: https://docs.rs/pyo3/0.7.0/pyo3/struct.PyObject.html
|
||||
[PyTuple]: https://docs.rs/pyo3/0.7.0/pyo3/types/struct.PyTuple.html
|
||||
[ObjectProtocol]: https://docs.rs/pyo3/0.7.0/pyo3/trait.ObjectProtocol.html
|
||||
|
|
|
@ -128,23 +128,39 @@ more complex arguments are required, the
|
|||
trait can be implemented. In that case, actual exception argument creation is delayed
|
||||
until a `Python` object is available.
|
||||
|
||||
```rust,ignore
|
||||
use std::net::TcpListener;
|
||||
use pyo3::{PyErr, PyResult, exc};
|
||||
```rust
|
||||
# use pyo3::{exceptions, PyErr, PyResult};
|
||||
# use std::error::Error;
|
||||
# use std::fmt;
|
||||
#
|
||||
# #[derive(Debug)]
|
||||
# struct CustomIOError;
|
||||
#
|
||||
# impl Error for CustomIOError {}
|
||||
#
|
||||
# impl fmt::Display for CustomIOError {
|
||||
# fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
# write!(f, "Oh no!")
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# fn bind(_addr: &str) -> Result<(), CustomIOError> {
|
||||
# Err(CustomIOError)
|
||||
# }
|
||||
|
||||
impl std::convert::From<std::io::Error> for PyErr {
|
||||
fn from(err: std::io::Error) -> PyErr {
|
||||
exceptions::OSError.into()
|
||||
impl std::convert::From<CustomIOError> for PyErr {
|
||||
fn from(err: CustomIOError) -> PyErr {
|
||||
exceptions::OSError::py_err(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn connect(s: String) -> PyResult<bool> {
|
||||
TcpListener::bind("127.0.0.1:80")?;
|
||||
bind("127.0.0.1:80")?;
|
||||
Ok(true)
|
||||
}
|
||||
```
|
||||
|
||||
The code snippet above will raise an `OSError` in Python if `TcpListener::bind()` returns an error.
|
||||
The code snippet above will raise an `OSError` in Python if `bind()` returns a `CustomIOError`.
|
||||
|
||||
The `std::convert::From<T>` trait is implemented for most of the Rust standard library's error
|
||||
types so the `try!` macro or the `?` operator can be used.
|
||||
|
|
|
@ -81,9 +81,7 @@ In order to make the function signature available to Python to be retrieved via
|
|||
formatted like in the example below. Please note that the newline after the
|
||||
`--` is mandatory. The `/` signifies the end of positional-only arguments. This
|
||||
is not a feature of this library in particular, but the general format used by
|
||||
CPython for annotating signatures of built-in functions. Function signatures for
|
||||
built-ins are new in Python 3 — in Python 2, they are simply considered to be a
|
||||
part of the docstring.
|
||||
CPython for annotating signatures of built-in functions.
|
||||
|
||||
```rust
|
||||
use pyo3::prelude::*;
|
||||
|
|
|
@ -10,7 +10,7 @@ A comparison with rust-cpython can be found [in the guide](https://pyo3.rs/maste
|
|||
|
||||
## Usage
|
||||
|
||||
PyO3 supports Python 3.5 and up. The minimum required Rust version is 1.34.0-nightly 2019-02-06.
|
||||
PyO3 supports Python 3.5 and up. The minimum required Rust version is 1.37.0-nightly 2019-07-19.
|
||||
|
||||
PyPy is also supported (via cpyext) for Python 3.5 only, targeted PyPy version is 7.0.0.
|
||||
Please refer to the guide for installation instruction against PyPy.
|
||||
|
|
|
@ -11,6 +11,9 @@ license = "Apache-2.0"
|
|||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
quote = "0.6.12"
|
||||
proc-macro2 = "0.4.30"
|
||||
syn = { version = "0.15.34", features = ["full", "extra-traits"] }
|
||||
quote = "1"
|
||||
proc-macro2 = "1"
|
||||
syn = { version = "1", features = ["full", "extra-traits"] }
|
||||
|
||||
[features]
|
||||
unsound-subclass = []
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
use crate::utils::print_err;
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::quote;
|
||||
use syn::Token;
|
||||
|
||||
// TODO:
|
||||
// Add lifetime support for args with Rptr
|
||||
|
@ -70,7 +71,7 @@ impl PartialEq<str> for MethodProto {
|
|||
|
||||
pub fn impl_method_proto(
|
||||
cls: &syn::Type,
|
||||
sig: &mut syn::MethodSig,
|
||||
sig: &mut syn::Signature,
|
||||
meth: &MethodProto,
|
||||
) -> TokenStream {
|
||||
if let MethodProto::Free { proto, .. } = meth {
|
||||
|
@ -80,7 +81,7 @@ pub fn impl_method_proto(
|
|||
};
|
||||
}
|
||||
|
||||
let ty = &*if let syn::ReturnType::Type(_, ref ty) = sig.decl.output {
|
||||
let ty = &*if let syn::ReturnType::Type(_, ref ty) = sig.output {
|
||||
ty.clone()
|
||||
} else {
|
||||
panic!("fn return type is not supported")
|
||||
|
@ -95,7 +96,7 @@ pub fn impl_method_proto(
|
|||
let tmp: syn::ItemFn = syn::parse_quote! {
|
||||
fn test(&self) -> <#cls as #p<'p>>::Result {}
|
||||
};
|
||||
sig.decl.output = tmp.decl.output;
|
||||
sig.output = tmp.sig.output;
|
||||
modify_self_ty(sig);
|
||||
|
||||
if pyres {
|
||||
|
@ -119,7 +120,7 @@ pub fn impl_method_proto(
|
|||
pyres,
|
||||
proto,
|
||||
} => {
|
||||
if sig.decl.inputs.len() <= 1 {
|
||||
if sig.inputs.len() <= 1 {
|
||||
println!("Not enough arguments for {}", name);
|
||||
return TokenStream::new();
|
||||
}
|
||||
|
@ -164,7 +165,7 @@ pub fn impl_method_proto(
|
|||
pyres,
|
||||
proto,
|
||||
} => {
|
||||
if sig.decl.inputs.len() <= 1 {
|
||||
if sig.inputs.len() <= 1 {
|
||||
print_err(format!("Not enough arguments {}", name), quote!(sig));
|
||||
return TokenStream::new();
|
||||
}
|
||||
|
@ -213,7 +214,7 @@ pub fn impl_method_proto(
|
|||
pyres,
|
||||
proto,
|
||||
} => {
|
||||
if sig.decl.inputs.len() <= 2 {
|
||||
if sig.inputs.len() <= 2 {
|
||||
print_err(format!("Not enough arguments {}", name), quote!(sig));
|
||||
return TokenStream::new();
|
||||
}
|
||||
|
@ -266,7 +267,7 @@ pub fn impl_method_proto(
|
|||
pyres,
|
||||
proto,
|
||||
} => {
|
||||
if sig.decl.inputs.len() <= 2 {
|
||||
if sig.inputs.len() <= 2 {
|
||||
print_err(format!("Not enough arguments {}", name), quote!(sig));
|
||||
return TokenStream::new();
|
||||
}
|
||||
|
@ -322,7 +323,7 @@ pub fn impl_method_proto(
|
|||
arg3,
|
||||
proto,
|
||||
} => {
|
||||
if sig.decl.inputs.len() <= 3 {
|
||||
if sig.inputs.len() <= 3 {
|
||||
print_err(format!("Not enough arguments {}", name), quote!(sig));
|
||||
return TokenStream::new();
|
||||
}
|
||||
|
@ -367,26 +368,26 @@ pub fn impl_method_proto(
|
|||
}
|
||||
|
||||
// TODO: better arg ty detection
|
||||
fn get_arg_ty(sig: &syn::MethodSig, idx: usize) -> syn::Type {
|
||||
let mut ty = match sig.decl.inputs[idx] {
|
||||
syn::FnArg::Captured(ref cap) => {
|
||||
match cap.ty {
|
||||
fn get_arg_ty(sig: &syn::Signature, idx: usize) -> syn::Type {
|
||||
let mut ty = match sig.inputs[idx] {
|
||||
syn::FnArg::Typed(ref cap) => {
|
||||
match *cap.ty {
|
||||
syn::Type::Path(ref ty) => {
|
||||
// use only last path segment for Option<>
|
||||
let seg = *ty.path.segments.last().unwrap().value();
|
||||
let seg = ty.path.segments.last().unwrap().clone();
|
||||
if seg.ident == "Option" {
|
||||
if let syn::PathArguments::AngleBracketed(ref data) = seg.arguments {
|
||||
if let Some(pair) = data.args.last() {
|
||||
match pair.value() {
|
||||
match pair {
|
||||
syn::GenericArgument::Type(ref ty) => return ty.clone(),
|
||||
_ => panic!("Option only accepted for concrete types"),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
cap.ty.clone()
|
||||
*cap.ty.clone()
|
||||
}
|
||||
_ => cap.ty.clone(),
|
||||
_ => *cap.ty.clone(),
|
||||
}
|
||||
}
|
||||
_ => panic!("fn arg type is not supported"),
|
||||
|
@ -408,9 +409,9 @@ fn get_res_success(ty: &syn::Type) -> (TokenStream, syn::GenericArgument) {
|
|||
match ty {
|
||||
syn::Type::Path(ref typath) => {
|
||||
if let Some(segment) = typath.path.segments.last() {
|
||||
match segment.value().ident.to_string().as_str() {
|
||||
match segment.ident.to_string().as_str() {
|
||||
// check for PyResult<T>
|
||||
"PyResult" => match segment.value().arguments {
|
||||
"PyResult" => match segment.arguments {
|
||||
syn::PathArguments::AngleBracketed(ref data) => {
|
||||
result = true;
|
||||
succ = data.args[0].clone();
|
||||
|
@ -420,10 +421,10 @@ fn get_res_success(ty: &syn::Type) -> (TokenStream, syn::GenericArgument) {
|
|||
data.args[0]
|
||||
{
|
||||
if let Some(segment) = typath.path.segments.last() {
|
||||
if "Option" == segment.value().ident.to_string().as_str() {
|
||||
if "Option" == segment.ident.to_string().as_str() {
|
||||
// get T from Option<T>
|
||||
if let syn::PathArguments::AngleBracketed(ref data) =
|
||||
segment.value().arguments
|
||||
segment.arguments
|
||||
{
|
||||
result = false;
|
||||
succ = data.args[0].clone();
|
||||
|
@ -436,7 +437,7 @@ fn get_res_success(ty: &syn::Type) -> (TokenStream, syn::GenericArgument) {
|
|||
},
|
||||
_ => panic!(
|
||||
"fn result type has to be PyResult or (), got {:?}",
|
||||
segment.value().ident
|
||||
segment.ident
|
||||
),
|
||||
}
|
||||
} else {
|
||||
|
@ -456,50 +457,55 @@ fn get_res_success(ty: &syn::Type) -> (TokenStream, syn::GenericArgument) {
|
|||
(res, succ)
|
||||
}
|
||||
|
||||
fn extract_decl(spec: syn::Item) -> syn::FnDecl {
|
||||
fn extract_decl(spec: syn::Item) -> syn::Signature {
|
||||
match spec {
|
||||
syn::Item::Fn(f) => *f.decl,
|
||||
syn::Item::Fn(f) => f.sig,
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
// modify method signature
|
||||
fn modify_arg_ty(sig: &mut syn::MethodSig, idx: usize, decl1: &syn::FnDecl, decl2: &syn::FnDecl) {
|
||||
let arg = sig.decl.inputs[idx].clone();
|
||||
fn modify_arg_ty(
|
||||
sig: &mut syn::Signature,
|
||||
idx: usize,
|
||||
decl1: &syn::Signature,
|
||||
decl2: &syn::Signature,
|
||||
) {
|
||||
let arg = sig.inputs[idx].clone();
|
||||
match arg {
|
||||
syn::FnArg::Captured(ref cap) => match cap.ty {
|
||||
syn::FnArg::Typed(ref cap) => match *cap.ty {
|
||||
syn::Type::Path(ref typath) => {
|
||||
let seg = *typath.path.segments.last().unwrap().value();
|
||||
let seg = typath.path.segments.last().unwrap().clone();
|
||||
if seg.ident == "Option" {
|
||||
sig.decl.inputs[idx] = fix_name(&cap.pat, &decl2.inputs[idx]);
|
||||
sig.inputs[idx] = fix_name(&cap.pat, &decl2.inputs[idx]);
|
||||
} else {
|
||||
sig.decl.inputs[idx] = fix_name(&cap.pat, &decl1.inputs[idx]);
|
||||
sig.inputs[idx] = fix_name(&cap.pat, &decl1.inputs[idx]);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
sig.decl.inputs[idx] = fix_name(&cap.pat, &decl1.inputs[idx]);
|
||||
sig.inputs[idx] = fix_name(&cap.pat, &decl1.inputs[idx]);
|
||||
}
|
||||
},
|
||||
_ => panic!("not supported"),
|
||||
}
|
||||
|
||||
sig.decl.output = decl1.output.clone();
|
||||
sig.output = decl1.output.clone();
|
||||
}
|
||||
|
||||
fn modify_self_ty(sig: &mut syn::MethodSig) {
|
||||
match sig.decl.inputs[0] {
|
||||
syn::FnArg::SelfRef(ref mut slf) => {
|
||||
slf.lifetime = Some(syn::parse_quote! {'p});
|
||||
fn modify_self_ty(sig: &mut syn::Signature) {
|
||||
match sig.inputs[0] {
|
||||
syn::FnArg::Receiver(ref mut slf) => {
|
||||
slf.reference = Some((Token![&](Span::call_site()), syn::parse_quote! {'p}));
|
||||
}
|
||||
syn::FnArg::Captured(_) => {}
|
||||
_ => panic!("not supported"),
|
||||
syn::FnArg::Typed(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn fix_name(pat: &syn::Pat, arg: &syn::FnArg) -> syn::FnArg {
|
||||
if let syn::FnArg::Captured(ref cap) = arg {
|
||||
syn::FnArg::Captured(syn::ArgCaptured {
|
||||
pat: pat.clone(),
|
||||
if let syn::FnArg::Typed(ref cap) = arg {
|
||||
syn::FnArg::Typed(syn::PatType {
|
||||
attrs: cap.attrs.clone(),
|
||||
pat: Box::new(pat.clone()),
|
||||
colon_token: cap.colon_token,
|
||||
ty: cap.ty.clone(),
|
||||
})
|
||||
|
|
|
@ -49,22 +49,19 @@ impl<'a> FnSpec<'a> {
|
|||
/// Parser function signature and function attributes
|
||||
pub fn parse(
|
||||
name: &'a syn::Ident,
|
||||
sig: &'a syn::MethodSig,
|
||||
sig: &'a syn::Signature,
|
||||
meth_attrs: &'a mut Vec<syn::Attribute>,
|
||||
) -> syn::Result<FnSpec<'a>> {
|
||||
let (mut fn_type, fn_attrs) = parse_attributes(meth_attrs)?;
|
||||
|
||||
let mut has_self = false;
|
||||
let mut arguments = Vec::new();
|
||||
for input in sig.decl.inputs.iter() {
|
||||
for input in sig.inputs.iter() {
|
||||
match input {
|
||||
syn::FnArg::SelfRef(_) => {
|
||||
syn::FnArg::Receiver(_) => {
|
||||
has_self = true;
|
||||
}
|
||||
syn::FnArg::SelfValue(_) => {
|
||||
has_self = true;
|
||||
}
|
||||
syn::FnArg::Captured(syn::ArgCaptured {
|
||||
syn::FnArg::Typed(syn::PatType {
|
||||
ref pat, ref ty, ..
|
||||
}) => {
|
||||
// skip first argument (cls)
|
||||
|
@ -73,7 +70,7 @@ impl<'a> FnSpec<'a> {
|
|||
continue;
|
||||
}
|
||||
|
||||
let (ident, by_ref, mutability) = match pat {
|
||||
let (ident, by_ref, mutability) = match **pat {
|
||||
syn::Pat::Ident(syn::PatIdent {
|
||||
ref ident,
|
||||
ref by_ref,
|
||||
|
@ -85,16 +82,7 @@ impl<'a> FnSpec<'a> {
|
|||
}
|
||||
};
|
||||
|
||||
let py = match ty {
|
||||
syn::Type::Path(syn::TypePath { ref path, .. }) => {
|
||||
if let Some(segment) = path.segments.last() {
|
||||
segment.value().ident == "Python"
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
let py = crate::utils::if_type_is_python(ty);
|
||||
|
||||
let opt = check_arg_ty_and_optional(name, ty);
|
||||
arguments.push(FnArg {
|
||||
|
@ -108,16 +96,10 @@ impl<'a> FnSpec<'a> {
|
|||
reference: is_ref(name, ty),
|
||||
});
|
||||
}
|
||||
syn::FnArg::Ignored(_) => {
|
||||
return Err(syn::Error::new_spanned(name, "ignored argument"));
|
||||
}
|
||||
syn::FnArg::Inferred(_) => {
|
||||
return Err(syn::Error::new_spanned(name, "inferred argument"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let ty = get_return_info(&sig.decl.output);
|
||||
let ty = get_return_info(&sig.output);
|
||||
|
||||
if fn_type == FnType::Fn && !has_self {
|
||||
if arguments.is_empty() {
|
||||
|
@ -140,8 +122,8 @@ impl<'a> FnSpec<'a> {
|
|||
|
||||
pub fn is_args(&self, name: &syn::Ident) -> bool {
|
||||
for s in self.attrs.iter() {
|
||||
if let Argument::VarArgs(ref ident) = s {
|
||||
return name == ident;
|
||||
if let Argument::VarArgs(ref path) = s {
|
||||
return path.is_ident(name);
|
||||
}
|
||||
}
|
||||
false
|
||||
|
@ -160,8 +142,8 @@ impl<'a> FnSpec<'a> {
|
|||
|
||||
pub fn is_kwargs(&self, name: &syn::Ident) -> bool {
|
||||
for s in self.attrs.iter() {
|
||||
if let Argument::KeywordArgs(ref ident) = s {
|
||||
return name == ident;
|
||||
if let Argument::KeywordArgs(ref path) = s {
|
||||
return path.is_ident(name);
|
||||
}
|
||||
}
|
||||
false
|
||||
|
@ -179,16 +161,16 @@ impl<'a> FnSpec<'a> {
|
|||
pub fn default_value(&self, name: &syn::Ident) -> Option<TokenStream> {
|
||||
for s in self.attrs.iter() {
|
||||
match *s {
|
||||
Argument::Arg(ref ident, ref opt) => {
|
||||
if ident == name {
|
||||
Argument::Arg(ref path, ref opt) => {
|
||||
if path.is_ident(name) {
|
||||
if let Some(ref val) = opt {
|
||||
let i: syn::Expr = syn::parse_str(&val).unwrap();
|
||||
return Some(i.into_token_stream());
|
||||
}
|
||||
}
|
||||
}
|
||||
Argument::Kwarg(ref ident, ref opt) => {
|
||||
if ident == name {
|
||||
Argument::Kwarg(ref path, ref opt) => {
|
||||
if path.is_ident(name) {
|
||||
let i: syn::Expr = syn::parse_str(&opt).unwrap();
|
||||
return Some(quote!(#i));
|
||||
}
|
||||
|
@ -201,8 +183,8 @@ impl<'a> FnSpec<'a> {
|
|||
|
||||
pub fn is_kw_only(&self, name: &syn::Ident) -> bool {
|
||||
for s in self.attrs.iter() {
|
||||
if let Argument::Kwarg(ref ident, _) = s {
|
||||
if ident == name {
|
||||
if let Argument::Kwarg(ref path, _) = s {
|
||||
if path.is_ident(name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -216,8 +198,8 @@ pub fn is_ref(name: &syn::Ident, ty: &syn::Type) -> bool {
|
|||
syn::Type::Reference(_) => return true,
|
||||
syn::Type::Path(syn::TypePath { ref path, .. }) => {
|
||||
if let Some(segment) = path.segments.last() {
|
||||
if "Option" == segment.value().ident.to_string().as_str() {
|
||||
match segment.value().arguments {
|
||||
if "Option" == segment.ident.to_string().as_str() {
|
||||
match segment.arguments {
|
||||
syn::PathArguments::AngleBracketed(ref params) => {
|
||||
if params.args.len() != 1 {
|
||||
panic!("argument type is not supported by python method: {:?} ({:?}) {:?}",
|
||||
|
@ -257,8 +239,8 @@ pub fn check_arg_ty_and_optional<'a>(
|
|||
//}
|
||||
|
||||
if let Some(segment) = path.segments.last() {
|
||||
match segment.value().ident.to_string().as_str() {
|
||||
"Option" => match segment.value().arguments {
|
||||
match segment.ident.to_string().as_str() {
|
||||
"Option" => match segment.arguments {
|
||||
syn::PathArguments::AngleBracketed(ref params) => {
|
||||
if params.args.len() != 1 {
|
||||
panic!("argument type is not supported by python method: {:?} ({:?}) {:?}",
|
||||
|
@ -303,40 +285,46 @@ fn parse_attributes(attrs: &mut Vec<syn::Attribute>) -> syn::Result<(FnType, Vec
|
|||
let mut res: Option<FnType> = None;
|
||||
|
||||
for attr in attrs.iter() {
|
||||
match attr.interpret_meta().unwrap() {
|
||||
syn::Meta::Word(ref name) => match name.to_string().as_ref() {
|
||||
"new" | "__new__" => res = Some(FnType::FnNew),
|
||||
"init" | "__init__" => res = Some(FnType::FnInit),
|
||||
"call" | "__call__" => res = Some(FnType::FnCall),
|
||||
"classmethod" => res = Some(FnType::FnClass),
|
||||
"staticmethod" => res = Some(FnType::FnStatic),
|
||||
"setter" | "getter" => {
|
||||
match attr.parse_meta().unwrap() {
|
||||
syn::Meta::Path(ref name) => {
|
||||
if name.is_ident("new") || name.is_ident("__new__") {
|
||||
res = Some(FnType::FnNew)
|
||||
} else if name.is_ident("init") || name.is_ident("__init__") {
|
||||
res = Some(FnType::FnInit)
|
||||
} else if name.is_ident("call") || name.is_ident("__call__") {
|
||||
res = Some(FnType::FnCall)
|
||||
} else if name.is_ident("classmethod") {
|
||||
res = Some(FnType::FnClass)
|
||||
} else if name.is_ident("staticmethod") {
|
||||
res = Some(FnType::FnStatic)
|
||||
} else if name.is_ident("setter") || name.is_ident("getter") {
|
||||
if let syn::AttrStyle::Inner(_) = attr.style {
|
||||
panic!(
|
||||
"Inner style attribute is not
|
||||
supported for setter and getter"
|
||||
);
|
||||
panic!("Inner style attribute is not supported for setter and getter");
|
||||
}
|
||||
if res != None {
|
||||
panic!("setter/getter attribute can not be used mutiple times");
|
||||
}
|
||||
if name == "setter" {
|
||||
if name.is_ident("setter") {
|
||||
res = Some(FnType::Setter(None))
|
||||
} else {
|
||||
res = Some(FnType::Getter(None))
|
||||
}
|
||||
} else {
|
||||
new_attrs.push(attr.clone())
|
||||
}
|
||||
_ => new_attrs.push(attr.clone()),
|
||||
},
|
||||
}
|
||||
syn::Meta::List(syn::MetaList {
|
||||
ref ident,
|
||||
ref path,
|
||||
ref nested,
|
||||
..
|
||||
}) => match ident.to_string().as_str() {
|
||||
"new" => res = Some(FnType::FnNew),
|
||||
"init" => res = Some(FnType::FnInit),
|
||||
"call" => res = Some(FnType::FnCall),
|
||||
"setter" | "getter" => {
|
||||
}) => {
|
||||
if path.is_ident("new") {
|
||||
res = Some(FnType::FnNew)
|
||||
} else if path.is_ident("init") {
|
||||
res = Some(FnType::FnInit)
|
||||
} else if path.is_ident("call") {
|
||||
res = Some(FnType::FnCall)
|
||||
} else if path.is_ident("setter") || path.is_ident("getter") {
|
||||
if let syn::AttrStyle::Inner(_) = attr.style {
|
||||
panic!(
|
||||
"Inner style attribute is not
|
||||
|
@ -349,17 +337,17 @@ fn parse_attributes(attrs: &mut Vec<syn::Attribute>) -> syn::Result<(FnType, Vec
|
|||
if nested.len() != 1 {
|
||||
panic!("setter/getter requires one value");
|
||||
}
|
||||
match nested.first().unwrap().value() {
|
||||
syn::NestedMeta::Meta(syn::Meta::Word(ref w)) => {
|
||||
if ident == "setter" {
|
||||
res = Some(FnType::Setter(Some(w.to_string())))
|
||||
match nested.first().unwrap() {
|
||||
syn::NestedMeta::Meta(syn::Meta::Path(ref w)) => {
|
||||
if path.is_ident("setter") {
|
||||
res = Some(FnType::Setter(Some(w.segments[0].ident.to_string())))
|
||||
} else {
|
||||
res = Some(FnType::Getter(Some(w.to_string())))
|
||||
res = Some(FnType::Getter(Some(w.segments[0].ident.to_string())))
|
||||
}
|
||||
}
|
||||
syn::NestedMeta::Literal(ref lit) => match *lit {
|
||||
syn::NestedMeta::Lit(ref lit) => match *lit {
|
||||
syn::Lit::Str(ref s) => {
|
||||
if ident == "setter" {
|
||||
if path.is_ident("setter") {
|
||||
res = Some(FnType::Setter(Some(s.value())))
|
||||
} else {
|
||||
res = Some(FnType::Getter(Some(s.value())))
|
||||
|
@ -370,16 +358,16 @@ fn parse_attributes(attrs: &mut Vec<syn::Attribute>) -> syn::Result<(FnType, Vec
|
|||
}
|
||||
},
|
||||
_ => {
|
||||
println!("cannot parse {:?} attribute: {:?}", ident, nested);
|
||||
println!("cannot parse {:?} attribute: {:?}", path, nested);
|
||||
}
|
||||
}
|
||||
}
|
||||
"args" => {
|
||||
} else if path.is_ident("args") {
|
||||
let attrs = PyFunctionAttr::from_meta(nested)?;
|
||||
spec.extend(attrs.arguments)
|
||||
} else {
|
||||
new_attrs.push(attr.clone())
|
||||
}
|
||||
_ => new_attrs.push(attr.clone()),
|
||||
},
|
||||
}
|
||||
syn::Meta::NameValue(_) => new_attrs.push(attr.clone()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,8 +8,7 @@ use crate::pymethod;
|
|||
use crate::pymethod::get_arg_names;
|
||||
use crate::utils;
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::quote;
|
||||
use syn::ext::IdentExt;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::Ident;
|
||||
|
||||
/// Generates the function that is called by the python interpreter to initialize the native
|
||||
|
@ -38,7 +37,7 @@ pub fn process_functions_in_module(func: &mut syn::ItemFn) {
|
|||
extract_pyfn_attrs(&mut func.attrs)
|
||||
{
|
||||
let function_to_python = add_fn_to_module(func, &python_name, pyfn_attrs);
|
||||
let function_wrapper_ident = function_wrapper_ident(&func.ident);
|
||||
let function_wrapper_ident = function_wrapper_ident(&func.sig.ident);
|
||||
let item: syn::ItemFn = syn::parse_quote! {
|
||||
fn block_wrapper() {
|
||||
#function_to_python
|
||||
|
@ -57,23 +56,14 @@ pub fn process_functions_in_module(func: &mut syn::ItemFn) {
|
|||
/// Transforms a rust fn arg parsed with syn into a method::FnArg
|
||||
fn wrap_fn_argument<'a>(input: &'a syn::FnArg, name: &'a Ident) -> Option<method::FnArg<'a>> {
|
||||
match input {
|
||||
syn::FnArg::SelfRef(_) | &syn::FnArg::SelfValue(_) => None,
|
||||
syn::FnArg::Captured(ref cap) => {
|
||||
let (mutability, by_ref, ident) = match cap.pat {
|
||||
syn::FnArg::Receiver(_) => None,
|
||||
syn::FnArg::Typed(ref cap) => {
|
||||
let (mutability, by_ref, ident) = match *cap.pat {
|
||||
syn::Pat::Ident(ref patid) => (&patid.mutability, &patid.by_ref, &patid.ident),
|
||||
_ => panic!("unsupported argument: {:?}", cap.pat),
|
||||
};
|
||||
|
||||
let py = match cap.ty {
|
||||
syn::Type::Path(ref typath) => typath
|
||||
.path
|
||||
.segments
|
||||
.last()
|
||||
.map(|seg| seg.value().ident == "Python")
|
||||
.unwrap_or(false),
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let py = crate::utils::if_type_is_python(&cap.ty);
|
||||
let opt = method::check_arg_ty_and_optional(&name, &cap.ty);
|
||||
Some(method::FnArg {
|
||||
name: ident,
|
||||
|
@ -85,15 +75,13 @@ fn wrap_fn_argument<'a>(input: &'a syn::FnArg, name: &'a Ident) -> Option<method
|
|||
reference: method::is_ref(&name, &cap.ty),
|
||||
})
|
||||
}
|
||||
syn::FnArg::Ignored(_) => panic!("ignored argument: {:?}", name),
|
||||
syn::FnArg::Inferred(_) => panic!("inferred argument: {:?}", name),
|
||||
}
|
||||
}
|
||||
|
||||
/// Extracts the data from the #[pyfn(...)] attribute of a function
|
||||
fn extract_pyfn_attrs(
|
||||
attrs: &mut Vec<syn::Attribute>,
|
||||
) -> Option<(Ident, Ident, Vec<pyfunction::Argument>)> {
|
||||
) -> Option<(syn::Path, Ident, Vec<pyfunction::Argument>)> {
|
||||
let mut new_attrs = Vec::new();
|
||||
let mut fnname = None;
|
||||
let mut modname = None;
|
||||
|
@ -101,19 +89,19 @@ fn extract_pyfn_attrs(
|
|||
|
||||
for attr in attrs.iter() {
|
||||
match attr.parse_meta() {
|
||||
Ok(syn::Meta::List(ref list)) if list.ident == "pyfn" => {
|
||||
Ok(syn::Meta::List(ref list)) if list.path.is_ident("pyfn") => {
|
||||
let meta: Vec<_> = list.nested.iter().cloned().collect();
|
||||
if meta.len() >= 2 {
|
||||
// read module name
|
||||
match meta[0] {
|
||||
syn::NestedMeta::Meta(syn::Meta::Word(ref ident)) => {
|
||||
modname = Some(ident.clone())
|
||||
syn::NestedMeta::Meta(syn::Meta::Path(ref path)) => {
|
||||
modname = Some(path.clone())
|
||||
}
|
||||
_ => panic!("The first parameter of pyfn must be a MetaItem"),
|
||||
}
|
||||
// read Python fonction name
|
||||
// read Python function name
|
||||
match meta[1] {
|
||||
syn::NestedMeta::Literal(syn::Lit::Str(ref lits)) => {
|
||||
syn::NestedMeta::Lit(syn::Lit::Str(ref lits)) => {
|
||||
fnname = Some(syn::Ident::new(&lits.value(), lits.span()));
|
||||
}
|
||||
_ => panic!("The second parameter of pyfn must be a Literal"),
|
||||
|
@ -139,10 +127,7 @@ fn extract_pyfn_attrs(
|
|||
/// Coordinates the naming of a the add-function-to-python-module function
|
||||
fn function_wrapper_ident(name: &Ident) -> Ident {
|
||||
// Make sure this ident matches the one of wrap_pyfunction
|
||||
Ident::new(
|
||||
&format!("__pyo3_get_function_{}", name.unraw().to_string()),
|
||||
Span::call_site(),
|
||||
)
|
||||
format_ident!("__pyo3_get_function_{}", name)
|
||||
}
|
||||
|
||||
/// Generates python wrapper over a function that allows adding it to a python module as a python
|
||||
|
@ -154,13 +139,13 @@ pub fn add_fn_to_module(
|
|||
) -> TokenStream {
|
||||
let mut arguments = Vec::new();
|
||||
|
||||
for input in func.decl.inputs.iter() {
|
||||
if let Some(fn_arg) = wrap_fn_argument(input, &func.ident) {
|
||||
for input in func.sig.inputs.iter() {
|
||||
if let Some(fn_arg) = wrap_fn_argument(input, &func.sig.ident) {
|
||||
arguments.push(fn_arg);
|
||||
}
|
||||
}
|
||||
|
||||
let ty = method::get_return_info(&func.decl.output);
|
||||
let ty = method::get_return_info(&func.sig.output);
|
||||
|
||||
let spec = method::FnSpec {
|
||||
tp: method::FnType::Fn,
|
||||
|
@ -169,9 +154,9 @@ pub fn add_fn_to_module(
|
|||
output: ty,
|
||||
};
|
||||
|
||||
let function_wrapper_ident = function_wrapper_ident(&func.ident);
|
||||
let function_wrapper_ident = function_wrapper_ident(&func.sig.ident);
|
||||
|
||||
let wrapper = function_c_wrapper(&func.ident, &spec);
|
||||
let wrapper = function_c_wrapper(&func.sig.ident, &spec);
|
||||
let doc = utils::get_doc(&func.attrs, true);
|
||||
|
||||
let tokens = quote! {
|
||||
|
|
|
@ -59,7 +59,7 @@ impl PyClassArgs {
|
|||
fn add_assign(&mut self, assign: &syn::ExprAssign) -> syn::Result<()> {
|
||||
let key = match *assign.left {
|
||||
syn::Expr::Path(ref exp) if exp.path.segments.len() == 1 => {
|
||||
exp.path.segments.first().unwrap().value().ident.to_string()
|
||||
exp.path.segments.first().unwrap().ident.to_string()
|
||||
}
|
||||
_ => {
|
||||
return Err(syn::Error::new_spanned(assign, "could not parse argument"));
|
||||
|
@ -123,7 +123,7 @@ impl PyClassArgs {
|
|||
|
||||
/// Match a key/value flag
|
||||
fn add_path(&mut self, exp: &syn::ExprPath) -> syn::Result<()> {
|
||||
let flag = exp.path.segments.first().unwrap().value().ident.to_string();
|
||||
let flag = exp.path.segments.first().unwrap().ident.to_string();
|
||||
let path = match flag.as_str() {
|
||||
"gc" => {
|
||||
parse_quote! {pyo3::type_object::PY_TYPE_FLAG_GC}
|
||||
|
@ -132,6 +132,12 @@ impl PyClassArgs {
|
|||
parse_quote! {pyo3::type_object::PY_TYPE_FLAG_WEAKREF}
|
||||
}
|
||||
"subclass" => {
|
||||
if cfg!(not(feature = "unsound-subclass")) {
|
||||
return Err(syn::Error::new_spanned(
|
||||
exp.path.clone(),
|
||||
"You need to activate the `unsound-subclass` feature if you want to use subclassing",
|
||||
));
|
||||
}
|
||||
parse_quote! {pyo3::type_object::PY_TYPE_FLAG_BASETYPE}
|
||||
}
|
||||
"dict" => {
|
||||
|
@ -178,28 +184,23 @@ fn parse_descriptors(item: &mut syn::Field) -> syn::Result<Vec<FnType>> {
|
|||
let mut new_attrs = Vec::new();
|
||||
for attr in item.attrs.iter() {
|
||||
if let Ok(syn::Meta::List(ref list)) = attr.parse_meta() {
|
||||
match list.ident.to_string().as_str() {
|
||||
"pyo3" => {
|
||||
for meta in list.nested.iter() {
|
||||
if let syn::NestedMeta::Meta(ref metaitem) = meta {
|
||||
match metaitem.name().to_string().as_str() {
|
||||
"get" => {
|
||||
descs.push(FnType::Getter(None));
|
||||
}
|
||||
"set" => {
|
||||
descs.push(FnType::Setter(None));
|
||||
}
|
||||
_ => {
|
||||
return Err(syn::Error::new_spanned(
|
||||
metaitem,
|
||||
"Only get and set are supported",
|
||||
));
|
||||
}
|
||||
}
|
||||
if list.path.is_ident("pyo3") {
|
||||
for meta in list.nested.iter() {
|
||||
if let syn::NestedMeta::Meta(ref metaitem) = meta {
|
||||
if metaitem.path().is_ident("get") {
|
||||
descs.push(FnType::Getter(None));
|
||||
} else if metaitem.path().is_ident("set") {
|
||||
descs.push(FnType::Setter(None));
|
||||
} else {
|
||||
return Err(syn::Error::new_spanned(
|
||||
metaitem,
|
||||
"Only get and set are supported",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => new_attrs.push(attr.clone()),
|
||||
} else {
|
||||
new_attrs.push(attr.clone())
|
||||
}
|
||||
} else {
|
||||
new_attrs.push(attr.clone());
|
||||
|
@ -296,12 +297,15 @@ fn impl_class(
|
|||
// insert space for weak ref
|
||||
let mut has_weakref = false;
|
||||
let mut has_dict = false;
|
||||
let mut has_gc = false;
|
||||
for f in attr.flags.iter() {
|
||||
if let syn::Expr::Path(ref epath) = f {
|
||||
if epath.path == parse_quote! {pyo3::type_object::PY_TYPE_FLAG_WEAKREF} {
|
||||
has_weakref = true;
|
||||
} else if epath.path == parse_quote! {pyo3::type_object::PY_TYPE_FLAG_DICT} {
|
||||
has_dict = true;
|
||||
} else if epath.path == parse_quote! {pyo3::type_object::PY_TYPE_FLAG_GC} {
|
||||
has_gc = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -321,6 +325,22 @@ fn impl_class(
|
|||
quote! { None }
|
||||
};
|
||||
|
||||
// Enforce at compile time that PyGCProtocol is implemented
|
||||
let gc_impl = if has_gc {
|
||||
let closure_name = format!("__assertion_closure_{}", cls.to_string());
|
||||
let closure_token = syn::Ident::new(&closure_name, Span::call_site());
|
||||
quote! {
|
||||
fn #closure_token() {
|
||||
use pyo3::class;
|
||||
|
||||
fn _assert_implements_protocol<'p, T: pyo3::class::PyGCProtocol<'p>>() {}
|
||||
_assert_implements_protocol::<#cls>();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
|
||||
let inventory_impl = impl_inventory(&cls);
|
||||
|
||||
let base = &attr.base;
|
||||
|
@ -356,15 +376,18 @@ fn impl_class(
|
|||
}
|
||||
}
|
||||
|
||||
impl pyo3::IntoPyObject for #cls {
|
||||
fn into_object(self, py: pyo3::Python) -> pyo3::PyObject {
|
||||
pyo3::Py::new(py, self).unwrap().into_object(py)
|
||||
impl pyo3::IntoPy<PyObject> for #cls {
|
||||
fn into_py(self, py: pyo3::Python) -> pyo3::PyObject {
|
||||
pyo3::IntoPy::into_py(pyo3::Py::new(py, self).unwrap(), py)
|
||||
}
|
||||
}
|
||||
|
||||
#inventory_impl
|
||||
|
||||
#extra
|
||||
|
||||
#gc_impl
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -417,9 +440,12 @@ fn impl_descriptors(cls: &syn::Type, descriptors: Vec<(syn::Field, Vec<FnType>)>
|
|||
|
||||
let field_ty = &field.ty;
|
||||
match *desc {
|
||||
FnType::Getter(ref getter) => {
|
||||
impl_py_getter_def(&name, doc, getter, &impl_wrap_getter(&cls, &name))
|
||||
}
|
||||
FnType::Getter(ref getter) => impl_py_getter_def(
|
||||
&name,
|
||||
doc,
|
||||
getter,
|
||||
&impl_wrap_getter(&cls, &name, false),
|
||||
),
|
||||
FnType::Setter(ref setter) => {
|
||||
let setter_name =
|
||||
syn::Ident::new(&format!("set_{}", name), Span::call_site());
|
||||
|
|
|
@ -2,15 +2,15 @@
|
|||
|
||||
use syn::parse::ParseBuffer;
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::{Ident, NestedMeta};
|
||||
use syn::{NestedMeta, Path};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Argument {
|
||||
VarArgsSeparator,
|
||||
VarArgs(syn::Ident),
|
||||
KeywordArgs(syn::Ident),
|
||||
Arg(syn::Ident, Option<String>),
|
||||
Kwarg(syn::Ident, String),
|
||||
VarArgs(syn::Path),
|
||||
KeywordArgs(syn::Path),
|
||||
Arg(syn::Path, Option<String>),
|
||||
Kwarg(syn::Path, String),
|
||||
}
|
||||
|
||||
/// The attributes of the pyfunction macro
|
||||
|
@ -41,11 +41,11 @@ impl PyFunctionAttr {
|
|||
|
||||
pub fn add_item(&mut self, item: &NestedMeta) -> syn::Result<()> {
|
||||
match item {
|
||||
NestedMeta::Meta(syn::Meta::Word(ref ident)) => self.add_work(item, ident)?,
|
||||
NestedMeta::Meta(syn::Meta::Path(ref ident)) => self.add_work(item, ident)?,
|
||||
NestedMeta::Meta(syn::Meta::NameValue(ref nv)) => {
|
||||
self.add_name_value(item, nv)?;
|
||||
}
|
||||
NestedMeta::Literal(ref lit) => {
|
||||
NestedMeta::Lit(ref lit) => {
|
||||
self.add_literal(item, lit)?;
|
||||
}
|
||||
_ => {
|
||||
|
@ -89,7 +89,7 @@ impl PyFunctionAttr {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn add_work(&mut self, item: &NestedMeta, ident: &Ident) -> syn::Result<()> {
|
||||
fn add_work(&mut self, item: &NestedMeta, path: &Path) -> syn::Result<()> {
|
||||
// self.arguments in form somename
|
||||
if self.has_kwargs {
|
||||
return Err(syn::Error::new_spanned(
|
||||
|
@ -103,7 +103,7 @@ impl PyFunctionAttr {
|
|||
"syntax error, argument is not allowed after keyword argument",
|
||||
));
|
||||
}
|
||||
self.arguments.push(Argument::Arg(ident.clone(), None));
|
||||
self.arguments.push(Argument::Arg(path.clone(), None));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -122,7 +122,7 @@ impl PyFunctionAttr {
|
|||
return Err(syn::Error::new_spanned(item, "*(var args) is defined"));
|
||||
}
|
||||
self.has_varargs = true;
|
||||
self.arguments.push(Argument::VarArgs(nv.ident.clone()));
|
||||
self.arguments.push(Argument::VarArgs(nv.path.clone()));
|
||||
} else if litstr.value() == "**" {
|
||||
// kwargs="**"
|
||||
if self.has_kwargs {
|
||||
|
@ -132,10 +132,10 @@ impl PyFunctionAttr {
|
|||
));
|
||||
}
|
||||
self.has_kwargs = true;
|
||||
self.arguments.push(Argument::KeywordArgs(nv.ident.clone()));
|
||||
self.arguments.push(Argument::KeywordArgs(nv.path.clone()));
|
||||
} else if self.has_varargs {
|
||||
self.arguments
|
||||
.push(Argument::Kwarg(nv.ident.clone(), litstr.value().clone()))
|
||||
.push(Argument::Kwarg(nv.path.clone(), litstr.value()))
|
||||
} else {
|
||||
if self.has_kwargs {
|
||||
return Err(syn::Error::new_spanned(
|
||||
|
@ -144,18 +144,14 @@ impl PyFunctionAttr {
|
|||
));
|
||||
}
|
||||
self.has_kw = true;
|
||||
self.arguments.push(Argument::Arg(
|
||||
nv.ident.clone(),
|
||||
Some(litstr.value().clone()),
|
||||
))
|
||||
self.arguments
|
||||
.push(Argument::Arg(nv.path.clone(), Some(litstr.value())))
|
||||
}
|
||||
}
|
||||
syn::Lit::Int(ref litint) => {
|
||||
if self.has_varargs {
|
||||
self.arguments.push(Argument::Kwarg(
|
||||
nv.ident.clone(),
|
||||
format!("{}", litint.value()),
|
||||
));
|
||||
self.arguments
|
||||
.push(Argument::Kwarg(nv.path.clone(), format!("{}", litint)));
|
||||
} else {
|
||||
if self.has_kwargs {
|
||||
return Err(syn::Error::new_spanned(
|
||||
|
@ -164,16 +160,14 @@ impl PyFunctionAttr {
|
|||
));
|
||||
}
|
||||
self.has_kw = true;
|
||||
self.arguments.push(Argument::Arg(
|
||||
nv.ident.clone(),
|
||||
Some(format!("{}", litint.value())),
|
||||
));
|
||||
self.arguments
|
||||
.push(Argument::Arg(nv.path.clone(), Some(format!("{}", litint))));
|
||||
}
|
||||
}
|
||||
syn::Lit::Bool(ref litb) => {
|
||||
if self.has_varargs {
|
||||
self.arguments
|
||||
.push(Argument::Kwarg(nv.ident.clone(), format!("{}", litb.value)));
|
||||
.push(Argument::Kwarg(nv.path.clone(), format!("{}", litb.value)));
|
||||
} else {
|
||||
if self.has_kwargs {
|
||||
return Err(syn::Error::new_spanned(
|
||||
|
@ -183,7 +177,7 @@ impl PyFunctionAttr {
|
|||
}
|
||||
self.has_kw = true;
|
||||
self.arguments.push(Argument::Arg(
|
||||
nv.ident.clone(),
|
||||
nv.path.clone(),
|
||||
Some(format!("{}", litb.value)),
|
||||
));
|
||||
}
|
||||
|
|
|
@ -16,11 +16,11 @@ pub fn build_py_methods(ast: &mut syn::ItemImpl) -> syn::Result<TokenStream> {
|
|||
"#[pymethods] can not be used with lifetime parameters or generics",
|
||||
))
|
||||
} else {
|
||||
Ok(impl_methods(&ast.self_ty, &mut ast.items))
|
||||
impl_methods(&ast.self_ty, &mut ast.items)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn impl_methods(ty: &syn::Type, impls: &mut Vec<syn::ImplItem>) -> TokenStream {
|
||||
pub fn impl_methods(ty: &syn::Type, impls: &mut Vec<syn::ImplItem>) -> syn::Result<TokenStream> {
|
||||
// get method names in impl block
|
||||
let mut methods = Vec::new();
|
||||
for iimpl in impls.iter_mut() {
|
||||
|
@ -31,16 +31,16 @@ pub fn impl_methods(ty: &syn::Type, impls: &mut Vec<syn::ImplItem>) -> TokenStre
|
|||
&name,
|
||||
&mut meth.sig,
|
||||
&mut meth.attrs,
|
||||
));
|
||||
)?);
|
||||
}
|
||||
}
|
||||
|
||||
quote! {
|
||||
Ok(quote! {
|
||||
pyo3::inventory::submit! {
|
||||
#![crate = pyo3] {
|
||||
type TyInventory = <#ty as pyo3::class::methods::PyMethodsInventoryDispatch>::InventoryType;
|
||||
<TyInventory as pyo3::class::methods::PyMethodsInventory>::new(&[#(#methods),*])
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -7,15 +7,15 @@ use quote::quote;
|
|||
pub fn gen_py_method(
|
||||
cls: &syn::Type,
|
||||
name: &syn::Ident,
|
||||
sig: &mut syn::MethodSig,
|
||||
sig: &mut syn::Signature,
|
||||
meth_attrs: &mut Vec<syn::Attribute>,
|
||||
) -> TokenStream {
|
||||
check_generic(name, sig);
|
||||
) -> syn::Result<TokenStream> {
|
||||
check_generic(name, sig)?;
|
||||
|
||||
let doc = utils::get_doc(&meth_attrs, true);
|
||||
let spec = FnSpec::parse(name, sig, meth_attrs).unwrap();
|
||||
let spec = FnSpec::parse(name, sig, meth_attrs)?;
|
||||
|
||||
match spec.tp {
|
||||
Ok(match spec.tp {
|
||||
FnType::Fn => impl_py_method_def(name, doc, &spec, &impl_wrap(cls, name, &spec, true)),
|
||||
FnType::PySelf(ref self_ty) => impl_py_method_def(
|
||||
name,
|
||||
|
@ -31,28 +31,43 @@ pub fn gen_py_method(
|
|||
impl_py_method_def_static(name, doc, &impl_wrap_static(cls, name, &spec))
|
||||
}
|
||||
FnType::Getter(ref getter) => {
|
||||
impl_py_getter_def(name, doc, getter, &impl_wrap_getter(cls, name))
|
||||
let takes_py = match &*spec.args {
|
||||
[] => false,
|
||||
[arg] if utils::if_type_is_python(arg.ty) => true,
|
||||
_ => {
|
||||
return Err(syn::Error::new_spanned(
|
||||
spec.args[0].ty,
|
||||
"Getter function can only have one argument of type pyo3::Python!",
|
||||
))
|
||||
}
|
||||
};
|
||||
impl_py_getter_def(name, doc, getter, &impl_wrap_getter(cls, name, takes_py))
|
||||
}
|
||||
FnType::Setter(ref setter) => {
|
||||
impl_py_setter_def(name, doc, setter, &impl_wrap_setter(cls, name, &spec))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn check_generic(name: &syn::Ident, sig: &syn::MethodSig) {
|
||||
for param in &sig.decl.generics.params {
|
||||
fn check_generic(name: &syn::Ident, sig: &syn::Signature) -> syn::Result<()> {
|
||||
let err_msg = |typ| {
|
||||
format!(
|
||||
"A Python method can't have a generic {} parameter: {}",
|
||||
name, typ
|
||||
)
|
||||
};
|
||||
for param in &sig.generics.params {
|
||||
match param {
|
||||
syn::GenericParam::Lifetime(_) => {}
|
||||
syn::GenericParam::Type(_) => panic!(
|
||||
"A Python method can't have a generic type parameter: {}",
|
||||
name
|
||||
),
|
||||
syn::GenericParam::Const(_) => panic!(
|
||||
"A Python method can't have a const generic parameter: {}",
|
||||
name
|
||||
),
|
||||
syn::GenericParam::Type(_) => {
|
||||
return Err(syn::Error::new_spanned(param, err_msg("type")))
|
||||
}
|
||||
syn::GenericParam::Const(_) => {
|
||||
return Err(syn::Error::new_spanned(param, err_msg("const")))
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Generate function wrapper (PyCFunction, PyCFunctionWithKeywords)
|
||||
|
@ -302,7 +317,12 @@ pub fn impl_wrap_static(cls: &syn::Type, name: &syn::Ident, spec: &FnSpec<'_>) -
|
|||
}
|
||||
|
||||
/// Generate functiona wrapper (PyCFunction, PyCFunctionWithKeywords)
|
||||
pub(crate) fn impl_wrap_getter(cls: &syn::Type, name: &syn::Ident) -> TokenStream {
|
||||
pub(crate) fn impl_wrap_getter(cls: &syn::Type, name: &syn::Ident, takes_py: bool) -> TokenStream {
|
||||
let fncall = if takes_py {
|
||||
quote! { _slf.#name(_py) }
|
||||
} else {
|
||||
quote! { _slf.#name() }
|
||||
};
|
||||
quote! {
|
||||
unsafe extern "C" fn __wrap(
|
||||
_slf: *mut pyo3::ffi::PyObject, _: *mut ::std::os::raw::c_void) -> *mut pyo3::ffi::PyObject
|
||||
|
@ -313,11 +333,11 @@ pub(crate) fn impl_wrap_getter(cls: &syn::Type, name: &syn::Ident) -> TokenStrea
|
|||
let _py = pyo3::Python::assume_gil_acquired();
|
||||
let _slf = _py.mut_from_borrowed_ptr::<#cls>(_slf);
|
||||
|
||||
let result = pyo3::derive_utils::IntoPyResult::into_py_result(_slf.#name());
|
||||
let result = pyo3::derive_utils::IntoPyResult::into_py_result(#fncall);
|
||||
|
||||
match result {
|
||||
Ok(val) => {
|
||||
pyo3::IntoPyPointer::into_ptr(val.into_object(_py))
|
||||
pyo3::IntoPyPointer::into_ptr(pyo3::IntoPy::<PyObject>::into_py(val, _py))
|
||||
}
|
||||
Err(e) => {
|
||||
e.restore(_py);
|
||||
|
|
|
@ -12,7 +12,7 @@ use quote::ToTokens;
|
|||
pub fn build_py_proto(ast: &mut syn::ItemImpl) -> syn::Result<TokenStream> {
|
||||
if let Some((_, ref mut path, _)) = ast.trait_ {
|
||||
let proto = if let Some(ref mut segment) = path.segments.last() {
|
||||
match segment.value().ident.to_string().as_str() {
|
||||
match segment.ident.to_string().as_str() {
|
||||
"PyObjectProtocol" => &defs::OBJECT,
|
||||
"PyAsyncProtocol" => &defs::ASYNC,
|
||||
"PyMappingProtocol" => &defs::MAPPING,
|
||||
|
@ -47,10 +47,10 @@ pub fn build_py_proto(ast: &mut syn::ItemImpl) -> syn::Result<TokenStream> {
|
|||
|
||||
Ok(tokens)
|
||||
} else {
|
||||
return Err(syn::Error::new_spanned(
|
||||
Err(syn::Error::new_spanned(
|
||||
ast,
|
||||
"#[pyproto] can only be used with protocol trait implementations",
|
||||
));
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,12 +2,24 @@
|
|||
|
||||
use proc_macro2::Span;
|
||||
use proc_macro2::TokenStream;
|
||||
use syn;
|
||||
|
||||
pub fn print_err(msg: String, t: TokenStream) {
|
||||
println!("Error: {} in '{}'", msg, t.to_string());
|
||||
}
|
||||
|
||||
/// Check if the given type `ty` is `pyo3::Python`.
|
||||
pub fn if_type_is_python(ty: &syn::Type) -> bool {
|
||||
match ty {
|
||||
syn::Type::Path(ref typath) => typath
|
||||
.path
|
||||
.segments
|
||||
.last()
|
||||
.map(|seg| seg.ident == "Python")
|
||||
.unwrap_or(false),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(althonos): not sure the docstring formatting is on par here.
|
||||
pub fn get_doc(attrs: &[syn::Attribute], null_terminated: bool) -> syn::Lit {
|
||||
let mut doc = Vec::new();
|
||||
|
@ -16,8 +28,8 @@ pub fn get_doc(attrs: &[syn::Attribute], null_terminated: bool) -> syn::Lit {
|
|||
// let mut span = None;
|
||||
|
||||
for attr in attrs.iter() {
|
||||
if let Some(syn::Meta::NameValue(ref metanv)) = attr.interpret_meta() {
|
||||
if metanv.ident == "doc" {
|
||||
if let Ok(syn::Meta::NameValue(ref metanv)) = attr.parse_meta() {
|
||||
if metanv.path.is_ident("doc") {
|
||||
// span = Some(metanv.span());
|
||||
if let syn::Lit::Str(ref litstr) = metanv.lit {
|
||||
let d = litstr.value();
|
||||
|
|
|
@ -14,7 +14,10 @@ edition = "2018"
|
|||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
quote= "0.6.12"
|
||||
proc-macro2 = "0.4.30"
|
||||
syn = { version = "0.15.34", features = ["full", "extra-traits"] }
|
||||
quote = "1"
|
||||
proc-macro2 = "1"
|
||||
syn = { version = "1", features = ["full", "extra-traits"] }
|
||||
pyo3-derive-backend = { path = "../pyo3-derive-backend", version = "=0.7.0" }
|
||||
|
||||
[features]
|
||||
unsound-subclass = ["pyo3-derive-backend/unsound-subclass"]
|
||||
|
|
|
@ -20,14 +20,14 @@ pub fn pymodule(attr: TokenStream, input: TokenStream) -> TokenStream {
|
|||
let mut ast = parse_macro_input!(input as syn::ItemFn);
|
||||
|
||||
let modname = if attr.is_empty() {
|
||||
ast.ident.clone()
|
||||
ast.sig.ident.clone()
|
||||
} else {
|
||||
parse_macro_input!(attr as syn::Ident)
|
||||
};
|
||||
|
||||
process_functions_in_module(&mut ast);
|
||||
|
||||
let expanded = py_init(&ast.ident, &modname, get_doc(&ast.attrs, false));
|
||||
let expanded = py_init(&ast.sig.ident, &modname, get_doc(&ast.attrs, false));
|
||||
|
||||
quote!(
|
||||
#ast
|
||||
|
@ -78,7 +78,7 @@ pub fn pyfunction(attr: TokenStream, input: TokenStream) -> TokenStream {
|
|||
let ast = parse_macro_input!(input as syn::ItemFn);
|
||||
let args = parse_macro_input!(attr as PyFunctionAttr);
|
||||
|
||||
let python_name = syn::Ident::new(&ast.ident.unraw().to_string(), Span::call_site());
|
||||
let python_name = syn::Ident::new(&ast.sig.ident.unraw().to_string(), Span::call_site());
|
||||
let expanded = add_fn_to_module(&ast, &python_name, args.arguments);
|
||||
|
||||
quote!(
|
||||
|
|
|
@ -26,11 +26,13 @@ use crate::Python;
|
|||
use libc;
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw;
|
||||
use std::pin::Pin;
|
||||
use std::{cell, mem, slice};
|
||||
|
||||
/// Allows access to the underlying buffer used by a python object such as `bytes`, `bytearray` or `array.array`.
|
||||
// use Pin<Box> because Python expects that the Py_buffer struct has a stable memory address
|
||||
#[repr(transparent)]
|
||||
pub struct PyBuffer(Box<ffi::Py_buffer>); // use Box<> because Python expects that the Py_buffer struct has a stable memory address
|
||||
pub struct PyBuffer(Pin<Box<ffi::Py_buffer>>);
|
||||
|
||||
// PyBuffer is thread-safe: the shape of the buffer is immutable while a Py_buffer exists.
|
||||
// Accessing the buffer contents is protected using the GIL.
|
||||
|
@ -165,7 +167,7 @@ impl PyBuffer {
|
|||
/// Get the underlying buffer from the specified python object.
|
||||
pub fn get(py: Python, obj: &PyAny) -> PyResult<PyBuffer> {
|
||||
unsafe {
|
||||
let mut buf = Box::new(mem::zeroed::<ffi::Py_buffer>());
|
||||
let mut buf = Box::pin(mem::zeroed::<ffi::Py_buffer>());
|
||||
err::error_on_minusone(
|
||||
py,
|
||||
ffi::PyObject_GetBuffer(obj.as_ptr(), &mut *buf, ffi::PyBUF_FULL_RO),
|
||||
|
@ -704,6 +706,7 @@ mod test {
|
|||
assert_eq!(buffer.to_vec::<u8>(py).unwrap(), b"abcde");
|
||||
}
|
||||
|
||||
#[allow(clippy::float_cmp)] // The test wants to ensure that no precision was lost on the Python round-trip
|
||||
#[test]
|
||||
fn test_array_buffer() {
|
||||
let gil = Python::acquire_gil();
|
||||
|
@ -713,7 +716,7 @@ mod test {
|
|||
.unwrap()
|
||||
.call_method("array", ("f", (1.0, 1.5, 2.0, 2.5)), None)
|
||||
.unwrap();
|
||||
let buffer = PyBuffer::get(py, array.into()).unwrap();
|
||||
let buffer = PyBuffer::get(py, array).unwrap();
|
||||
assert_eq!(buffer.dimensions(), 1);
|
||||
assert_eq!(buffer.item_count(), 4);
|
||||
assert_eq!(buffer.format().to_str().unwrap(), "f");
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
use crate::err::PyResult;
|
||||
use crate::exceptions::OverflowError;
|
||||
use crate::ffi::{self, Py_hash_t};
|
||||
use crate::Python;
|
||||
use crate::{IntoPyObject, IntoPyPointer};
|
||||
use crate::IntoPyPointer;
|
||||
use crate::{IntoPy, PyObject, Python};
|
||||
use std::os::raw::c_int;
|
||||
use std::{isize, ptr};
|
||||
|
||||
|
@ -21,12 +21,12 @@ pub struct PyObjectCallbackConverter;
|
|||
|
||||
impl<S> CallbackConverter<S> for PyObjectCallbackConverter
|
||||
where
|
||||
S: IntoPyObject,
|
||||
S: IntoPy<PyObject>,
|
||||
{
|
||||
type R = *mut ffi::PyObject;
|
||||
|
||||
fn convert(val: S, py: Python) -> *mut ffi::PyObject {
|
||||
val.into_object(py).into_ptr()
|
||||
val.into_py(py).into_ptr()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -11,14 +11,14 @@
|
|||
use crate::callback::{BoolCallbackConverter, HashConverter, PyObjectCallbackConverter};
|
||||
use crate::class::methods::PyMethodDef;
|
||||
use crate::err::{PyErr, PyResult};
|
||||
use crate::exceptions;
|
||||
use crate::ffi;
|
||||
use crate::objectprotocol::ObjectProtocol;
|
||||
use crate::type_object::PyTypeInfo;
|
||||
use crate::types::PyAny;
|
||||
use crate::FromPyObject;
|
||||
use crate::IntoPyPointer;
|
||||
use crate::Python;
|
||||
use crate::{FromPyObject, IntoPyObject};
|
||||
use crate::{exceptions, IntoPy, PyObject};
|
||||
use std::os::raw::c_int;
|
||||
use std::ptr;
|
||||
|
||||
|
@ -109,7 +109,7 @@ pub trait PyObjectProtocol<'p>: PyTypeInfo {
|
|||
|
||||
pub trait PyObjectGetAttrProtocol<'p>: PyObjectProtocol<'p> {
|
||||
type Name: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
pub trait PyObjectSetAttrProtocol<'p>: PyObjectProtocol<'p> {
|
||||
|
@ -122,16 +122,16 @@ pub trait PyObjectDelAttrProtocol<'p>: PyObjectProtocol<'p> {
|
|||
type Result: Into<PyResult<()>>;
|
||||
}
|
||||
pub trait PyObjectStrProtocol<'p>: PyObjectProtocol<'p> {
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
pub trait PyObjectReprProtocol<'p>: PyObjectProtocol<'p> {
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
pub trait PyObjectFormatProtocol<'p>: PyObjectProtocol<'p> {
|
||||
type Format: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
pub trait PyObjectHashProtocol<'p>: PyObjectProtocol<'p> {
|
||||
|
@ -141,12 +141,12 @@ pub trait PyObjectBoolProtocol<'p>: PyObjectProtocol<'p> {
|
|||
type Result: Into<PyResult<bool>>;
|
||||
}
|
||||
pub trait PyObjectBytesProtocol<'p>: PyObjectProtocol<'p> {
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
pub trait PyObjectRichcmpProtocol<'p>: PyObjectProtocol<'p> {
|
||||
type Other: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
|
@ -207,12 +207,37 @@ where
|
|||
T: for<'p> PyObjectGetAttrProtocol<'p>,
|
||||
{
|
||||
fn tp_getattro() -> Option<ffi::binaryfunc> {
|
||||
py_binary_func!(
|
||||
PyObjectGetAttrProtocol,
|
||||
T::__getattr__,
|
||||
T::Success,
|
||||
PyObjectCallbackConverter
|
||||
)
|
||||
#[allow(unused_mut)]
|
||||
unsafe extern "C" fn wrap<T>(
|
||||
slf: *mut ffi::PyObject,
|
||||
arg: *mut ffi::PyObject,
|
||||
) -> *mut ffi::PyObject
|
||||
where
|
||||
T: for<'p> PyObjectGetAttrProtocol<'p>,
|
||||
{
|
||||
let _pool = crate::GILPool::new();
|
||||
let py = Python::assume_gil_acquired();
|
||||
|
||||
// Behave like python's __getattr__ (as opposed to __getattribute__) and check
|
||||
// for existing fields and methods first
|
||||
let existing = ffi::PyObject_GenericGetAttr(slf, arg);
|
||||
if existing.is_null() {
|
||||
// PyObject_HasAttr also tries to get an object and clears the error if it fails
|
||||
ffi::PyErr_Clear();
|
||||
} else {
|
||||
return existing;
|
||||
}
|
||||
|
||||
let slf = py.mut_from_borrowed_ptr::<T>(slf);
|
||||
let arg = py.from_borrowed_ptr::<crate::types::PyAny>(arg);
|
||||
|
||||
let result = match arg.extract() {
|
||||
Ok(arg) => slf.__getattr__(arg).into(),
|
||||
Err(e) => Err(e),
|
||||
};
|
||||
crate::callback::cb_convert(PyObjectCallbackConverter, py, result)
|
||||
}
|
||||
Some(wrap::<T>)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -438,7 +463,7 @@ where
|
|||
Err(e) => Err(e),
|
||||
};
|
||||
match res {
|
||||
Ok(val) => val.into_object(py).into_ptr(),
|
||||
Ok(val) => val.into_py(py).into_ptr(),
|
||||
Err(e) => {
|
||||
e.restore(py);
|
||||
ptr::null_mut()
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
use crate::class::methods::PyMethodDef;
|
||||
use crate::err::PyResult;
|
||||
use crate::type_object::PyTypeInfo;
|
||||
use crate::PyObject;
|
||||
|
||||
/// Context manager interface
|
||||
#[allow(unused_variables)]
|
||||
|
@ -32,7 +33,7 @@ pub trait PyContextProtocol<'p>: PyTypeInfo {
|
|||
}
|
||||
|
||||
pub trait PyContextEnterProtocol<'p>: PyContextProtocol<'p> {
|
||||
type Success: crate::IntoPyObject;
|
||||
type Success: crate::IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
|
@ -40,7 +41,7 @@ pub trait PyContextExitProtocol<'p>: PyContextProtocol<'p> {
|
|||
type ExcType: crate::FromPyObject<'p>;
|
||||
type ExcValue: crate::FromPyObject<'p>;
|
||||
type Traceback: crate::FromPyObject<'p>;
|
||||
type Success: crate::IntoPyObject;
|
||||
type Success: crate::IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
use crate::callback::{PyObjectCallbackConverter, UnitCallbackConverter};
|
||||
use crate::class::methods::PyMethodDef;
|
||||
use crate::err::PyResult;
|
||||
use crate::ffi;
|
||||
use crate::type_object::PyTypeInfo;
|
||||
use crate::types::{PyAny, PyType};
|
||||
use crate::{FromPyObject, IntoPyObject};
|
||||
use crate::FromPyObject;
|
||||
use crate::{ffi, IntoPy, PyObject};
|
||||
use std::os::raw::c_int;
|
||||
|
||||
/// Descriptor interface
|
||||
|
@ -49,7 +49,7 @@ pub trait PyDescrProtocol<'p>: PyTypeInfo {
|
|||
pub trait PyDescrGetProtocol<'p>: PyDescrProtocol<'p> {
|
||||
type Inst: FromPyObject<'p>;
|
||||
type Owner: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,15 +13,9 @@ use std::os::raw::{c_int, c_void};
|
|||
pub struct PyTraverseError(c_int);
|
||||
|
||||
/// GC support
|
||||
#[allow(unused_variables)]
|
||||
pub trait PyGCProtocol<'p>: PyTypeInfo {
|
||||
fn __traverse__(&'p self, visit: PyVisit) -> Result<(), PyTraverseError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn __clear__(&'p mut self) {
|
||||
unimplemented!()
|
||||
}
|
||||
fn __traverse__(&'p self, visit: PyVisit) -> Result<(), PyTraverseError>;
|
||||
fn __clear__(&'p mut self);
|
||||
}
|
||||
|
||||
pub trait PyGCTraverseProtocol<'p>: PyGCProtocol<'p> {}
|
||||
|
|
|
@ -4,12 +4,11 @@
|
|||
|
||||
use crate::callback::{CallbackConverter, PyObjectCallbackConverter};
|
||||
use crate::err::PyResult;
|
||||
use crate::ffi;
|
||||
use crate::instance::PyRefMut;
|
||||
use crate::type_object::PyTypeInfo;
|
||||
use crate::IntoPyObject;
|
||||
use crate::IntoPyPointer;
|
||||
use crate::Python;
|
||||
use crate::{ffi, IntoPy, PyObject};
|
||||
use std::ptr;
|
||||
|
||||
/// Python Iterator Interface.
|
||||
|
@ -34,12 +33,12 @@ pub trait PyIterProtocol<'p>: PyTypeInfo + Sized {
|
|||
}
|
||||
|
||||
pub trait PyIterIterProtocol<'p>: PyIterProtocol<'p> {
|
||||
type Success: crate::IntoPyObject;
|
||||
type Success: crate::IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyIterNextProtocol<'p>: PyIterProtocol<'p> {
|
||||
type Success: crate::IntoPyObject;
|
||||
type Success: crate::IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Option<Self::Success>>>;
|
||||
}
|
||||
|
||||
|
@ -111,13 +110,13 @@ struct IterNextConverter;
|
|||
|
||||
impl<T> CallbackConverter<Option<T>> for IterNextConverter
|
||||
where
|
||||
T: IntoPyObject,
|
||||
T: IntoPy<PyObject>,
|
||||
{
|
||||
type R = *mut ffi::PyObject;
|
||||
|
||||
fn convert(val: Option<T>, py: Python) -> *mut ffi::PyObject {
|
||||
match val {
|
||||
Some(val) => val.into_object(py).into_ptr(),
|
||||
Some(val) => val.into_py(py).into_ptr(),
|
||||
None => unsafe {
|
||||
ffi::PyErr_SetNone(ffi::PyExc_StopIteration);
|
||||
ptr::null_mut()
|
||||
|
|
|
@ -21,7 +21,7 @@ macro_rules! py_unary_func {
|
|||
let py = $crate::Python::assume_gil_acquired();
|
||||
let slf = py.mut_from_borrowed_ptr::<T>(slf);
|
||||
let res = slf.$f().into();
|
||||
$crate::callback::cb_convert($conv, py, res)
|
||||
$crate::callback::cb_convert($conv, py, res.map(|x| x))
|
||||
}
|
||||
Some(wrap::<$class>)
|
||||
}};
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
use crate::callback::{LenResultConverter, PyObjectCallbackConverter};
|
||||
use crate::class::methods::PyMethodDef;
|
||||
use crate::err::{PyErr, PyResult};
|
||||
use crate::exceptions;
|
||||
use crate::ffi;
|
||||
use crate::type_object::PyTypeInfo;
|
||||
use crate::FromPyObject;
|
||||
use crate::Python;
|
||||
use crate::{FromPyObject, IntoPyObject};
|
||||
use crate::{exceptions, IntoPy, PyObject};
|
||||
|
||||
/// Mapping interface
|
||||
#[allow(unused_variables)]
|
||||
|
@ -74,7 +74,7 @@ pub trait PyMappingLenProtocol<'p>: PyMappingProtocol<'p> {
|
|||
|
||||
pub trait PyMappingGetItemProtocol<'p>: PyMappingProtocol<'p> {
|
||||
type Key: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
|
@ -90,7 +90,7 @@ pub trait PyMappingDelItemProtocol<'p>: PyMappingProtocol<'p> {
|
|||
}
|
||||
|
||||
pub trait PyMappingIterProtocol<'p>: PyMappingProtocol<'p> {
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
|
@ -100,7 +100,7 @@ pub trait PyMappingContainsProtocol<'p>: PyMappingProtocol<'p> {
|
|||
}
|
||||
|
||||
pub trait PyMappingReversedProtocol<'p>: PyMappingProtocol<'p> {
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,9 +7,9 @@ use crate::callback::PyObjectCallbackConverter;
|
|||
use crate::class::basic::PyObjectProtocolImpl;
|
||||
use crate::class::methods::PyMethodDef;
|
||||
use crate::err::PyResult;
|
||||
use crate::ffi;
|
||||
use crate::type_object::PyTypeInfo;
|
||||
use crate::{FromPyObject, IntoPyObject};
|
||||
use crate::FromPyObject;
|
||||
use crate::{ffi, IntoPy, PyObject};
|
||||
|
||||
/// Number interface
|
||||
#[allow(unused_variables)]
|
||||
|
@ -323,56 +323,56 @@ pub trait PyNumberProtocol<'p>: PyTypeInfo {
|
|||
pub trait PyNumberAddProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Left: FromPyObject<'p>;
|
||||
type Right: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberSubProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Left: FromPyObject<'p>;
|
||||
type Right: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberMulProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Left: FromPyObject<'p>;
|
||||
type Right: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberMatmulProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Left: FromPyObject<'p>;
|
||||
type Right: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberTruedivProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Left: FromPyObject<'p>;
|
||||
type Right: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberFloordivProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Left: FromPyObject<'p>;
|
||||
type Right: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberModProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Left: FromPyObject<'p>;
|
||||
type Right: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberDivmodProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Left: FromPyObject<'p>;
|
||||
type Right: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
|
@ -380,127 +380,127 @@ pub trait PyNumberPowProtocol<'p>: PyNumberProtocol<'p> {
|
|||
type Left: FromPyObject<'p>;
|
||||
type Right: FromPyObject<'p>;
|
||||
type Modulo: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberLShiftProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Left: FromPyObject<'p>;
|
||||
type Right: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberRShiftProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Left: FromPyObject<'p>;
|
||||
type Right: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberAndProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Left: FromPyObject<'p>;
|
||||
type Right: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberXorProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Left: FromPyObject<'p>;
|
||||
type Right: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberOrProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Left: FromPyObject<'p>;
|
||||
type Right: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberRAddProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Other: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberRSubProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Other: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberRMulProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Other: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberRMatmulProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Other: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberRTruedivProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Other: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberRFloordivProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Other: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberRModProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Other: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberRDivmodProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Other: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberRPowProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Other: FromPyObject<'p>;
|
||||
type Modulo: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberRLShiftProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Other: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberRRShiftProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Other: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberRAndProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Other: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberRXorProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Other: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberROrProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Other: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
|
@ -576,47 +576,47 @@ pub trait PyNumberIOrProtocol<'p>: PyNumberProtocol<'p> {
|
|||
}
|
||||
|
||||
pub trait PyNumberNegProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberPosProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberAbsProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberInvertProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberComplexProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberIntProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberFloatProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberRoundProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyNumberIndexProtocol<'p>: PyNumberProtocol<'p> {
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
|
@ -1271,7 +1271,8 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
trait PyNumberRAddProtocolImpl {
|
||||
#[doc(hidden)]
|
||||
pub trait PyNumberRAddProtocolImpl {
|
||||
fn __radd__() -> Option<PyMethodDef> {
|
||||
None
|
||||
}
|
||||
|
@ -1279,7 +1280,8 @@ trait PyNumberRAddProtocolImpl {
|
|||
|
||||
impl<'p, T> PyNumberRAddProtocolImpl for T where T: PyNumberProtocol<'p> {}
|
||||
|
||||
trait PyNumberRSubProtocolImpl {
|
||||
#[doc(hidden)]
|
||||
pub trait PyNumberRSubProtocolImpl {
|
||||
fn __rsub__() -> Option<PyMethodDef> {
|
||||
None
|
||||
}
|
||||
|
@ -1287,7 +1289,8 @@ trait PyNumberRSubProtocolImpl {
|
|||
|
||||
impl<'p, T> PyNumberRSubProtocolImpl for T where T: PyNumberProtocol<'p> {}
|
||||
|
||||
trait PyNumberRMulProtocolImpl {
|
||||
#[doc(hidden)]
|
||||
pub trait PyNumberRMulProtocolImpl {
|
||||
fn __rmul__() -> Option<PyMethodDef> {
|
||||
None
|
||||
}
|
||||
|
@ -1295,7 +1298,8 @@ trait PyNumberRMulProtocolImpl {
|
|||
|
||||
impl<'p, T> PyNumberRMulProtocolImpl for T where T: PyNumberProtocol<'p> {}
|
||||
|
||||
trait PyNumberRMatmulProtocolImpl {
|
||||
#[doc(hidden)]
|
||||
pub trait PyNumberRMatmulProtocolImpl {
|
||||
fn __rmatmul__() -> Option<PyMethodDef> {
|
||||
None
|
||||
}
|
||||
|
@ -1303,7 +1307,8 @@ trait PyNumberRMatmulProtocolImpl {
|
|||
|
||||
impl<'p, T> PyNumberRMatmulProtocolImpl for T where T: PyNumberProtocol<'p> {}
|
||||
|
||||
trait PyNumberRTruedivProtocolImpl {
|
||||
#[doc(hidden)]
|
||||
pub trait PyNumberRTruedivProtocolImpl {
|
||||
fn __rtruediv__() -> Option<PyMethodDef> {
|
||||
None
|
||||
}
|
||||
|
@ -1311,7 +1316,8 @@ trait PyNumberRTruedivProtocolImpl {
|
|||
|
||||
impl<'p, T> PyNumberRTruedivProtocolImpl for T where T: PyNumberProtocol<'p> {}
|
||||
|
||||
trait PyNumberRFloordivProtocolImpl {
|
||||
#[doc(hidden)]
|
||||
pub trait PyNumberRFloordivProtocolImpl {
|
||||
fn __rfloordiv__() -> Option<PyMethodDef> {
|
||||
None
|
||||
}
|
||||
|
@ -1319,7 +1325,8 @@ trait PyNumberRFloordivProtocolImpl {
|
|||
|
||||
impl<'p, T> PyNumberRFloordivProtocolImpl for T where T: PyNumberProtocol<'p> {}
|
||||
|
||||
trait PyNumberRModProtocolImpl {
|
||||
#[doc(hidden)]
|
||||
pub trait PyNumberRModProtocolImpl {
|
||||
fn __rmod__() -> Option<PyMethodDef> {
|
||||
None
|
||||
}
|
||||
|
@ -1327,7 +1334,8 @@ trait PyNumberRModProtocolImpl {
|
|||
|
||||
impl<'p, T> PyNumberRModProtocolImpl for T where T: PyNumberProtocol<'p> {}
|
||||
|
||||
trait PyNumberRDivmodProtocolImpl {
|
||||
#[doc(hidden)]
|
||||
pub trait PyNumberRDivmodProtocolImpl {
|
||||
fn __rdivmod__() -> Option<PyMethodDef> {
|
||||
None
|
||||
}
|
||||
|
@ -1335,7 +1343,8 @@ trait PyNumberRDivmodProtocolImpl {
|
|||
|
||||
impl<'p, T> PyNumberRDivmodProtocolImpl for T where T: PyNumberProtocol<'p> {}
|
||||
|
||||
trait PyNumberRPowProtocolImpl {
|
||||
#[doc(hidden)]
|
||||
pub trait PyNumberRPowProtocolImpl {
|
||||
fn __rpow__() -> Option<PyMethodDef> {
|
||||
None
|
||||
}
|
||||
|
@ -1343,7 +1352,8 @@ trait PyNumberRPowProtocolImpl {
|
|||
|
||||
impl<'p, T> PyNumberRPowProtocolImpl for T where T: PyNumberProtocol<'p> {}
|
||||
|
||||
trait PyNumberRLShiftProtocolImpl {
|
||||
#[doc(hidden)]
|
||||
pub trait PyNumberRLShiftProtocolImpl {
|
||||
fn __rlshift__() -> Option<PyMethodDef> {
|
||||
None
|
||||
}
|
||||
|
@ -1351,7 +1361,8 @@ trait PyNumberRLShiftProtocolImpl {
|
|||
|
||||
impl<'p, T> PyNumberRLShiftProtocolImpl for T where T: PyNumberProtocol<'p> {}
|
||||
|
||||
trait PyNumberRRShiftProtocolImpl {
|
||||
#[doc(hidden)]
|
||||
pub trait PyNumberRRShiftProtocolImpl {
|
||||
fn __rrshift__() -> Option<PyMethodDef> {
|
||||
None
|
||||
}
|
||||
|
@ -1359,7 +1370,8 @@ trait PyNumberRRShiftProtocolImpl {
|
|||
|
||||
impl<'p, T> PyNumberRRShiftProtocolImpl for T where T: PyNumberProtocol<'p> {}
|
||||
|
||||
trait PyNumberRAndProtocolImpl {
|
||||
#[doc(hidden)]
|
||||
pub trait PyNumberRAndProtocolImpl {
|
||||
fn __rand__() -> Option<PyMethodDef> {
|
||||
None
|
||||
}
|
||||
|
@ -1367,7 +1379,8 @@ trait PyNumberRAndProtocolImpl {
|
|||
|
||||
impl<'p, T> PyNumberRAndProtocolImpl for T where T: PyNumberProtocol<'p> {}
|
||||
|
||||
trait PyNumberRXorProtocolImpl {
|
||||
#[doc(hidden)]
|
||||
pub trait PyNumberRXorProtocolImpl {
|
||||
fn __rxor__() -> Option<PyMethodDef> {
|
||||
None
|
||||
}
|
||||
|
@ -1375,7 +1388,8 @@ trait PyNumberRXorProtocolImpl {
|
|||
|
||||
impl<'p, T> PyNumberRXorProtocolImpl for T where T: PyNumberProtocol<'p> {}
|
||||
|
||||
trait PyNumberROrProtocolImpl {
|
||||
#[doc(hidden)]
|
||||
pub trait PyNumberROrProtocolImpl {
|
||||
fn __ror__() -> Option<PyMethodDef> {
|
||||
None
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ use crate::class::methods::PyMethodDef;
|
|||
use crate::err::PyResult;
|
||||
use crate::ffi;
|
||||
use crate::type_object::PyTypeInfo;
|
||||
use crate::PyObject;
|
||||
|
||||
/// Python Async/Await support interface.
|
||||
///
|
||||
|
@ -61,22 +62,22 @@ pub trait PyAsyncProtocol<'p>: PyTypeInfo {
|
|||
}
|
||||
|
||||
pub trait PyAsyncAwaitProtocol<'p>: PyAsyncProtocol<'p> {
|
||||
type Success: crate::IntoPyObject;
|
||||
type Success: crate::IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyAsyncAiterProtocol<'p>: PyAsyncProtocol<'p> {
|
||||
type Success: crate::IntoPyObject;
|
||||
type Success: crate::IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PyAsyncAnextProtocol<'p>: PyAsyncProtocol<'p> {
|
||||
type Success: crate::IntoPyObject;
|
||||
type Success: crate::IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Option<Self::Success>>>;
|
||||
}
|
||||
|
||||
pub trait PyAsyncAenterProtocol<'p>: PyAsyncProtocol<'p> {
|
||||
type Success: crate::IntoPyObject;
|
||||
type Success: crate::IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
|
@ -84,7 +85,7 @@ pub trait PyAsyncAexitProtocol<'p>: PyAsyncProtocol<'p> {
|
|||
type ExcType: crate::FromPyObject<'p>;
|
||||
type ExcValue: crate::FromPyObject<'p>;
|
||||
type Traceback: crate::FromPyObject<'p>;
|
||||
type Success: crate::IntoPyObject;
|
||||
type Success: crate::IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
|
@ -186,23 +187,22 @@ impl<'p, T> PyAsyncAnextProtocolImpl for T where T: PyAsyncProtocol<'p> {}
|
|||
mod anext {
|
||||
use super::{PyAsyncAnextProtocol, PyAsyncAnextProtocolImpl};
|
||||
use crate::callback::CallbackConverter;
|
||||
use crate::ffi;
|
||||
use crate::IntoPyObject;
|
||||
use crate::IntoPyPointer;
|
||||
use crate::Python;
|
||||
use crate::{ffi, IntoPy, PyObject};
|
||||
use std::ptr;
|
||||
|
||||
pub struct IterANextResultConverter;
|
||||
|
||||
impl<T> CallbackConverter<Option<T>> for IterANextResultConverter
|
||||
where
|
||||
T: IntoPyObject,
|
||||
T: IntoPy<PyObject>,
|
||||
{
|
||||
type R = *mut ffi::PyObject;
|
||||
|
||||
fn convert(val: Option<T>, py: Python) -> *mut ffi::PyObject {
|
||||
match val {
|
||||
Some(val) => val.into_object(py).into_ptr(),
|
||||
Some(val) => val.into_py(py).into_ptr(),
|
||||
None => unsafe {
|
||||
ffi::PyErr_SetNone(ffi::PyExc_StopAsyncIteration);
|
||||
ptr::null_mut()
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
|
||||
use crate::callback::{BoolCallbackConverter, LenResultConverter, PyObjectCallbackConverter};
|
||||
use crate::err::{PyErr, PyResult};
|
||||
use crate::exceptions;
|
||||
use crate::ffi;
|
||||
use crate::objectprotocol::ObjectProtocol;
|
||||
use crate::type_object::PyTypeInfo;
|
||||
use crate::types::PyAny;
|
||||
use crate::FromPyObject;
|
||||
use crate::Python;
|
||||
use crate::{FromPyObject, IntoPyObject};
|
||||
use crate::{exceptions, IntoPy, PyObject};
|
||||
use std::os::raw::c_int;
|
||||
|
||||
/// Sequence interface
|
||||
|
@ -90,7 +90,7 @@ pub trait PySequenceLenProtocol<'p>: PySequenceProtocol<'p> {
|
|||
|
||||
pub trait PySequenceGetItemProtocol<'p>: PySequenceProtocol<'p> {
|
||||
type Index: FromPyObject<'p> + From<isize>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
|
@ -112,22 +112,22 @@ pub trait PySequenceContainsProtocol<'p>: PySequenceProtocol<'p> {
|
|||
|
||||
pub trait PySequenceConcatProtocol<'p>: PySequenceProtocol<'p> {
|
||||
type Other: FromPyObject<'p>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PySequenceRepeatProtocol<'p>: PySequenceProtocol<'p> {
|
||||
type Index: FromPyObject<'p> + From<isize>;
|
||||
type Success: IntoPyObject;
|
||||
type Success: IntoPy<PyObject>;
|
||||
type Result: Into<PyResult<Self::Success>>;
|
||||
}
|
||||
|
||||
pub trait PySequenceInplaceConcatProtocol<'p>: PySequenceProtocol<'p> + IntoPyObject {
|
||||
pub trait PySequenceInplaceConcatProtocol<'p>: PySequenceProtocol<'p> + IntoPy<PyObject> {
|
||||
type Other: FromPyObject<'p>;
|
||||
type Result: Into<PyResult<Self>>;
|
||||
}
|
||||
|
||||
pub trait PySequenceInplaceRepeatProtocol<'p>: PySequenceProtocol<'p> + IntoPyObject {
|
||||
pub trait PySequenceInplaceRepeatProtocol<'p>: PySequenceProtocol<'p> + IntoPy<PyObject> {
|
||||
type Index: FromPyObject<'p> + From<isize>;
|
||||
type Result: Into<PyResult<Self>>;
|
||||
}
|
||||
|
|
|
@ -139,7 +139,7 @@ impl<T, U> IntoPy<U> for T
|
|||
where
|
||||
U: FromPy<T>,
|
||||
{
|
||||
fn into_py(self, py: Python) -> U {
|
||||
default fn into_py(self, py: Python) -> U {
|
||||
U::from_py(self, py)
|
||||
}
|
||||
}
|
||||
|
@ -151,13 +151,6 @@ impl<T> FromPy<T> for T {
|
|||
}
|
||||
}
|
||||
|
||||
/// Conversion trait that allows various objects to be converted into `PyObject`
|
||||
/// by consuming original object.
|
||||
pub trait IntoPyObject {
|
||||
/// Converts self into a Python object. (Consumes self)
|
||||
fn into_object(self, py: Python) -> PyObject;
|
||||
}
|
||||
|
||||
/// `FromPyObject` is implemented by various types that can be extracted from
|
||||
/// a Python object reference.
|
||||
///
|
||||
|
@ -209,13 +202,13 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> IntoPyObject for Option<T>
|
||||
impl<T> IntoPy<PyObject> for Option<T>
|
||||
where
|
||||
T: IntoPyObject,
|
||||
T: IntoPy<PyObject>,
|
||||
{
|
||||
fn into_object(self, py: Python) -> PyObject {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
match self {
|
||||
Some(val) => val.into_object(py),
|
||||
Some(val) => val.into_py(py),
|
||||
None => py.None(),
|
||||
}
|
||||
}
|
||||
|
@ -228,29 +221,19 @@ impl ToPyObject for () {
|
|||
}
|
||||
}
|
||||
|
||||
impl IntoPyObject for () {
|
||||
fn into_object(self, py: Python) -> PyObject {
|
||||
impl FromPy<()> for PyObject {
|
||||
fn from_py(_: (), py: Python) -> Self {
|
||||
py.None()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> IntoPyObject for &'a T
|
||||
impl<'a, T> FromPy<&'a T> for PyObject
|
||||
where
|
||||
T: AsPyPointer,
|
||||
{
|
||||
#[inline]
|
||||
fn into_object(self, py: Python) -> PyObject {
|
||||
unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> IntoPyObject for &'a mut T
|
||||
where
|
||||
T: AsPyPointer,
|
||||
{
|
||||
#[inline]
|
||||
fn into_object(self, py: Python) -> PyObject {
|
||||
unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) }
|
||||
fn from_py(other: &'a T, py: Python) -> PyObject {
|
||||
unsafe { PyObject::from_borrowed_ptr(py, other.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,12 +6,11 @@
|
|||
|
||||
use crate::err::PyResult;
|
||||
use crate::exceptions::TypeError;
|
||||
use crate::ffi;
|
||||
use crate::init_once;
|
||||
use crate::types::{PyAny, PyDict, PyModule, PyTuple};
|
||||
use crate::GILPool;
|
||||
use crate::IntoPyObject;
|
||||
use crate::Python;
|
||||
use crate::{ffi, IntoPy, PyObject};
|
||||
use std::ptr;
|
||||
|
||||
/// Description of a python parameter; used for `parse_args()`.
|
||||
|
@ -154,7 +153,7 @@ pub unsafe fn make_module(
|
|||
}
|
||||
}
|
||||
|
||||
/// This trait wraps a T: IntoPyObject into PyResult<T> while PyResult<T> remains PyResult<T>.
|
||||
/// This trait wraps a T: IntoPy<PyObject> into PyResult<T> while PyResult<T> remains PyResult<T>.
|
||||
///
|
||||
/// This is necessary because proc macros run before typechecking and can't decide
|
||||
/// whether a return type is a (possibly aliased) PyResult or not. It is also quite handy because
|
||||
|
@ -163,13 +162,13 @@ pub trait IntoPyResult<T> {
|
|||
fn into_py_result(self) -> PyResult<T>;
|
||||
}
|
||||
|
||||
impl<T: IntoPyObject> IntoPyResult<T> for T {
|
||||
impl<T: IntoPy<PyObject>> IntoPyResult<T> for T {
|
||||
fn into_py_result(self) -> PyResult<T> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IntoPyObject> IntoPyResult<T> for PyResult<T> {
|
||||
impl<T: IntoPy<PyObject>> IntoPyResult<T> for PyResult<T> {
|
||||
fn into_py_result(self) -> PyResult<T> {
|
||||
self
|
||||
}
|
||||
|
|
24
src/err.rs
24
src/err.rs
|
@ -1,7 +1,5 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
use crate::exceptions;
|
||||
use crate::ffi;
|
||||
use crate::instance::Py;
|
||||
use crate::object::PyObject;
|
||||
use crate::type_object::PyTypeObject;
|
||||
|
@ -9,9 +7,10 @@ use crate::types::{PyAny, PyType};
|
|||
use crate::AsPyPointer;
|
||||
use crate::IntoPyPointer;
|
||||
use crate::Python;
|
||||
use crate::{IntoPyObject, ToBorrowedObject, ToPyObject};
|
||||
use crate::{exceptions, IntoPy};
|
||||
use crate::{ffi, FromPy};
|
||||
use crate::{ToBorrowedObject, ToPyObject};
|
||||
use libc::c_int;
|
||||
use std::error::Error;
|
||||
use std::ffi::CString;
|
||||
use std::io;
|
||||
use std::os::raw::c_char;
|
||||
|
@ -358,9 +357,9 @@ impl std::fmt::Debug for PyErr {
|
|||
}
|
||||
}
|
||||
|
||||
impl IntoPyObject for PyErr {
|
||||
fn into_object(self, py: Python) -> PyObject {
|
||||
self.instance(py)
|
||||
impl FromPy<PyErr> for PyObject {
|
||||
fn from_py(other: PyErr, py: Python) -> Self {
|
||||
other.instance(py)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -371,8 +370,8 @@ impl ToPyObject for PyErr {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoPyObject for &'a PyErr {
|
||||
fn into_object(self, py: Python) -> PyObject {
|
||||
impl<'a> IntoPy<PyObject> for &'a PyErr {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
let err = self.clone_ref(py);
|
||||
err.instance(py)
|
||||
}
|
||||
|
@ -412,7 +411,7 @@ macro_rules! impl_to_pyerr {
|
|||
($err: ty, $pyexc: ty) => {
|
||||
impl PyErrArguments for $err {
|
||||
fn arguments(&self, py: Python) -> PyObject {
|
||||
self.description().to_object(py)
|
||||
self.to_string().to_object(py)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -459,10 +458,9 @@ impl std::convert::From<io::Error> for PyErr {
|
|||
}
|
||||
}
|
||||
|
||||
/// Extract `errno` and `errdesc` from from `io::Error`
|
||||
impl PyErrArguments for io::Error {
|
||||
fn arguments(&self, py: Python) -> PyObject {
|
||||
(self.raw_os_error().unwrap_or(0), self.description()).to_object(py)
|
||||
self.to_string().to_object(py)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -474,7 +472,7 @@ impl<W: 'static + Send + std::fmt::Debug> std::convert::From<std::io::IntoInnerE
|
|||
|
||||
impl<W: Send + std::fmt::Debug> PyErrArguments for std::io::IntoInnerError<W> {
|
||||
fn arguments(&self, py: Python) -> PyObject {
|
||||
self.description().to_object(py)
|
||||
self.to_string().to_object(py)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -299,7 +299,7 @@ mod typeobject {
|
|||
impl Default for PyNumberMethods {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
unsafe { mem::zeroed() }
|
||||
PyNumberMethods_INIT
|
||||
}
|
||||
}
|
||||
macro_rules! as_expr {
|
||||
|
|
20
src/gil.rs
20
src/gil.rs
|
@ -9,8 +9,8 @@ use spin;
|
|||
use std::ptr::NonNull;
|
||||
use std::{any, marker, rc, sync};
|
||||
|
||||
static START: sync::Once = sync::ONCE_INIT;
|
||||
static START_PYO3: sync::Once = sync::ONCE_INIT;
|
||||
static START: sync::Once = sync::Once::new();
|
||||
static START_PYO3: sync::Once = sync::Once::new();
|
||||
|
||||
/// Prepares the use of Python in a free-threaded context.
|
||||
///
|
||||
|
@ -280,18 +280,16 @@ use self::array_list::ArrayList;
|
|||
|
||||
mod array_list {
|
||||
use std::collections::LinkedList;
|
||||
use std::mem;
|
||||
|
||||
const BLOCK_SIZE: usize = 256;
|
||||
|
||||
/// A container type for Release Pool
|
||||
/// See #271 for why this is crated
|
||||
pub(super) struct ArrayList<T> {
|
||||
inner: LinkedList<[T; BLOCK_SIZE]>,
|
||||
inner: LinkedList<[Option<T>; BLOCK_SIZE]>,
|
||||
length: usize,
|
||||
}
|
||||
|
||||
impl<T: Clone> ArrayList<T> {
|
||||
impl<T: Copy> ArrayList<T> {
|
||||
pub fn new() -> Self {
|
||||
ArrayList {
|
||||
inner: LinkedList::new(),
|
||||
|
@ -301,20 +299,20 @@ mod array_list {
|
|||
pub fn push_back(&mut self, item: T) -> &T {
|
||||
let next_idx = self.next_idx();
|
||||
if next_idx == 0 {
|
||||
self.inner.push_back(unsafe { mem::uninitialized() });
|
||||
self.inner.push_back([None; BLOCK_SIZE]);
|
||||
}
|
||||
self.inner.back_mut().unwrap()[next_idx] = item;
|
||||
self.inner.back_mut().unwrap()[next_idx] = Some(item);
|
||||
self.length += 1;
|
||||
&self.inner.back().unwrap()[next_idx]
|
||||
self.inner.back().unwrap()[next_idx].as_ref().unwrap()
|
||||
}
|
||||
pub fn pop_back(&mut self) -> Option<T> {
|
||||
self.length -= 1;
|
||||
let current_idx = self.next_idx();
|
||||
if current_idx == 0 {
|
||||
let last_list = self.inner.pop_back()?;
|
||||
return Some(last_list[0].clone());
|
||||
return last_list[0];
|
||||
}
|
||||
self.inner.back().map(|arr| arr[current_idx].clone())
|
||||
self.inner.back().and_then(|arr| arr[current_idx])
|
||||
}
|
||||
pub fn len(&self) -> usize {
|
||||
self.length
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
use crate::err::{PyErr, PyResult};
|
||||
use crate::ffi;
|
||||
use crate::gil;
|
||||
use crate::instance;
|
||||
use crate::object::PyObject;
|
||||
|
@ -8,9 +7,8 @@ use crate::objectprotocol::ObjectProtocol;
|
|||
use crate::type_object::PyTypeCreate;
|
||||
use crate::type_object::{PyTypeInfo, PyTypeObject};
|
||||
use crate::types::PyAny;
|
||||
use crate::{
|
||||
AsPyPointer, FromPyObject, FromPyPointer, IntoPyObject, IntoPyPointer, Python, ToPyObject,
|
||||
};
|
||||
use crate::{ffi, IntoPy};
|
||||
use crate::{AsPyPointer, FromPyObject, FromPyPointer, IntoPyPointer, Python, ToPyObject};
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
@ -96,6 +94,12 @@ impl<'a, T: PyTypeInfo> ToPyObject for PyRef<'a, T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, T: PyTypeInfo> IntoPy<PyObject> for PyRef<'a, T> {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
self.to_object(py)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: PyTypeInfo> Deref for PyRef<'a, T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &T {
|
||||
|
@ -170,6 +174,12 @@ impl<'a, T: PyTypeInfo> ToPyObject for PyRefMut<'a, T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, T: PyTypeInfo> IntoPy<PyObject> for PyRefMut<'a, T> {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
self.to_object(py)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: PyTypeInfo> Deref for PyRefMut<'a, T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &T {
|
||||
|
@ -395,11 +405,11 @@ impl<T> ToPyObject for Py<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> IntoPyObject for Py<T> {
|
||||
impl<T> IntoPy<PyObject> for Py<T> {
|
||||
/// Converts `Py` instance -> PyObject.
|
||||
/// Consumes `self` without calling `Py_DECREF()`
|
||||
#[inline]
|
||||
fn into_object(self, py: Python) -> PyObject {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
unsafe { PyObject::from_owned_ptr(py, self.into_ptr()) }
|
||||
}
|
||||
}
|
||||
|
|
30
src/lib.rs
Normal file → Executable file
30
src/lib.rs
Normal file → Executable file
|
@ -119,8 +119,8 @@
|
|||
|
||||
pub use crate::class::*;
|
||||
pub use crate::conversion::{
|
||||
AsPyPointer, FromPy, FromPyObject, FromPyPointer, IntoPy, IntoPyObject, IntoPyPointer,
|
||||
PyTryFrom, PyTryInto, ToBorrowedObject, ToPyObject,
|
||||
AsPyPointer, FromPy, FromPyObject, FromPyPointer, IntoPy, IntoPyPointer, PyTryFrom, PyTryInto,
|
||||
ToBorrowedObject, ToPyObject,
|
||||
};
|
||||
pub use crate::err::{PyDowncastError, PyErr, PyErrArguments, PyErrValue, PyResult};
|
||||
pub use crate::gil::{init_once, GILGuard, GILPool};
|
||||
|
@ -132,7 +132,7 @@ pub use crate::type_object::{PyObjectAlloc, PyRawObject, PyTypeInfo};
|
|||
|
||||
// Re-exported for wrap_function
|
||||
#[doc(hidden)]
|
||||
pub use mashup;
|
||||
pub use paste;
|
||||
// Re-exported for py_run
|
||||
#[doc(hidden)]
|
||||
pub use indoc;
|
||||
|
@ -183,18 +183,8 @@ pub mod proc_macro {
|
|||
/// Use this together with `#[pyfunction]` and [types::PyModule::add_wrapped].
|
||||
#[macro_export]
|
||||
macro_rules! wrap_pyfunction {
|
||||
($function_name:ident) => {{
|
||||
// Get the mashup macro and its helpers into scope
|
||||
use pyo3::mashup::*;
|
||||
|
||||
mashup! {
|
||||
// Make sure this ident matches the one in function_wrapper_ident
|
||||
m["method"] = __pyo3_get_function_ $function_name;
|
||||
}
|
||||
|
||||
m! {
|
||||
&"method"
|
||||
}
|
||||
($function_name: ident) => {{
|
||||
&pyo3::paste::expr! { [<__pyo3_get_function_ $function_name>] }
|
||||
}};
|
||||
}
|
||||
|
||||
|
@ -204,14 +194,8 @@ macro_rules! wrap_pyfunction {
|
|||
#[macro_export]
|
||||
macro_rules! wrap_pymodule {
|
||||
($module_name:ident) => {{
|
||||
use pyo3::mashup::*;
|
||||
|
||||
mashup! {
|
||||
m["method"] = PyInit_ $module_name;
|
||||
}
|
||||
|
||||
m! {
|
||||
&|py| unsafe { pyo3::PyObject::from_owned_ptr(py, "method"()) }
|
||||
pyo3::paste::expr! {
|
||||
&|py| unsafe { pyo3::PyObject::from_owned_ptr(py, [<PyInit_ $module_name>]()) }
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
|
|
@ -8,9 +8,7 @@ use crate::types::{PyAny, PyDict, PyTuple};
|
|||
use crate::AsPyPointer;
|
||||
use crate::Py;
|
||||
use crate::Python;
|
||||
use crate::{
|
||||
FromPyObject, IntoPy, IntoPyObject, IntoPyPointer, PyTryFrom, ToBorrowedObject, ToPyObject,
|
||||
};
|
||||
use crate::{FromPyObject, IntoPy, IntoPyPointer, PyTryFrom, ToBorrowedObject, ToPyObject};
|
||||
use std::ptr::NonNull;
|
||||
|
||||
/// A python object
|
||||
|
@ -299,13 +297,6 @@ impl PartialEq for PyObject {
|
|||
}
|
||||
}
|
||||
|
||||
impl IntoPyObject for PyObject {
|
||||
#[inline]
|
||||
fn into_object(self, _py: Python) -> PyObject {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromPyObject<'a> for PyObject {
|
||||
#[inline]
|
||||
/// Extracts `Self` from the source `PyObject`.
|
||||
|
|
|
@ -16,9 +16,7 @@ pub use crate::instance::{AsPyRef, Py, PyRef, PyRefMut};
|
|||
pub use crate::object::PyObject;
|
||||
pub use crate::objectprotocol::ObjectProtocol;
|
||||
pub use crate::python::Python;
|
||||
pub use crate::{
|
||||
FromPy, FromPyObject, IntoPy, IntoPyObject, IntoPyPointer, PyTryFrom, PyTryInto, ToPyObject,
|
||||
};
|
||||
pub use crate::{FromPy, FromPyObject, IntoPy, IntoPyPointer, PyTryFrom, PyTryInto, ToPyObject};
|
||||
// This is only part of the prelude because we need it for the pymodule function
|
||||
pub use crate::types::PyModule;
|
||||
// This is required for the constructor
|
||||
|
|
|
@ -234,6 +234,7 @@ impl<'p> Python<'p> {
|
|||
}
|
||||
|
||||
/// Register `ffi::PyObject` pointer in release pool
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
pub unsafe fn from_borrowed_ptr_to_obj(self, ptr: *mut ffi::PyObject) -> &'p PyAny {
|
||||
match NonNull::new(ptr) {
|
||||
Some(p) => gil::register_borrowed(self, p),
|
||||
|
@ -243,6 +244,7 @@ impl<'p> Python<'p> {
|
|||
|
||||
/// Register `ffi::PyObject` pointer in release pool,
|
||||
/// and do unchecked downcast to specific type.
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
pub unsafe fn from_owned_ptr<T>(self, ptr: *mut ffi::PyObject) -> &'p T
|
||||
where
|
||||
T: PyTypeInfo,
|
||||
|
@ -262,6 +264,7 @@ impl<'p> Python<'p> {
|
|||
/// Register owned `ffi::PyObject` pointer in release pool.
|
||||
/// Returns `Err(PyErr)` if the pointer is `null`.
|
||||
/// do unchecked downcast to specific type.
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
pub unsafe fn from_owned_ptr_or_err<T>(self, ptr: *mut ffi::PyObject) -> PyResult<&'p T>
|
||||
where
|
||||
T: PyTypeInfo,
|
||||
|
@ -272,6 +275,7 @@ impl<'p> Python<'p> {
|
|||
/// Register owned `ffi::PyObject` pointer in release pool.
|
||||
/// Returns `None` if the pointer is `null`.
|
||||
/// do unchecked downcast to specific type.
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
pub unsafe fn from_owned_ptr_or_opt<T>(self, ptr: *mut ffi::PyObject) -> Option<&'p T>
|
||||
where
|
||||
T: PyTypeInfo,
|
||||
|
@ -282,6 +286,7 @@ impl<'p> Python<'p> {
|
|||
/// Register borrowed `ffi::PyObject` pointer in release pool.
|
||||
/// Panics if the pointer is `null`.
|
||||
/// do unchecked downcast to specific type.
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
pub unsafe fn from_borrowed_ptr<T>(self, ptr: *mut ffi::PyObject) -> &'p T
|
||||
where
|
||||
T: PyTypeInfo,
|
||||
|
@ -302,6 +307,7 @@ impl<'p> Python<'p> {
|
|||
/// Register borrowed `ffi::PyObject` pointer in release pool.
|
||||
/// Returns `Err(PyErr)` if the pointer is `null`.
|
||||
/// do unchecked downcast to specific type.
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
pub unsafe fn from_borrowed_ptr_or_err<T>(self, ptr: *mut ffi::PyObject) -> PyResult<&'p T>
|
||||
where
|
||||
T: PyTypeInfo,
|
||||
|
@ -312,6 +318,7 @@ impl<'p> Python<'p> {
|
|||
/// Register borrowed `ffi::PyObject` pointer in release pool.
|
||||
/// Returns `None` if the pointer is `null`.
|
||||
/// do unchecked downcast to specific `T`.
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
pub unsafe fn from_borrowed_ptr_or_opt<T>(self, ptr: *mut ffi::PyObject) -> Option<&'p T>
|
||||
where
|
||||
T: PyTypeInfo,
|
||||
|
|
|
@ -352,36 +352,24 @@ where
|
|||
// basic methods
|
||||
<T as class::basic::PyObjectProtocolImpl>::tp_as_object(type_object);
|
||||
|
||||
fn to_ptr<T>(value: Option<T>) -> *mut T {
|
||||
value
|
||||
.map(|v| Box::into_raw(Box::new(v)))
|
||||
.unwrap_or_else(ptr::null_mut)
|
||||
}
|
||||
|
||||
// number methods
|
||||
if let Some(meth) = <T as class::number::PyNumberProtocolImpl>::tp_as_number() {
|
||||
type_object.tp_as_number = Box::into_raw(Box::new(meth));
|
||||
} else {
|
||||
type_object.tp_as_number = ::std::ptr::null_mut()
|
||||
}
|
||||
|
||||
type_object.tp_as_number = to_ptr(<T as class::number::PyNumberProtocolImpl>::tp_as_number());
|
||||
// mapping methods
|
||||
if let Some(meth) = <T as class::mapping::PyMappingProtocolImpl>::tp_as_mapping() {
|
||||
type_object.tp_as_mapping = Box::into_raw(Box::new(meth));
|
||||
} else {
|
||||
type_object.tp_as_mapping = ::std::ptr::null_mut()
|
||||
}
|
||||
|
||||
type_object.tp_as_mapping =
|
||||
to_ptr(<T as class::mapping::PyMappingProtocolImpl>::tp_as_mapping());
|
||||
// sequence methods
|
||||
if let Some(meth) = <T as class::sequence::PySequenceProtocolImpl>::tp_as_sequence() {
|
||||
type_object.tp_as_sequence = Box::into_raw(Box::new(meth));
|
||||
} else {
|
||||
type_object.tp_as_sequence = ::std::ptr::null_mut()
|
||||
}
|
||||
|
||||
type_object.tp_as_sequence =
|
||||
to_ptr(<T as class::sequence::PySequenceProtocolImpl>::tp_as_sequence());
|
||||
// async methods
|
||||
async_methods::<T>(type_object);
|
||||
|
||||
type_object.tp_as_async = to_ptr(<T as class::pyasync::PyAsyncProtocolImpl>::tp_as_async());
|
||||
// buffer protocol
|
||||
if let Some(meth) = <T as class::buffer::PyBufferProtocolImpl>::tp_as_buffer() {
|
||||
type_object.tp_as_buffer = Box::into_raw(Box::new(meth));
|
||||
} else {
|
||||
type_object.tp_as_buffer = ::std::ptr::null_mut()
|
||||
}
|
||||
type_object.tp_as_buffer = to_ptr(<T as class::buffer::PyBufferProtocolImpl>::tp_as_buffer());
|
||||
|
||||
// normal methods
|
||||
let (new, init, call, mut methods) = py_class_method_defs::<T>();
|
||||
|
@ -428,14 +416,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn async_methods<T>(type_info: &mut ffi::PyTypeObject) {
|
||||
if let Some(meth) = <T as class::pyasync::PyAsyncProtocolImpl>::tp_as_async() {
|
||||
type_info.tp_as_async = Box::into_raw(Box::new(meth));
|
||||
} else {
|
||||
type_info.tp_as_async = ::std::ptr::null_mut()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn tp_dealloc_callback<T>(obj: *mut ffi::PyObject)
|
||||
where
|
||||
T: PyObjectAlloc,
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
use crate::ffi;
|
||||
use crate::object::PyObject;
|
||||
use crate::types::PyAny;
|
||||
use crate::AsPyPointer;
|
||||
use crate::FromPyObject;
|
||||
use crate::PyResult;
|
||||
use crate::Python;
|
||||
use crate::{IntoPyObject, PyTryFrom, ToPyObject};
|
||||
use crate::{AsPyPointer, FromPy};
|
||||
use crate::{PyTryFrom, ToPyObject};
|
||||
|
||||
/// Represents a Python `bool`.
|
||||
#[repr(transparent)]
|
||||
|
@ -45,10 +45,10 @@ impl ToPyObject for bool {
|
|||
}
|
||||
}
|
||||
|
||||
impl IntoPyObject for bool {
|
||||
impl FromPy<bool> for PyObject {
|
||||
#[inline]
|
||||
fn into_object(self, py: Python) -> PyObject {
|
||||
PyBool::new(py, self).into()
|
||||
fn from_py(other: bool, py: Python) -> Self {
|
||||
PyBool::new(py, other).into()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,8 +18,6 @@ pyobject_native_type!(PyByteArray, ffi::PyByteArray_Type, ffi::PyByteArray_Check
|
|||
impl PyByteArray {
|
||||
/// Creates a new Python bytearray object.
|
||||
/// The byte string is initialized by copying the data from the `&[u8]`.
|
||||
///
|
||||
/// Panics if out of memory.
|
||||
pub fn new<'p>(py: Python<'p>, src: &[u8]) -> &'p PyByteArray {
|
||||
let ptr = src.as_ptr() as *const c_char;
|
||||
let len = src.len() as ffi::Py_ssize_t;
|
||||
|
@ -47,16 +45,37 @@ impl PyByteArray {
|
|||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Gets the Python bytearray data as byte slice.
|
||||
pub fn data(&self) -> &[u8] {
|
||||
unsafe {
|
||||
/// Copies the contents of the bytearray to a rust vector
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use pyo3::prelude::*;
|
||||
/// # use pyo3::types::PyByteArray;
|
||||
/// # use pyo3::types::IntoPyDict;
|
||||
/// # let gil = GILGuard::acquire();
|
||||
/// # let py = gil.python();
|
||||
/// #
|
||||
/// let bytearray = PyByteArray::new(py, b"Hello World.");
|
||||
/// let mut copied_message = bytearray.to_vec();
|
||||
/// assert_eq!(b"Hello World.", copied_message.as_slice());
|
||||
///
|
||||
/// copied_message[11] = b'!';
|
||||
/// assert_eq!(b"Hello World!", copied_message.as_slice());
|
||||
///
|
||||
/// let locals = [("bytearray", bytearray)].into_py_dict(py);
|
||||
/// py.run("assert bytearray == b'Hello World.'", None, Some(locals)).unwrap();
|
||||
/// ```
|
||||
pub fn to_vec(&self) -> Vec<u8> {
|
||||
let slice = unsafe {
|
||||
let buffer = ffi::PyByteArray_AsString(self.0.as_ptr()) as *mut u8;
|
||||
let length = ffi::PyByteArray_Size(self.0.as_ptr()) as usize;
|
||||
slice::from_raw_parts_mut(buffer, length)
|
||||
}
|
||||
};
|
||||
slice.to_vec()
|
||||
}
|
||||
|
||||
/// Resize bytearray object.
|
||||
/// Resize bytearray object to `len`.
|
||||
pub fn resize(&self, len: usize) -> PyResult<()> {
|
||||
unsafe {
|
||||
let result = ffi::PyByteArray_Resize(self.0.as_ptr(), len as ffi::Py_ssize_t);
|
||||
|
@ -84,13 +103,13 @@ mod test {
|
|||
let src = b"Hello Python";
|
||||
let bytearray = PyByteArray::new(py, src);
|
||||
assert_eq!(src.len(), bytearray.len());
|
||||
assert_eq!(src, bytearray.data());
|
||||
assert_eq!(src, bytearray.to_vec().as_slice());
|
||||
|
||||
let ba: PyObject = bytearray.into();
|
||||
let bytearray = PyByteArray::from(py, &ba).unwrap();
|
||||
|
||||
assert_eq!(src.len(), bytearray.len());
|
||||
assert_eq!(src, bytearray.data());
|
||||
assert_eq!(src, bytearray.to_vec().as_slice());
|
||||
|
||||
bytearray.resize(20).unwrap();
|
||||
assert_eq!(20, bytearray.len());
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::ffi;
|
||||
#[cfg(not(PyPy))]
|
||||
use crate::instance::PyNativeType;
|
||||
use crate::object::PyObject;
|
||||
use crate::AsPyPointer;
|
||||
use crate::PyObject;
|
||||
use crate::Python;
|
||||
#[cfg(not(PyPy))]
|
||||
use std::ops::*;
|
||||
|
@ -130,7 +130,7 @@ mod complex_conversion {
|
|||
use crate::err::PyErr;
|
||||
use crate::types::PyAny;
|
||||
use crate::PyResult;
|
||||
use crate::{FromPyObject, IntoPyObject, ToPyObject};
|
||||
use crate::{FromPyObject, ToPyObject};
|
||||
use num_complex::Complex;
|
||||
|
||||
impl PyComplex {
|
||||
|
@ -150,11 +150,11 @@ mod complex_conversion {
|
|||
impl ToPyObject for Complex<$float> {
|
||||
#[inline]
|
||||
fn to_object(&self, py: Python) -> PyObject {
|
||||
IntoPyObject::into_object(self.to_owned(), py)
|
||||
crate::IntoPy::<PyObject>::into_py(self.to_owned(), py)
|
||||
}
|
||||
}
|
||||
impl IntoPyObject for Complex<$float> {
|
||||
fn into_object(self, py: Python) -> PyObject {
|
||||
impl crate::IntoPy<PyObject> for Complex<$float> {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
unsafe {
|
||||
let raw_obj =
|
||||
ffi::PyComplex_FromDoubles(self.re as c_double, self.im as c_double);
|
||||
|
@ -196,6 +196,7 @@ mod complex_conversion {
|
|||
complex_conversion!(f32);
|
||||
complex_conversion!(f64);
|
||||
|
||||
#[allow(clippy::float_cmp)] // The test wants to ensure that no precision was lost on the Python round-trip
|
||||
#[test]
|
||||
fn from_complex() {
|
||||
let gil = Python::acquire_gil();
|
||||
|
@ -230,11 +231,13 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_from_double() {
|
||||
use assert_approx_eq::assert_approx_eq;
|
||||
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let complex = PyComplex::from_doubles(py, 3.0, 1.2);
|
||||
assert_eq!(complex.real(), 3.0);
|
||||
assert_eq!(complex.imag(), 1.2);
|
||||
assert_approx_eq!(complex.real(), 3.0);
|
||||
assert_approx_eq!(complex.imag(), 1.2);
|
||||
}
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
|
@ -281,8 +284,8 @@ mod test {
|
|||
let l = PyComplex::from_doubles(py, 3.0, 1.2);
|
||||
let r = PyComplex::from_doubles(py, 1.0, 2.6);
|
||||
let res = l / r;
|
||||
assert_approx_eq!(res.real(), 0.7886597938144329);
|
||||
assert_approx_eq!(res.imag(), -0.8505154639175257);
|
||||
assert_approx_eq!(res.real(), 0.788_659_793_814_432_9);
|
||||
assert_approx_eq!(res.imag(), -0.850_515_463_917_525_7);
|
||||
}
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
|
@ -302,7 +305,7 @@ mod test {
|
|||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let val = PyComplex::from_doubles(py, 3.0, 1.2);
|
||||
assert_approx_eq!(val.abs(), 3.2310988842807022);
|
||||
assert_approx_eq!(val.abs(), 3.231_098_884_280_702_2);
|
||||
}
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
|
@ -313,7 +316,7 @@ mod test {
|
|||
let l = PyComplex::from_doubles(py, 3.0, 1.2);
|
||||
let r = PyComplex::from_doubles(py, 1.2, 2.6);
|
||||
let val = l.pow(r);
|
||||
assert_approx_eq!(val.real(), -1.4193099970166037);
|
||||
assert_approx_eq!(val.imag(), -0.5412974660335446);
|
||||
assert_approx_eq!(val.real(), -1.419_309_997_016_603_7);
|
||||
assert_approx_eq!(val.imag(), -0.541_297_466_033_544_6);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
use crate::err::{self, PyErr, PyResult};
|
||||
use crate::ffi;
|
||||
use crate::instance::PyNativeType;
|
||||
use crate::object::PyObject;
|
||||
use crate::types::{PyAny, PyList};
|
||||
|
@ -9,8 +8,9 @@ use crate::AsPyPointer;
|
|||
#[cfg(not(PyPy))]
|
||||
use crate::IntoPyPointer;
|
||||
use crate::Python;
|
||||
use crate::{IntoPyObject, ToBorrowedObject, ToPyObject};
|
||||
use std::{cmp, collections, hash, mem};
|
||||
use crate::{ffi, IntoPy};
|
||||
use crate::{ToBorrowedObject, ToPyObject};
|
||||
use std::{cmp, collections, hash};
|
||||
|
||||
/// Represents a Python `dict`.
|
||||
#[repr(transparent)]
|
||||
|
@ -172,8 +172,8 @@ impl<'py> Iterator for PyDictIterator<'py> {
|
|||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
unsafe {
|
||||
let mut key: *mut ffi::PyObject = mem::uninitialized();
|
||||
let mut value: *mut ffi::PyObject = mem::uninitialized();
|
||||
let mut key: *mut ffi::PyObject = std::ptr::null_mut();
|
||||
let mut value: *mut ffi::PyObject = std::ptr::null_mut();
|
||||
if ffi::PyDict_Next(self.dict.as_ptr(), &mut self.pos, &mut key, &mut value) != 0 {
|
||||
let py = self.py;
|
||||
Some((py.from_borrowed_ptr(key), py.from_borrowed_ptr(value)))
|
||||
|
@ -214,29 +214,29 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<K, V, H> IntoPyObject for collections::HashMap<K, V, H>
|
||||
impl<K, V, H> IntoPy<PyObject> for collections::HashMap<K, V, H>
|
||||
where
|
||||
K: hash::Hash + cmp::Eq + IntoPyObject,
|
||||
V: IntoPyObject,
|
||||
K: hash::Hash + cmp::Eq + IntoPy<PyObject>,
|
||||
V: IntoPy<PyObject>,
|
||||
H: hash::BuildHasher,
|
||||
{
|
||||
fn into_object(self, py: Python) -> PyObject {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
let iter = self
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k.into_object(py), v.into_object(py)));
|
||||
.map(|(k, v)| (k.into_py(py), v.into_py(py)));
|
||||
IntoPyDict::into_py_dict(iter, py).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> IntoPyObject for collections::BTreeMap<K, V>
|
||||
impl<K, V> IntoPy<PyObject> for collections::BTreeMap<K, V>
|
||||
where
|
||||
K: cmp::Eq + IntoPyObject,
|
||||
V: IntoPyObject,
|
||||
K: cmp::Eq + IntoPy<PyObject>,
|
||||
V: IntoPy<PyObject>,
|
||||
{
|
||||
fn into_object(self, py: Python) -> PyObject {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
let iter = self
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k.into_object(py), v.into_object(py)));
|
||||
.map(|(k, v)| (k.into_py(py), v.into_py(py)));
|
||||
IntoPyDict::into_py_dict(iter, py).into()
|
||||
}
|
||||
}
|
||||
|
@ -304,12 +304,13 @@ where
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::conversion::IntoPy;
|
||||
use crate::instance::AsPyRef;
|
||||
use crate::types::dict::IntoPyDict;
|
||||
use crate::types::{PyDict, PyList, PyTuple};
|
||||
use crate::ObjectProtocol;
|
||||
use crate::Python;
|
||||
use crate::{IntoPyObject, PyTryFrom, ToPyObject};
|
||||
use crate::{ObjectProtocol, PyObject};
|
||||
use crate::{PyTryFrom, ToPyObject};
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
|
||||
#[test]
|
||||
|
@ -600,7 +601,7 @@ mod test {
|
|||
let mut map = HashMap::<i32, i32>::new();
|
||||
map.insert(1, 1);
|
||||
|
||||
let m = map.into_object(py);
|
||||
let m: PyObject = map.into_py(py);
|
||||
let py_map = <PyDict as PyTryFrom>::try_from(m.as_ref(py)).unwrap();
|
||||
|
||||
assert!(py_map.len() == 1);
|
||||
|
@ -622,14 +623,14 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_btreemap_into_object() {
|
||||
fn test_btreemap_into_py() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
||||
let mut map = BTreeMap::<i32, i32>::new();
|
||||
map.insert(1, 1);
|
||||
|
||||
let m = map.into_object(py);
|
||||
let m: PyObject = map.into_py(py);
|
||||
let py_map = <PyDict as PyTryFrom>::try_from(m.as_ref(py)).unwrap();
|
||||
|
||||
assert!(py_map.len() == 1);
|
||||
|
|
|
@ -8,11 +8,11 @@ use crate::instance::PyNativeType;
|
|||
use crate::object::PyObject;
|
||||
use crate::objectprotocol::ObjectProtocol;
|
||||
use crate::types::PyAny;
|
||||
use crate::AsPyPointer;
|
||||
use crate::FromPyObject;
|
||||
use crate::PyResult;
|
||||
use crate::Python;
|
||||
use crate::{IntoPyObject, ToPyObject};
|
||||
use crate::ToPyObject;
|
||||
use crate::{AsPyPointer, FromPy};
|
||||
use std::os::raw::c_double;
|
||||
|
||||
/// Represents a Python `float` object.
|
||||
|
@ -44,9 +44,9 @@ impl ToPyObject for f64 {
|
|||
}
|
||||
}
|
||||
|
||||
impl IntoPyObject for f64 {
|
||||
fn into_object(self, py: Python) -> PyObject {
|
||||
PyFloat::new(py, self).into()
|
||||
impl FromPy<f64> for PyObject {
|
||||
fn from_py(other: f64, py: Python) -> Self {
|
||||
PyFloat::new(py, other).into()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,9 +70,9 @@ impl ToPyObject for f32 {
|
|||
}
|
||||
}
|
||||
|
||||
impl IntoPyObject for f32 {
|
||||
fn into_object(self, py: Python) -> PyObject {
|
||||
PyFloat::new(py, f64::from(self)).into()
|
||||
impl FromPy<f32> for PyObject {
|
||||
fn from_py(other: f32, py: Python) -> Self {
|
||||
PyFloat::new(py, f64::from(other)).into()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,11 +91,13 @@ mod test {
|
|||
($func_name:ident, $t1:ty, $t2:ty) => (
|
||||
#[test]
|
||||
fn $func_name() {
|
||||
use assert_approx_eq::assert_approx_eq;
|
||||
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let val = 123 as $t1;
|
||||
let obj = val.to_object(py);
|
||||
assert_eq!(obj.extract::<$t2>(py).unwrap(), val as $t2);
|
||||
assert_approx_eq!(obj.extract::<$t2>(py).unwrap(), val as $t2);
|
||||
}
|
||||
)
|
||||
);
|
||||
|
@ -106,10 +108,12 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_as_double_macro() {
|
||||
use assert_approx_eq::assert_approx_eq;
|
||||
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let v = 1.23f64;
|
||||
let obj = v.to_object(py);
|
||||
assert_eq!(v, unsafe { PyFloat_AS_DOUBLE(obj.as_ptr()) });
|
||||
assert_approx_eq!(v, unsafe { PyFloat_AS_DOUBLE(obj.as_ptr()) });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,6 +40,10 @@ impl<'p> PyIterator<'p> {
|
|||
{
|
||||
unsafe {
|
||||
let ptr = ffi::PyObject_GetIter(obj.as_ptr());
|
||||
// Returns NULL if an object cannot be iterated.
|
||||
if ptr.is_null() {
|
||||
return Err(PyDowncastError);
|
||||
}
|
||||
|
||||
if ffi::PyIter_Check(ptr) != 0 {
|
||||
// this is not right, but this cause of segfault check #71
|
||||
|
|
|
@ -7,10 +7,10 @@ use crate::ffi::{self, Py_ssize_t};
|
|||
use crate::instance::PyNativeType;
|
||||
use crate::object::PyObject;
|
||||
use crate::types::PyAny;
|
||||
use crate::AsPyPointer;
|
||||
use crate::IntoPyPointer;
|
||||
use crate::Python;
|
||||
use crate::{IntoPyObject, ToBorrowedObject, ToPyObject};
|
||||
use crate::{AsPyPointer, IntoPy};
|
||||
use crate::{ToBorrowedObject, ToPyObject};
|
||||
|
||||
/// Represents a Python `list`.
|
||||
#[repr(transparent)]
|
||||
|
@ -186,15 +186,15 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> IntoPyObject for Vec<T>
|
||||
impl<T> IntoPy<PyObject> for Vec<T>
|
||||
where
|
||||
T: IntoPyObject,
|
||||
T: IntoPy<PyObject>,
|
||||
{
|
||||
fn into_object(self, py: Python) -> PyObject {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
unsafe {
|
||||
let ptr = ffi::PyList_New(self.len() as Py_ssize_t);
|
||||
for (i, e) in self.into_iter().enumerate() {
|
||||
let obj = e.into_object(py).into_ptr();
|
||||
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)
|
||||
|
|
|
@ -9,8 +9,9 @@ use crate::instance::PyNativeType;
|
|||
use crate::object::PyObject;
|
||||
use crate::types::PyAny;
|
||||
use crate::AsPyPointer;
|
||||
use crate::IntoPy;
|
||||
use crate::Python;
|
||||
use crate::{FromPyObject, IntoPyObject, ToPyObject};
|
||||
use crate::{FromPyObject, ToPyObject};
|
||||
use num_traits::cast::cast;
|
||||
use std::i64;
|
||||
use std::os::raw::c_int;
|
||||
|
@ -34,12 +35,12 @@ macro_rules! int_fits_larger_int (
|
|||
impl ToPyObject for $rust_type {
|
||||
#[inline]
|
||||
fn to_object(&self, py: Python) -> PyObject {
|
||||
(*self as $larger_type).into_object(py)
|
||||
(*self as $larger_type).into_py(py)
|
||||
}
|
||||
}
|
||||
impl IntoPyObject for $rust_type {
|
||||
fn into_object(self, py: Python) -> PyObject {
|
||||
(self as $larger_type).into_object(py)
|
||||
impl IntoPy<PyObject> for $rust_type {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
(self as $larger_type).into_py(py)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,11 +63,11 @@ macro_rules! int_convert_bignum (
|
|||
impl ToPyObject for $rust_type {
|
||||
#[inline]
|
||||
fn to_object(&self, py: Python) -> PyObject {
|
||||
self.into_object(py)
|
||||
(*self).into_py(py)
|
||||
}
|
||||
}
|
||||
impl IntoPyObject for $rust_type {
|
||||
fn into_object(self, py: Python) -> PyObject {
|
||||
impl IntoPy<PyObject> for $rust_type {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
unsafe {
|
||||
// TODO: Replace this with functions from the from_bytes family
|
||||
// Once they are stabilized
|
||||
|
@ -140,9 +141,9 @@ macro_rules! int_fits_c_long (
|
|||
}
|
||||
}
|
||||
}
|
||||
impl IntoPyObject for $rust_type {
|
||||
impl IntoPy<PyObject> for $rust_type {
|
||||
#![cfg_attr(feature="cargo-clippy", allow(clippy::cast_lossless))]
|
||||
fn into_object(self, py: Python) -> PyObject {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
unsafe {
|
||||
PyObject::from_owned_ptr_or_panic(py, ffi::PyLong_FromLong(self as c_long))
|
||||
}
|
||||
|
@ -181,9 +182,9 @@ macro_rules! int_convert_u64_or_i64 (
|
|||
}
|
||||
}
|
||||
}
|
||||
impl IntoPyObject for $rust_type {
|
||||
impl IntoPy<PyObject> for $rust_type {
|
||||
#[inline]
|
||||
fn into_object(self, py: Python) -> PyObject {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
unsafe {
|
||||
PyObject::from_owned_ptr_or_panic(py, $pylong_from_ll_or_ull(self))
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ use crate::object::PyObject;
|
|||
use crate::AsPyPointer;
|
||||
use crate::Python;
|
||||
use crate::{ToBorrowedObject, ToPyObject};
|
||||
use std::ptr;
|
||||
use std::{collections, hash};
|
||||
|
||||
/// Represents a Python `set`
|
||||
|
@ -28,6 +29,11 @@ impl PySet {
|
|||
unsafe { py.from_owned_ptr_or_err(ffi::PySet_New(list.as_ptr())) }
|
||||
}
|
||||
|
||||
/// Creates a new empty set
|
||||
pub fn empty<'p>(py: Python<'p>) -> PyResult<&'p PySet> {
|
||||
unsafe { py.from_owned_ptr_or_err(ffi::PySet_New(ptr::null_mut())) }
|
||||
}
|
||||
|
||||
/// Remove all elements from the set.
|
||||
#[inline]
|
||||
pub fn clear(&self) {
|
||||
|
@ -128,6 +134,11 @@ impl PyFrozenSet {
|
|||
unsafe { py.from_owned_ptr_or_err(ffi::PyFrozenSet_New(list.as_ptr())) }
|
||||
}
|
||||
|
||||
/// Creates a new empty frozen set
|
||||
pub fn empty<'p>(py: Python<'p>) -> PyResult<&'p PySet> {
|
||||
unsafe { py.from_owned_ptr_or_err(ffi::PyFrozenSet_New(ptr::null_mut())) }
|
||||
}
|
||||
|
||||
/// Return the number of items in the set.
|
||||
/// This is equivalent to len(p) on a set.
|
||||
#[inline]
|
||||
|
@ -177,6 +188,14 @@ mod test {
|
|||
assert!(PySet::new(py, &[v]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_empty() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let set = PySet::empty(py).unwrap();
|
||||
assert_eq!(0, set.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_len() {
|
||||
let gil = Python::acquire_gil();
|
||||
|
@ -264,6 +283,14 @@ mod test {
|
|||
assert!(PyFrozenSet::new(py, &[v]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_frozenset_empty() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let set = PyFrozenSet::empty(py).unwrap();
|
||||
assert_eq!(0, set.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_frozenset_contains() {
|
||||
let gil = Python::acquire_gil();
|
||||
|
|
|
@ -1,26 +1,32 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
use crate::conversion::FromPyObject;
|
||||
use crate::conversion::{IntoPyObject, PyTryFrom, ToPyObject};
|
||||
use crate::conversion::{PyTryFrom, ToPyObject};
|
||||
use crate::err::{PyErr, PyResult};
|
||||
use crate::exceptions;
|
||||
use crate::ffi;
|
||||
use crate::instance::PyNativeType;
|
||||
use crate::object::PyObject;
|
||||
use crate::types::PyAny;
|
||||
use crate::AsPyPointer;
|
||||
use crate::Python;
|
||||
use crate::{exceptions, IntoPy};
|
||||
use crate::{ffi, FromPy};
|
||||
use std::borrow::Cow;
|
||||
use std::ops::Index;
|
||||
use std::os::raw::c_char;
|
||||
use std::{mem, str};
|
||||
use std::slice::SliceIndex;
|
||||
use std::str;
|
||||
|
||||
/// Represents a Python `string`.
|
||||
///
|
||||
/// This type is immutable
|
||||
#[repr(transparent)]
|
||||
pub struct PyString(PyObject);
|
||||
|
||||
pyobject_native_type!(PyString, ffi::PyUnicode_Type, ffi::PyUnicode_Check);
|
||||
|
||||
/// Represents a Python `byte` string.
|
||||
/// Represents a Python `bytes`.
|
||||
///
|
||||
/// This type is immutable
|
||||
#[repr(transparent)]
|
||||
pub struct PyBytes(PyObject);
|
||||
|
||||
|
@ -56,7 +62,7 @@ impl PyString {
|
|||
#[inline]
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
unsafe {
|
||||
let mut size: ffi::Py_ssize_t = mem::uninitialized();
|
||||
let mut size: ffi::Py_ssize_t = 0;
|
||||
let data = ffi::PyUnicode_AsUTF8AndSize(self.0.as_ptr(), &mut size) as *const u8;
|
||||
// PyUnicode_AsUTF8AndSize would return null if the pointer did not reference a valid
|
||||
// unicode object, but because we have a valid PyString, assume success
|
||||
|
@ -120,6 +126,15 @@ impl PyBytes {
|
|||
}
|
||||
}
|
||||
|
||||
/// This is the same way [Vec] is indexed
|
||||
impl<I: SliceIndex<[u8]>> Index<I> for PyBytes {
|
||||
type Output = I::Output;
|
||||
|
||||
fn index(&self, index: I) -> &Self::Output {
|
||||
&self.as_bytes()[index]
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts Rust `str` to Python object.
|
||||
/// See `PyString::new` for details on the conversion.
|
||||
impl ToPyObject for str {
|
||||
|
@ -129,9 +144,9 @@ impl ToPyObject for str {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoPyObject for &'a str {
|
||||
impl<'a> IntoPy<PyObject> for &'a str {
|
||||
#[inline]
|
||||
fn into_object(self, py: Python) -> PyObject {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
PyString::new(py, self).into()
|
||||
}
|
||||
}
|
||||
|
@ -154,16 +169,15 @@ impl ToPyObject for String {
|
|||
}
|
||||
}
|
||||
|
||||
impl IntoPyObject for String {
|
||||
#[inline]
|
||||
fn into_object(self, py: Python) -> PyObject {
|
||||
PyString::new(py, &self).into()
|
||||
impl FromPy<String> for PyObject {
|
||||
fn from_py(other: String, py: Python) -> Self {
|
||||
PyString::new(py, &other).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoPyObject for &'a String {
|
||||
impl<'a> IntoPy<PyObject> for &'a String {
|
||||
#[inline]
|
||||
fn into_object(self, py: Python) -> PyObject {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
PyString::new(py, self).into()
|
||||
}
|
||||
}
|
||||
|
@ -203,7 +217,7 @@ impl<'source> FromPyObject<'source> for String {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::PyString;
|
||||
use super::{PyBytes, PyString};
|
||||
use crate::instance::AsPyRef;
|
||||
use crate::object::PyObject;
|
||||
use crate::Python;
|
||||
|
@ -261,4 +275,12 @@ mod test {
|
|||
assert!(py_string.to_string().is_ok());
|
||||
assert_eq!(Cow::Borrowed(s), py_string.to_string().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bytes_index() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let bytes = PyBytes::new(py, b"Hello World");
|
||||
assert_eq!(bytes[1], b'e');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ use crate::types::PyAny;
|
|||
use crate::AsPyPointer;
|
||||
use crate::IntoPyPointer;
|
||||
use crate::Python;
|
||||
use crate::{FromPyObject, IntoPy, IntoPyObject, PyTryFrom, ToPyObject};
|
||||
use crate::{FromPyObject, IntoPy, PyTryFrom, ToPyObject};
|
||||
use std::slice;
|
||||
|
||||
/// Represents a Python `tuple` object.
|
||||
|
@ -21,10 +21,7 @@ pyobject_native_type!(PyTuple, ffi::PyTuple_Type, ffi::PyTuple_Check);
|
|||
|
||||
impl PyTuple {
|
||||
/// Construct a new tuple with the given elements.
|
||||
pub fn new<'p, T, U>(
|
||||
py: Python<'p>,
|
||||
elements: impl IntoIterator<Item = T, IntoIter = U>,
|
||||
) -> &'p PyTuple
|
||||
pub fn new<T, U>(py: Python, elements: impl IntoIterator<Item = T, IntoIter = U>) -> &PyTuple
|
||||
where
|
||||
T: ToPyObject,
|
||||
U: ExactSizeIterator<Item = T>,
|
||||
|
@ -41,7 +38,7 @@ impl PyTuple {
|
|||
}
|
||||
|
||||
/// Retrieves the empty tuple.
|
||||
pub fn empty<'p>(py: Python<'p>) -> &'p PyTuple {
|
||||
pub fn empty(py: Python) -> &PyTuple {
|
||||
unsafe { py.from_owned_ptr(ffi::PyTuple_New(0)) }
|
||||
}
|
||||
|
||||
|
@ -157,26 +154,26 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
|
|||
fn to_object(&self, py: Python) -> PyObject {
|
||||
unsafe {
|
||||
let ptr = ffi::PyTuple_New($length);
|
||||
$(ffi::PyTuple_SetItem(ptr, $n, self.$n.to_object(py).into_ptr());)+;
|
||||
$(ffi::PyTuple_SetItem(ptr, $n, self.$n.to_object(py).into_ptr());)+
|
||||
PyObject::from_owned_ptr_or_panic(py, ptr)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl <$($T: IntoPyObject),+> IntoPyObject for ($($T,)+) {
|
||||
fn into_object(self, py: Python) -> PyObject {
|
||||
impl <$($T: IntoPy<PyObject>),+> IntoPy<PyObject> for ($($T,)+) {
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
unsafe {
|
||||
let ptr = ffi::PyTuple_New($length);
|
||||
$(ffi::PyTuple_SetItem(ptr, $n, self.$n.into_object(py).into_ptr());)+;
|
||||
$(ffi::PyTuple_SetItem(ptr, $n, self.$n.into_py(py).into_ptr());)+
|
||||
PyObject::from_owned_ptr_or_panic(py, ptr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <$($T: IntoPyObject),+> IntoPy<Py<PyTuple>> for ($($T,)+) {
|
||||
impl <$($T: IntoPy<PyObject>),+> IntoPy<Py<PyTuple>> for ($($T,)+) {
|
||||
fn into_py(self, py: Python) -> Py<PyTuple> {
|
||||
unsafe {
|
||||
let ptr = ffi::PyTuple_New($length);
|
||||
$(ffi::PyTuple_SetItem(ptr, $n, self.$n.into_object(py).into_ptr());)+;
|
||||
$(ffi::PyTuple_SetItem(ptr, $n, self.$n.into_py(py).into_ptr());)+
|
||||
Py::from_owned_ptr_or_panic(ptr)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#![feature(specialization)]
|
||||
|
||||
use pyo3::class::basic::CompareOp;
|
||||
use pyo3::class::*;
|
||||
use pyo3::prelude::*;
|
||||
|
@ -186,6 +188,28 @@ fn binary_arithmetic() {
|
|||
py_run!(py, c, "assert 1 | c == '1 | BA'");
|
||||
}
|
||||
|
||||
#[pyclass]
|
||||
struct RhsArithmetic {}
|
||||
|
||||
#[pyproto]
|
||||
impl PyNumberProtocol for RhsArithmetic {
|
||||
fn __radd__(&self, other: &PyAny) -> PyResult<String> {
|
||||
Ok(format!("{:?} + RA", other))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rhs_arithmetic() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
||||
let c = Py::new(py, RhsArithmetic {}).unwrap();
|
||||
py_run!(py, c, "assert c.__radd__(1) == '1 + RA'");
|
||||
// TODO: commented out for now until reflected arithemtics gets fixed.
|
||||
// see discussion here: https://github.com/PyO3/pyo3/pull/550
|
||||
// py_run!(py, c, "assert 1 + c == '1 + RA'");
|
||||
}
|
||||
|
||||
#[pyclass]
|
||||
struct RichComparisons {}
|
||||
|
||||
|
|
2
tests/test_compile_error.rs
Normal file → Executable file
2
tests/test_compile_error.rs
Normal file → Executable file
|
@ -1,6 +1,6 @@
|
|||
#[test]
|
||||
#[cfg(testkcovstopmarker)]
|
||||
fn test_compile_errors() {
|
||||
let t = trybuild::TestCases::new();
|
||||
t.compile_fail("tests/ui/reject_generics.rs");
|
||||
t.compile_fail("tests/ui/too_many_args_to_getter.rs");
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ use pyo3::ffi::*;
|
|||
use pyo3::prelude::*;
|
||||
use pyo3::types::{IntoPyDict, PyAny};
|
||||
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
fn _get_subclasses<'p>(
|
||||
py: &'p Python,
|
||||
py_type: &str,
|
||||
|
@ -101,6 +102,7 @@ fn test_delta_check() {
|
|||
|
||||
#[test]
|
||||
fn test_datetime_utc() {
|
||||
use assert_approx_eq::assert_approx_eq;
|
||||
use pyo3::types::PyDateTime;
|
||||
|
||||
let gil = Python::acquire_gil();
|
||||
|
@ -119,10 +121,11 @@ fn test_datetime_utc() {
|
|||
.unwrap()
|
||||
.extract()
|
||||
.unwrap();
|
||||
assert_eq!(offset, 0f32);
|
||||
assert_approx_eq!(offset, 0f32);
|
||||
}
|
||||
|
||||
static INVALID_DATES: &'static [(i32, u8, u8)] = &[
|
||||
#[cfg(Py_3_6)]
|
||||
static INVALID_DATES: &[(i32, u8, u8)] = &[
|
||||
(-1, 1, 1),
|
||||
(0, 1, 1),
|
||||
(10000, 1, 1),
|
||||
|
@ -134,7 +137,8 @@ static INVALID_DATES: &'static [(i32, u8, u8)] = &[
|
|||
(2018, 1, 32),
|
||||
];
|
||||
|
||||
static INVALID_TIMES: &'static [(u8, u8, u8, u32)] =
|
||||
#[cfg(Py_3_6)]
|
||||
static INVALID_TIMES: &[(u8, u8, u8, u32)] =
|
||||
&[(25, 0, 0, 0), (255, 0, 0, 0), (0, 60, 0, 0), (0, 0, 61, 0)];
|
||||
|
||||
#[cfg(Py_3_6)]
|
||||
|
@ -145,7 +149,7 @@ fn test_pydate_out_of_bounds() {
|
|||
// This test is an XFAIL on Python < 3.6 until bounds checking is implemented
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
for val in INVALID_DATES.into_iter() {
|
||||
for val in INVALID_DATES {
|
||||
let (year, month, day) = val;
|
||||
let dt = PyDate::new(py, *year, *month, *day);
|
||||
dt.unwrap_err();
|
||||
|
|
|
@ -12,5 +12,5 @@ fn iter_dict_nosegv() {
|
|||
let i: u64 = k.extract().unwrap();
|
||||
sum += i;
|
||||
}
|
||||
assert_eq!(sum, 49999995000000);
|
||||
assert_eq!(sum, 49_999_995_000_000);
|
||||
}
|
||||
|
|
26
tests/test_dunder.rs
Normal file → Executable file
26
tests/test_dunder.rs
Normal file → Executable file
|
@ -391,11 +391,11 @@ impl<'p> PyMappingProtocol<'p> for Test {
|
|||
if let Ok(slice) = idx.cast_as::<PySlice>() {
|
||||
let indices = slice.indices(1000)?;
|
||||
if indices.start == 100 && indices.stop == 200 && indices.step == 1 {
|
||||
return Ok("slice".into_object(gil.python()));
|
||||
return Ok("slice".into_py(gil.python()));
|
||||
}
|
||||
} else if let Ok(idx) = idx.extract::<isize>() {
|
||||
if idx == 1 {
|
||||
return Ok("int".into_object(gil.python()));
|
||||
return Ok("int".into_py(gil.python()));
|
||||
}
|
||||
}
|
||||
Err(PyErr::new::<ValueError, _>("error"))
|
||||
|
@ -462,3 +462,25 @@ fn weakref_dunder_dict_support() {
|
|||
"import weakref; assert weakref.ref(inst)() is inst; inst.a = 1; assert inst.a == 1"
|
||||
);
|
||||
}
|
||||
|
||||
#[pyclass]
|
||||
struct ClassWithGetAttr {
|
||||
#[pyo3(get, set)]
|
||||
data: u32,
|
||||
}
|
||||
|
||||
#[pyproto]
|
||||
impl PyObjectProtocol for ClassWithGetAttr {
|
||||
fn __getattr__(&self, _name: &str) -> PyResult<u32> {
|
||||
Ok(self.data * 2)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn getattr_doesnt_override_member() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let inst = PyRef::new(py, ClassWithGetAttr { data: 4 }).unwrap();
|
||||
py_assert!(py, inst, "inst.data == 4");
|
||||
py_assert!(py, inst, "inst.a == 8");
|
||||
}
|
||||
|
|
77
tests/test_exceptions.rs
Normal file
77
tests/test_exceptions.rs
Normal file
|
@ -0,0 +1,77 @@
|
|||
use pyo3::prelude::*;
|
||||
use pyo3::{exceptions, py_run, wrap_pyfunction, PyErr, PyResult};
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::fs::File;
|
||||
|
||||
mod common;
|
||||
|
||||
#[pyfunction]
|
||||
fn fail_to_open_file() -> PyResult<()> {
|
||||
File::open("not_there.txt")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn test_filenotfounderror() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let fail_to_open_file = wrap_pyfunction!(fail_to_open_file)(py);
|
||||
|
||||
py_run!(
|
||||
py,
|
||||
fail_to_open_file,
|
||||
r#"
|
||||
try:
|
||||
fail_to_open_file()
|
||||
except FileNotFoundError as e:
|
||||
assert str(e) == "No such file or directory (os error 2)"
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CustomError;
|
||||
|
||||
impl Error for CustomError {}
|
||||
|
||||
impl fmt::Display for CustomError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Oh no!")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<CustomError> for PyErr {
|
||||
fn from(err: CustomError) -> PyErr {
|
||||
exceptions::OSError::py_err(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn fail_with_custom_error() -> Result<(), CustomError> {
|
||||
Err(CustomError)
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn call_fail_with_custom_error() -> PyResult<()> {
|
||||
fail_with_custom_error()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_custom_error() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let call_fail_with_custom_error = wrap_pyfunction!(call_fail_with_custom_error)(py);
|
||||
|
||||
py_run!(
|
||||
py,
|
||||
call_fail_with_custom_error,
|
||||
r#"
|
||||
try:
|
||||
call_fail_with_custom_error()
|
||||
except OSError as e:
|
||||
assert str(e) == "Oh no!"
|
||||
"#
|
||||
);
|
||||
}
|
|
@ -94,7 +94,7 @@ impl Drop for ClassWithDrop {
|
|||
let py = Python::assume_gil_acquired();
|
||||
|
||||
let _empty1: Py<PyTuple> = FromPy::from_py(PyTuple::empty(py), py);
|
||||
let _empty2 = PyTuple::empty(py).into_object(py);
|
||||
let _empty2: PyObject = PyTuple::empty(py).into_py(py);
|
||||
let _empty3: &PyAny = py.from_owned_ptr(ffi::PyTuple_New(0));
|
||||
}
|
||||
}
|
||||
|
@ -110,9 +110,9 @@ fn create_pointers_in_drop() {
|
|||
{
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let empty = PyTuple::empty(py).into_object(py);
|
||||
let empty: PyObject = PyTuple::empty(py).into_py(py);
|
||||
ptr = empty.as_ptr();
|
||||
// substract 2, because `PyTuple::empty(py).into_object(py)` increases the refcnt by 2
|
||||
// substract 2, because `PyTuple::empty(py).into_py(py)` increases the refcnt by 2
|
||||
cnt = empty.get_refcnt() - 2;
|
||||
let inst = Py::new(py, ClassWithDrop {}).unwrap();
|
||||
drop(inst);
|
||||
|
@ -180,15 +180,20 @@ fn gc_integration() {
|
|||
#[pyclass(gc)]
|
||||
struct GCIntegration2 {}
|
||||
|
||||
#[pyproto]
|
||||
impl PyGCProtocol for GCIntegration2 {
|
||||
fn __traverse__(&self, _visit: PyVisit) -> Result<(), PyTraverseError> {
|
||||
Ok(())
|
||||
}
|
||||
fn __clear__(&mut self) {}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gc_integration2() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
// Temporarily disable pythons garbage collector to avoid a race condition
|
||||
py.run("import gc; gc.disable()", None, None).unwrap();
|
||||
let inst = PyRef::new(py, GCIntegration2 {}).unwrap();
|
||||
py_run!(py, inst, "assert inst in gc.get_objects()");
|
||||
py.run("gc.enable()", None, None).unwrap();
|
||||
py_run!(py, inst, "import gc; assert inst in gc.get_objects()");
|
||||
}
|
||||
|
||||
#[pyclass(weakref)]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use pyo3::prelude::*;
|
||||
use pyo3::py_run;
|
||||
use pyo3::types::IntoPyDict;
|
||||
use pyo3::types::{IntoPyDict, PyList};
|
||||
use std::isize;
|
||||
|
||||
mod common;
|
||||
|
@ -32,10 +32,16 @@ impl ClassWithProperties {
|
|||
fn get_unwrapped(&self) -> i32 {
|
||||
self.num
|
||||
}
|
||||
|
||||
#[setter]
|
||||
fn set_unwrapped(&mut self, value: i32) {
|
||||
self.num = value;
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn get_data_list<'py>(&self, py: Python<'py>) -> &'py PyList {
|
||||
PyList::new(py, &[self.num])
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -48,12 +54,12 @@ fn class_with_properties() {
|
|||
py_run!(py, inst, "assert inst.get_num() == 10");
|
||||
py_run!(py, inst, "assert inst.get_num() == inst.DATA");
|
||||
py_run!(py, inst, "inst.DATA = 20");
|
||||
py_run!(py, inst, "assert inst.get_num() == 20");
|
||||
py_run!(py, inst, "assert inst.get_num() == inst.DATA");
|
||||
py_run!(py, inst, "assert inst.get_num() == 20 == inst.DATA");
|
||||
|
||||
py_run!(py, inst, "assert inst.get_num() == inst.unwrapped == 20");
|
||||
py_run!(py, inst, "inst.unwrapped = 42");
|
||||
py_run!(py, inst, "assert inst.get_num() == inst.unwrapped == 42");
|
||||
py_run!(py, inst, "assert inst.data_list == [42]");
|
||||
|
||||
let d = [("C", py.get_type::<ClassWithProperties>())].into_py_dict(py);
|
||||
py.run(
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use pyo3::prelude::*;
|
||||
use pyo3::py_run;
|
||||
#[cfg(feature = "unsound-subclass")]
|
||||
use pyo3::types::IntoPyDict;
|
||||
use std::isize;
|
||||
|
||||
|
@ -11,9 +12,11 @@ struct BaseClass {
|
|||
val1: usize,
|
||||
}
|
||||
|
||||
#[cfg(feature = "unsound-subclass")]
|
||||
#[pyclass(subclass)]
|
||||
struct SubclassAble {}
|
||||
|
||||
#[cfg(feature = "unsound-subclass")]
|
||||
#[test]
|
||||
fn subclass() {
|
||||
let gil = Python::acquire_gil();
|
||||
|
|
|
@ -17,6 +17,9 @@ struct Reader {
|
|||
|
||||
#[pymethods]
|
||||
impl Reader {
|
||||
fn clone_ref(slf: PyRef<Self>) -> PyRef<Self> {
|
||||
slf
|
||||
}
|
||||
fn get_iter(slf: PyRef<Self>, keys: Py<PyBytes>) -> PyResult<Iter> {
|
||||
Ok(Iter {
|
||||
reader: slf.into(),
|
||||
|
@ -70,15 +73,18 @@ impl PyIterProtocol for Iter {
|
|||
}
|
||||
}
|
||||
|
||||
fn reader() -> Reader {
|
||||
let reader = [(1, "a"), (2, "b"), (3, "c"), (4, "d"), (5, "e")];
|
||||
Reader {
|
||||
inner: reader.iter().map(|(k, v)| (*k, v.to_string())).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nested_iter() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let reader = [(1, "a"), (2, "b"), (3, "c"), (4, "d"), (5, "e")];
|
||||
let reader = Reader {
|
||||
inner: reader.iter().map(|(k, v)| (*k, v.to_string())).collect(),
|
||||
}
|
||||
.into_object(py);
|
||||
let reader: PyObject = reader().into_py(py);
|
||||
py_assert!(
|
||||
py,
|
||||
reader,
|
||||
|
@ -86,23 +92,23 @@ fn test_nested_iter() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clone_ref() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let reader: PyObject = reader().into_py(py);
|
||||
py_assert!(py, reader, "reader == reader.clone_ref()");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nested_iter_reset() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let reader = [(1, "a"), (2, "b"), (3, "c"), (4, "d"), (5, "e")];
|
||||
let reader = PyRef::new(
|
||||
py,
|
||||
Reader {
|
||||
inner: reader.iter().map(|(k, v)| (*k, v.to_string())).collect(),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let obj = reader.into_object(py);
|
||||
let reader = PyRef::new(py, reader()).unwrap();
|
||||
py_assert!(
|
||||
py,
|
||||
obj,
|
||||
"list(obj.get_iter_and_reset(bytes([3, 5, 2]))) == ['c', 'e', 'b']"
|
||||
reader,
|
||||
"list(reader.get_iter_and_reset(bytes([3, 5, 2]))) == ['c', 'e', 'b']"
|
||||
);
|
||||
assert!(reader.inner.is_empty());
|
||||
}
|
||||
|
|
|
@ -40,8 +40,8 @@ impl PySequenceProtocol for ByteSequence {
|
|||
fn __getitem__(&self, idx: isize) -> PyResult<u8> {
|
||||
self.elements
|
||||
.get(idx as usize)
|
||||
.map(|&byte| byte)
|
||||
.ok_or(IndexError::py_err("list index out of range"))
|
||||
.copied()
|
||||
.ok_or_else(|| IndexError::py_err("list index out of range"))
|
||||
}
|
||||
|
||||
fn __setitem__(&mut self, idx: isize, value: u8) -> PyResult<()> {
|
||||
|
|
|
@ -111,7 +111,7 @@ fn pytuple_pyclass_iter() {
|
|||
PyRef::new(py, SimplePyClass {}).unwrap(),
|
||||
PyRef::new(py, SimplePyClass {}).unwrap(),
|
||||
]
|
||||
.into_iter(),
|
||||
.iter(),
|
||||
);
|
||||
py_assert!(py, tup, "type(tup[0]).__name__ == 'SimplePyClass'");
|
||||
py_assert!(py, tup, "type(tup[0]).__name__ == type(tup[0]).__name__");
|
||||
|
@ -170,3 +170,13 @@ fn test_pickle() {
|
|||
"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn incorrect_iter() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let int = 13isize.to_object(py);
|
||||
let int_ref = int.as_ref(py);
|
||||
// Should not segfault.
|
||||
assert!(int_ref.iter().is_err());
|
||||
}
|
||||
|
|
|
@ -3,3 +3,5 @@ error: #[pyclass] cannot have generic parameters
|
|||
|
|
||||
4 | struct ClassWithGenerics<A> {
|
||||
| ^^^
|
||||
|
||||
error: Could not compile `pyo3-tests`.
|
||||
|
|
14
tests/ui/too_many_args_to_getter.rs
Normal file
14
tests/ui/too_many_args_to_getter.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
use pyo3::prelude::*;
|
||||
|
||||
#[pyclass]
|
||||
struct ClassWithGetter {
|
||||
a: u32,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl ClassWithGetter {
|
||||
#[getter]
|
||||
fn get_num(&self, index: u32) {}
|
||||
}
|
||||
|
||||
fn main() {}
|
7
tests/ui/too_many_args_to_getter.stderr
Normal file
7
tests/ui/too_many_args_to_getter.stderr
Normal file
|
@ -0,0 +1,7 @@
|
|||
error: Getter function can only have one argument of type pyo3::Python!
|
||||
--> $DIR/too_many_args_to_getter.rs:11:30
|
||||
|
|
||||
11 | fn get_num(&self, index: u32) {}
|
||||
| ^^^
|
||||
|
||||
error: Could not compile `pyo3-tests`.
|
Loading…
Reference in a new issue