Merge branch 'master' into abi3-merge-master
This commit is contained in:
commit
140790b15f
|
@ -53,9 +53,9 @@ jobs:
|
||||||
{ os: "windows-latest", python-architecture: "x86", rust-target: "i686-pc-windows-msvc" },
|
{ os: "windows-latest", python-architecture: "x86", rust-target: "i686-pc-windows-msvc" },
|
||||||
]
|
]
|
||||||
exclude:
|
exclude:
|
||||||
# There is no 64-bit pypy on windows
|
# See https://github.com/PyO3/pyo3/pull/1215 for why.
|
||||||
- python-version: pypy3
|
- python-version: pypy3
|
||||||
platform: { os: "windows-latest", python-architecture: "x64" }
|
platform: { os: "windows-latest" }
|
||||||
include:
|
include:
|
||||||
# Test minimal supported Rust version
|
# Test minimal supported Rust version
|
||||||
- rust: 1.39.0
|
- rust: 1.39.0
|
||||||
|
|
|
@ -5,6 +5,7 @@ on:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
release:
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
|
@ -12,6 +13,8 @@ env:
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
tag_name: ${{ steps.prepare_tag.outputs.tag_name }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
@ -44,3 +47,23 @@ jobs:
|
||||||
publish_dir: ./gh-pages-build/
|
publish_dir: ./gh-pages-build/
|
||||||
destination_dir: ${{ steps.prepare_tag.outputs.tag_name }}
|
destination_dir: ${{ steps.prepare_tag.outputs.tag_name }}
|
||||||
full_commit_message: 'Upload documentation for ${{ steps.prepare_tag.outputs.tag_name }}'
|
full_commit_message: 'Upload documentation for ${{ steps.prepare_tag.outputs.tag_name }}'
|
||||||
|
|
||||||
|
release:
|
||||||
|
needs: deploy
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event_name == 'release' }}
|
||||||
|
steps:
|
||||||
|
- name: Create latest tag redirect
|
||||||
|
env:
|
||||||
|
TAG_NAME: ${{ needs.deploy.outputs.tag_name }}
|
||||||
|
run: |
|
||||||
|
mkdir public
|
||||||
|
echo "<meta http-equiv=refresh content=0;url='$TAG_NAME/'>" > public/index.html
|
||||||
|
|
||||||
|
- name: Deploy
|
||||||
|
uses: peaceiris/actions-gh-pages@v3.7.0-8
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
publish_dir: ./public/
|
||||||
|
full_commit_message: 'Release ${{ needs.deploy.outputs.tag_name }}'
|
||||||
|
keep_files: true
|
||||||
|
|
110
CHANGELOG.md
110
CHANGELOG.md
|
@ -7,66 +7,81 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
### Added
|
### Added
|
||||||
- Add FFI definitions `Py_FinalizeEx`, `PyOS_getsig`, `PyOS_setsig`. [#1021](https://github.com/PyO3/pyo3/pull/1021)
|
- Add support for keyword-only arguments without default values in `#[pyfunction]`. [#1209](https://github.com/PyO3/pyo3/pull/1209)
|
||||||
- Add `Python::with_gil` for executing a closure with the Python GIL. [#1037](https://github.com/PyO3/pyo3/pull/1037)
|
- Add a wrapper for `PyErr_CheckSignals()` as `Python::check_signals()`. [#1214](https://github.com/PyO3/pyo3/pull/1214)
|
||||||
- Implement `Debug` for `PyIterator`. [#1051](https://github.com/PyO3/pyo3/pull/1051)
|
|
||||||
- Implement type information for conversion failures. [#1050](https://github.com/PyO3/pyo3/pull/1050)
|
|
||||||
- Add `PyBytes::new_with` and `PyByteArray::new_with` for initialising Python-allocated bytes and bytearrays using a closure. [#1074](https://github.com/PyO3/pyo3/pull/1074)
|
|
||||||
- Add `Py::as_ref` and `Py::into_ref`. [#1098](https://github.com/PyO3/pyo3/pull/1098)
|
|
||||||
- Add optional implementations of `ToPyObject`, `IntoPy`, and `FromPyObject` for [hashbrown](https://crates.io/crates/hashbrown)'s `HashMap` and `HashSet` types. The `hashbrown` feature must be enabled for these implementations to be built. [#1114](https://github.com/PyO3/pyo3/pull/1114/)
|
|
||||||
- Allow other `Result` types when using `#[pyfunction]`. [#1106](https://github.com/PyO3/pyo3/issues/1106).
|
|
||||||
- Add `#[derive(FromPyObject)]` macro for enums and structs. [#1065](https://github.com/PyO3/pyo3/pull/1065)
|
|
||||||
- Add macro attribute to `#[pyfn]` and `#[pyfunction]` to pass the module of a Python function to the function
|
|
||||||
body. [#1143](https://github.com/PyO3/pyo3/pull/1143)
|
|
||||||
- Add `add_function()` and `add_submodule()` functions to `PyModule` [#1143](https://github.com/PyO3/pyo3/pull/1143)
|
|
||||||
- Add native `PyCFunction` and `PyFunction` types, change `add_function` to take a wrapper returning
|
|
||||||
a `&PyCFunction`instead of `PyObject`. [#1163](https://github.com/PyO3/pyo3/pull/1163)
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Exception types have been renamed from e.g. `RuntimeError` to `PyRuntimeError`, and are now accessible by `&T` or `Py<T>` similar to other Python-native types. The old names continue to exist but are deprecated. [#1024](https://github.com/PyO3/pyo3/pull/1024) [#1115](https://github.com/PyO3/pyo3/pull/1115)
|
- Fields of `PyMethodDef`, `PyGetterDef`, `PySetterDef`, and `PyClassAttributeDef` are now private. [#1169](https://github.com/PyO3/pyo3/pull/1169)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fix invalid document for protocol methods. [#1169](https://github.com/PyO3/pyo3/pull/1169)
|
||||||
|
|
||||||
|
## [0.12.1] - 2020-09-16
|
||||||
|
### Fixed
|
||||||
|
- Fix building for a 32-bit Python on 64-bit Windows with a 64-bit Rust toolchain. [#1179](https://github.com/PyO3/pyo3/pull/1179)
|
||||||
|
- Fix building on platforms where `c_char` is `u8`. [#1182](https://github.com/PyO3/pyo3/pull/1182)
|
||||||
|
|
||||||
|
## [0.12.0] - 2020-09-12
|
||||||
|
### Added
|
||||||
|
- Add FFI definitions `Py_FinalizeEx`, `PyOS_getsig`, and `PyOS_setsig`. [#1021](https://github.com/PyO3/pyo3/pull/1021)
|
||||||
|
- Add `PyString::to_str` for accessing `PyString` as `&str`. [#1023](https://github.com/PyO3/pyo3/pull/1023)
|
||||||
|
- Add `Python::with_gil` for executing a closure with the Python GIL. [#1037](https://github.com/PyO3/pyo3/pull/1037)
|
||||||
|
- Add type information to failures in `PyAny::downcast()`. [#1050](https://github.com/PyO3/pyo3/pull/1050)
|
||||||
|
- Implement `Debug` for `PyIterator`. [#1051](https://github.com/PyO3/pyo3/pull/1051)
|
||||||
|
- Add `PyBytes::new_with` and `PyByteArray::new_with` for initialising `bytes` and `bytearray` objects using a closure. [#1074](https://github.com/PyO3/pyo3/pull/1074)
|
||||||
|
- Add `#[derive(FromPyObject)]` macro for enums and structs. [#1065](https://github.com/PyO3/pyo3/pull/1065)
|
||||||
|
- Add `Py::as_ref` and `Py::into_ref` for converting `Py<T>` to `&T`. [#1098](https://github.com/PyO3/pyo3/pull/1098)
|
||||||
|
- Add ability to return `Result` types other than `PyResult` from `#[pyfunction]`, `#[pymethod]` and `#[pyproto]` functions. [#1106](https://github.com/PyO3/pyo3/pull/1118).
|
||||||
|
- Implement `ToPyObject`, `IntoPy`, and `FromPyObject` for [hashbrown](https://crates.io/crates/hashbrown)'s `HashMap` and `HashSet` types (requires the `hashbrown` feature). [#1114](https://github.com/PyO3/pyo3/pull/1114)
|
||||||
|
- Add `#[pyfunction(pass_module)]` and `#[pyfn(pass_module)]` to pass the module object as the first function argument. [#1143](https://github.com/PyO3/pyo3/pull/1143)
|
||||||
|
- Add `PyModule::add_function` and `PyModule::add_submodule` as typed alternatives to `PyModule::add_wrapped`. [#1143](https://github.com/PyO3/pyo3/pull/1143)
|
||||||
|
- Add native `PyCFunction` and `PyFunction` types. [#1163](https://github.com/PyO3/pyo3/pull/1163)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Rework exception types: [#1024](https://github.com/PyO3/pyo3/pull/1024) [#1115](https://github.com/PyO3/pyo3/pull/1115)
|
||||||
|
- Rename exception types from e.g. `RuntimeError` to `PyRuntimeError`. The old names continue to exist but are deprecated.
|
||||||
|
- Exception objects are now accessible as `&T` or `Py<T>`, just like other Python-native types.
|
||||||
- Rename `PyException::py_err()` to `PyException::new_err()`.
|
- Rename `PyException::py_err()` to `PyException::new_err()`.
|
||||||
- Rename `PyUnicodeDecodeErr::new_err()` to `PyUnicodeDecodeErr::new()`.
|
- Rename `PyUnicodeDecodeErr::new_err()` to `PyUnicodeDecodeErr::new()`.
|
||||||
- Remove `PyStopIteration::stop_iteration()`.
|
- Remove `PyStopIteration::stop_iteration()`.
|
||||||
- Correct FFI definitions `Py_SetProgramName` and `Py_SetPythonHome` to take `*const` argument instead of `*mut`. [#1021](https://github.com/PyO3/pyo3/pull/1021)
|
- Require `T: Send` for the return value `T` of `Python::allow_threads`. [#1036](https://github.com/PyO3/pyo3/pull/1036)
|
||||||
- Rename `PyString::to_string` to `to_str`, change return type `Cow<str>` to `&str`. [#1023](https://github.com/PyO3/pyo3/pull/1023)
|
- Rename `PYTHON_SYS_EXECUTABLE` to `PYO3_PYTHON`. The old name will continue to work (undocumented) but will be removed in a future release. [#1039](https://github.com/PyO3/pyo3/pull/1039)
|
||||||
- Correct FFI definition `_PyLong_AsByteArray` `*mut c_uchar` argument instead of `*const c_uchar`. [#1029](https://github.com/PyO3/pyo3/pull/1029)
|
- Remove `unsafe` from signature of `PyType::as_type_ptr`. [#1047](https://github.com/PyO3/pyo3/pull/1047)
|
||||||
- Add `T: Send` bound to the return value of `Python::allow_threads`. [#1036](https://github.com/PyO3/pyo3/pull/1036)
|
- Change return type of `PyIterator::from_object` to `PyResult<PyIterator>` (was `Result<PyIterator, PyDowncastError>`). [#1051](https://github.com/PyO3/pyo3/pull/1051)
|
||||||
- Rename `PYTHON_SYS_EXECUTABLE` to `PYO3_PYTHON`. The old name will continue to work but will be undocumented, and will be removed in a future release. [#1039](https://github.com/PyO3/pyo3/pull/1039)
|
|
||||||
- `PyType::as_type_ptr` is no longer `unsafe`. [#1047](https://github.com/PyO3/pyo3/pull/1047)
|
|
||||||
- Change `PyIterator::from_object` to return `PyResult<PyIterator>` instead of `Result<PyIterator, PyDowncastError>`. [#1051](https://github.com/PyO3/pyo3/pull/1051)
|
|
||||||
- `IntoPy` is no longer implied by `FromPy`. [#1063](https://github.com/PyO3/pyo3/pull/1063)
|
- `IntoPy` is no longer implied by `FromPy`. [#1063](https://github.com/PyO3/pyo3/pull/1063)
|
||||||
- `PyObject` is now just a type alias for `Py<PyAny>`. [#1063](https://github.com/PyO3/pyo3/pull/1063)
|
- Change `PyObject` to be a type alias for `Py<PyAny>`. [#1063](https://github.com/PyO3/pyo3/pull/1063)
|
||||||
- Rework PyErr to be compatible with the `std::error::Error` trait: [#1067](https://github.com/PyO3/pyo3/pull/1067) [#1115](https://github.com/PyO3/pyo3/pull/1115)
|
- Rework `PyErr` to be compatible with the `std::error::Error` trait: [#1067](https://github.com/PyO3/pyo3/pull/1067) [#1115](https://github.com/PyO3/pyo3/pull/1115)
|
||||||
- Implement `Display`, `Error`, `Send` and `Sync` for `PyErr` and `PyErrArguments`.
|
- Implement `Display`, `Error`, `Send` and `Sync` for `PyErr` and `PyErrArguments`.
|
||||||
- Add `PyErr::instance()` which returns `&PyBaseException`.
|
- Add `PyErr::instance()` for accessing `PyErr` as `&PyBaseException`.
|
||||||
- `PyErr`'s fields are now an implementation detail. The equivalent values can be accessed with `PyErr::ptype()`, `PyErr::pvalue()` and `PyErr::ptraceback()`.
|
- `PyErr`'s fields are now an implementation detail. The equivalent values can be accessed with `PyErr::ptype()`, `PyErr::pvalue()` and `PyErr::ptraceback()`.
|
||||||
- Change `PyErr::print()` and `PyErr::print_and_set_sys_last_vars()` to take `&self` instead of `self`.
|
- Change receiver of `PyErr::print()` and `PyErr::print_and_set_sys_last_vars()` to `&self` (was `self`).
|
||||||
- Remove `PyErrValue`, `PyErr::from_value`, `PyErr::into_normalized()`, and `PyErr::normalize()`.
|
- Remove `PyErrValue`, `PyErr::from_value`, `PyErr::into_normalized()`, and `PyErr::normalize()`.
|
||||||
- Remove `PyException::into()` and `Into<PyResult<T>>` for `PyErr` and `PyException`.
|
- Remove `PyException::into()`.
|
||||||
- Change `#[pyproto]` to return NotImplemented for operators for which Python can try a reversed operation. #[1072](https://github.com/PyO3/pyo3/pull/1072)
|
- Remove `Into<PyResult<T>>` for `PyErr` and `PyException`.
|
||||||
- `PyModule::add` now uses `IntoPy<PyObject>` instead of `ToPyObject`. #[1124](https://github.com/PyO3/pyo3/pull/1124)
|
- Change methods generated by `#[pyproto]` to return `NotImplemented` if Python should try a reversed operation. #[1072](https://github.com/PyO3/pyo3/pull/1072)
|
||||||
- Add nested modules as `&PyModule` instead of using the wrapper generated by `#[pymodule]`. [#1143](https://github.com/PyO3/pyo3/pull/1143)
|
- Change argument to `PyModule::add` to `impl IntoPy<PyObject>` (was `impl ToPyObject`). #[1124](https://github.com/PyO3/pyo3/pull/1124)
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
- Remove many exception and `PyErr` APIs; see the "changed" section above. [#1024](https://github.com/PyO3/pyo3/pull/1024) [#1067](https://github.com/PyO3/pyo3/pull/1067) [#1115](https://github.com/PyO3/pyo3/pull/1115)
|
||||||
|
- Remove `PyString::to_string` (use new `PyString::to_str`). [#1023](https://github.com/PyO3/pyo3/pull/1023)
|
||||||
- Remove `PyString::as_bytes`. [#1023](https://github.com/PyO3/pyo3/pull/1023)
|
- Remove `PyString::as_bytes`. [#1023](https://github.com/PyO3/pyo3/pull/1023)
|
||||||
- Remove `Python::register_any`. [#1023](https://github.com/PyO3/pyo3/pull/1023)
|
- Remove `Python::register_any`. [#1023](https://github.com/PyO3/pyo3/pull/1023)
|
||||||
- Remove `GILGuard::acquire` from the public API. Use `Python::acquire_gil` or `Python::with_gil`. [#1036](https://github.com/PyO3/pyo3/pull/1036)
|
- Remove `GILGuard::acquire` from the public API. Use `Python::acquire_gil` or `Python::with_gil`. [#1036](https://github.com/PyO3/pyo3/pull/1036)
|
||||||
- Remove `FromPy`. [#1063](https://github.com/PyO3/pyo3/pull/1063)
|
- Remove the `FromPy` trait. [#1063](https://github.com/PyO3/pyo3/pull/1063)
|
||||||
- Remove `AsPyRef`. [#1098](https://github.com/PyO3/pyo3/pull/1098)
|
- Remove the `AsPyRef` trait. [#1098](https://github.com/PyO3/pyo3/pull/1098)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Conversion from types with an `__index__` method to Rust BigInts. [#1027](https://github.com/PyO3/pyo3/pull/1027)
|
- Correct FFI definitions `Py_SetProgramName` and `Py_SetPythonHome` to take `*const` arguments (was `*mut`). [#1021](https://github.com/PyO3/pyo3/pull/1021)
|
||||||
- Fix segfault with #[pyclass(dict, unsendable)] [#1058](https://github.com/PyO3/pyo3/pull/1058)
|
- Fix `FromPyObject` for `num_bigint::BigInt` for Python objects with an `__index__` method. [#1027](https://github.com/PyO3/pyo3/pull/1027)
|
||||||
- Don't rely on the order of structmembers to compute offsets in PyCell. Related to
|
- Correct FFI definition `_PyLong_AsByteArray` to take `*mut c_uchar` argument (was `*const c_uchar`). [#1029](https://github.com/PyO3/pyo3/pull/1029)
|
||||||
[#1058](https://github.com/PyO3/pyo3/pull/1058). [#1059](https://github.com/PyO3/pyo3/pull/1059)
|
- Fix segfault with `#[pyclass(dict, unsendable)]`. [#1058](https://github.com/PyO3/pyo3/pull/1058) [#1059](https://github.com/PyO3/pyo3/pull/1059)
|
||||||
- Allows `&Self` as a `#[pymethods]` argument again. [#1071](https://github.com/PyO3/pyo3/pull/1071)
|
- Fix using `&Self` as an argument type for functions in a `#[pymethods]` block. [#1071](https://github.com/PyO3/pyo3/pull/1071)
|
||||||
- Fix best-effort build against PyPy 3.6. #[1092](https://github.com/PyO3/pyo3/pull/1092)
|
- Fix best-effort build against PyPy 3.6. [#1092](https://github.com/PyO3/pyo3/pull/1092)
|
||||||
- Improve lifetime elision in `#[pyproto]`. [#1093](https://github.com/PyO3/pyo3/pull/1093)
|
- Fix many cases of lifetime elision in `#[pyproto]` implementations. [#1093](https://github.com/PyO3/pyo3/pull/1093)
|
||||||
- Fix python configuration detection when cross-compiling. [#1095](https://github.com/PyO3/pyo3/pull/1095)
|
- Fix detection of Python build configuration when cross-compiling. [#1095](https://github.com/PyO3/pyo3/pull/1095)
|
||||||
- Link against libpython on android with `extension-module` set. [#1095](https://github.com/PyO3/pyo3/pull/1095)
|
- Always link against libpython on android with the `extension-module` feature. [#1095](https://github.com/PyO3/pyo3/pull/1095)
|
||||||
- Fix support for both `__add__` and `__radd__` in the `+` operator when both are defined in `PyNumberProtocol`
|
- Fix the `+` operator not trying `__radd__` when both `__add__` and `__radd__` are defined in `PyNumberProtocol` (and similar for all other reversible operators). [#1107](https://github.com/PyO3/pyo3/pull/1107)
|
||||||
(and similar for all other reversible operators). [#1107](https://github.com/PyO3/pyo3/pull/1107)
|
- Fix building with Anaconda python. [#1175](https://github.com/PyO3/pyo3/pull/1175)
|
||||||
- Associate Python functions with their module by passing the Module and Module name [#1143](https://github.com/PyO3/pyo3/pull/1143)
|
|
||||||
|
|
||||||
## [0.11.1] - 2020-06-30
|
## [0.11.1] - 2020-06-30
|
||||||
### Added
|
### Added
|
||||||
|
@ -493,11 +508,13 @@ Yanked
|
||||||
- Allow to add gc support without implementing PyGCProtocol #57
|
- Allow to add gc support without implementing PyGCProtocol #57
|
||||||
- Refactor `PyErr` implementation. Drop `py` parameter from constructor.
|
- Refactor `PyErr` implementation. Drop `py` parameter from constructor.
|
||||||
|
|
||||||
## 0.1.0 - 07-23-2017
|
## [0.1.0] - 07-23-2017
|
||||||
### Added
|
### Added
|
||||||
- Initial release
|
- Initial release
|
||||||
|
|
||||||
[Unreleased]: https://github.com/pyo3/pyo3/compare/v0.11.1...HEAD
|
[Unreleased]: https://github.com/pyo3/pyo3/compare/v0.12.1...HEAD
|
||||||
|
[0.12.1]: https://github.com/pyo3/pyo3/compare/v0.12.0...v0.12.1
|
||||||
|
[0.12.0]: https://github.com/pyo3/pyo3/compare/v0.11.1...v0.12.0
|
||||||
[0.11.1]: https://github.com/pyo3/pyo3/compare/v0.11.0...v0.11.1
|
[0.11.1]: https://github.com/pyo3/pyo3/compare/v0.11.0...v0.11.1
|
||||||
[0.11.0]: https://github.com/pyo3/pyo3/compare/v0.10.1...v0.11.0
|
[0.11.0]: https://github.com/pyo3/pyo3/compare/v0.10.1...v0.11.0
|
||||||
[0.10.1]: https://github.com/pyo3/pyo3/compare/v0.10.0...v0.10.1
|
[0.10.1]: https://github.com/pyo3/pyo3/compare/v0.10.0...v0.10.1
|
||||||
|
@ -529,3 +546,4 @@ Yanked
|
||||||
[0.2.2]: https://github.com/pyo3/pyo3/compare/v0.2.1...v0.2.2
|
[0.2.2]: https://github.com/pyo3/pyo3/compare/v0.2.1...v0.2.2
|
||||||
[0.2.1]: https://github.com/pyo3/pyo3/compare/v0.2.0...v0.2.1
|
[0.2.1]: https://github.com/pyo3/pyo3/compare/v0.2.0...v0.2.1
|
||||||
[0.2.0]: https://github.com/pyo3/pyo3/compare/v0.1.0...v0.2.0
|
[0.2.0]: https://github.com/pyo3/pyo3/compare/v0.1.0...v0.2.0
|
||||||
|
[0.1.0]: https://github.com/PyO3/pyo3/tree/0.1.0
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "pyo3"
|
name = "pyo3"
|
||||||
version = "0.11.1"
|
version = "0.12.1"
|
||||||
description = "Bindings to Python interpreter"
|
description = "Bindings to Python interpreter"
|
||||||
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
|
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
@ -22,8 +22,8 @@ libc = "0.2.62"
|
||||||
parking_lot = "0.11.0"
|
parking_lot = "0.11.0"
|
||||||
num-bigint = { version = "0.3", optional = true }
|
num-bigint = { version = "0.3", optional = true }
|
||||||
num-complex = { version = "0.3", optional = true }
|
num-complex = { version = "0.3", optional = true }
|
||||||
paste = { version = "0.1.6", optional = true }
|
paste = { version = "1.0.1", optional = true }
|
||||||
pyo3cls = { path = "pyo3cls", version = "=0.11.1", optional = true }
|
pyo3cls = { path = "pyo3cls", version = "=0.12.1", optional = true }
|
||||||
unindent = { version = "0.1.4", optional = true }
|
unindent = { version = "0.1.4", optional = true }
|
||||||
hashbrown = { version = "0.9", optional = true }
|
hashbrown = { version = "0.9", optional = true }
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ name = "string_sum"
|
||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
[dependencies.pyo3]
|
[dependencies.pyo3]
|
||||||
version = "0.11.1"
|
version = "0.12.1"
|
||||||
features = ["extension-module"]
|
features = ["extension-module"]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ use it to run Python code, add `pyo3` to your `Cargo.toml` like this:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
pyo3 = "0.11.1"
|
pyo3 = "0.12.1"
|
||||||
```
|
```
|
||||||
|
|
||||||
Example program displaying the value of `sys.version` and the current user name:
|
Example program displaying the value of `sys.version` and the current user name:
|
||||||
|
@ -135,7 +135,7 @@ about this topic.
|
||||||
## Tools and libraries
|
## Tools and libraries
|
||||||
* [maturin](https://github.com/PyO3/maturin) _Zero configuration build tool for Rust-made Python extensions_.
|
* [maturin](https://github.com/PyO3/maturin) _Zero configuration build tool for Rust-made Python extensions_.
|
||||||
* [setuptools-rust](https://github.com/PyO3/setuptools-rust) _Setuptools plugin for Rust support_.
|
* [setuptools-rust](https://github.com/PyO3/setuptools-rust) _Setuptools plugin for Rust support_.
|
||||||
* [pyo3-built](https://github.com/PyO3/pyo3-built) _Simple macro to expose metadata obtained with the [`built`](https://crates.io/crates/built) crate as a [`PyDict`](https://pyo3.github.io/pyo3/pyo3/struct.PyDict.html)_
|
* [pyo3-built](https://github.com/PyO3/pyo3-built) _Simple macro to expose metadata obtained with the [`built`](https://crates.io/crates/built) crate as a [`PyDict`](https://docs.rs/pyo3/0.12.0/pyo3/types/struct.PyDict.html)_
|
||||||
* [rust-numpy](https://github.com/PyO3/rust-numpy) _Rust binding of NumPy C-API_
|
* [rust-numpy](https://github.com/PyO3/rust-numpy) _Rust binding of NumPy C-API_
|
||||||
* [dict-derive](https://github.com/gperinazzo/dict-derive) _Derive FromPyObject to automatically transform Python dicts into Rust structs_
|
* [dict-derive](https://github.com/gperinazzo/dict-derive) _Derive FromPyObject to automatically transform Python dicts into Rust structs_
|
||||||
* [pyo3-log](https://github.com/vorner/pyo3-log) _Bridge from Rust to Python logging_
|
* [pyo3-log](https://github.com/vorner/pyo3-log) _Bridge from Rust to Python logging_
|
||||||
|
@ -152,7 +152,7 @@ about this topic.
|
||||||
* [Rogue-Gym](https://github.com/kngwyu/rogue-gym) _Customizable rogue-like game for AI experiments_
|
* [Rogue-Gym](https://github.com/kngwyu/rogue-gym) _Customizable rogue-like game for AI experiments_
|
||||||
* Contains an example of building wheels on Azure Pipelines
|
* Contains an example of building wheels on Azure Pipelines
|
||||||
* [fastuuid](https://github.com/thedrow/fastuuid/) _Python bindings to Rust's UUID library_
|
* [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_
|
* [wasmer-python](https://github.com/wasmerio/wasmer-python) _Python library to run WebAssembly binaries_
|
||||||
* [mocpy](https://github.com/cds-astro/mocpy) _Astronomical Python library offering data structures for describing any arbitrary coverage regions on the unit sphere_
|
* [mocpy](https://github.com/cds-astro/mocpy) _Astronomical Python library offering data structures for describing any arbitrary coverage regions on the unit sphere_
|
||||||
* [tokenizers](https://github.com/huggingface/tokenizers/tree/master/bindings/python) _Python bindings to the Hugging Face tokenizers (NLP) written in Rust_
|
* [tokenizers](https://github.com/huggingface/tokenizers/tree/master/bindings/python) _Python bindings to the Hugging Face tokenizers (NLP) written in Rust_
|
||||||
|
|
||||||
|
|
12
build.rs
12
build.rs
|
@ -148,7 +148,9 @@ impl CrossCompileConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cross_compiling() -> Result<Option<CrossCompileConfig>> {
|
fn cross_compiling() -> Result<Option<CrossCompileConfig>> {
|
||||||
if env::var("TARGET")? == env::var("HOST")? {
|
let target = env::var("TARGET")?;
|
||||||
|
let host = env::var("HOST")?;
|
||||||
|
if target == host || (target == "i686-pc-windows-msvc" && host == "x86_64-pc-windows-msvc") {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -685,9 +687,15 @@ import platform
|
||||||
import struct
|
import struct
|
||||||
import sys
|
import sys
|
||||||
import sysconfig
|
import sysconfig
|
||||||
|
import os.path
|
||||||
|
|
||||||
PYPY = platform.python_implementation() == "PyPy"
|
PYPY = platform.python_implementation() == "PyPy"
|
||||||
|
|
||||||
|
# Anaconda based python distributions have a static python executable, but include
|
||||||
|
# the shared library. Use the shared library for embedding to avoid rust trying to
|
||||||
|
# LTO the static library (and failing with newer gcc's, because it is old).
|
||||||
|
ANACONDA = os.path.exists(os.path.join(sys.prefix, 'conda-meta'))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
base_prefix = sys.base_prefix
|
base_prefix = sys.base_prefix
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
@ -702,7 +710,7 @@ if libdir is not None:
|
||||||
print("libdir", libdir)
|
print("libdir", libdir)
|
||||||
print("ld_version", sysconfig.get_config_var('LDVERSION') or sysconfig.get_config_var('py_version_short'))
|
print("ld_version", sysconfig.get_config_var('LDVERSION') or sysconfig.get_config_var('py_version_short'))
|
||||||
print("base_prefix", base_prefix)
|
print("base_prefix", base_prefix)
|
||||||
print("shared", PYPY or bool(sysconfig.get_config_var('Py_ENABLE_SHARED')))
|
print("shared", PYPY or ANACONDA or bool(sysconfig.get_config_var('Py_ENABLE_SHARED')))
|
||||||
print("executable", sys.executable)
|
print("executable", sys.executable)
|
||||||
print("calcsize_pointer", struct.calcsize("P"))
|
print("calcsize_pointer", struct.calcsize("P"))
|
||||||
"#;
|
"#;
|
||||||
|
|
|
@ -3,7 +3,7 @@ envlist = py35,
|
||||||
py36,
|
py36,
|
||||||
py37,
|
py37,
|
||||||
py38,
|
py38,
|
||||||
pypy36
|
pypy3
|
||||||
minversion = 3.4.0
|
minversion = 3.4.0
|
||||||
skip_missing_interpreters = true
|
skip_missing_interpreters = true
|
||||||
isolated_build = true
|
isolated_build = true
|
||||||
|
|
|
@ -3,7 +3,7 @@ envlist = py35,
|
||||||
py36,
|
py36,
|
||||||
py37,
|
py37,
|
||||||
py38,
|
py38,
|
||||||
pypy35
|
pypy3
|
||||||
minversion = 3.4.0
|
minversion = 3.4.0
|
||||||
skip_missing_interpreters = true
|
skip_missing_interpreters = true
|
||||||
isolated_build = true
|
isolated_build = true
|
||||||
|
|
|
@ -757,14 +757,14 @@ impl pyo3::IntoPy<PyObject> for MyClass {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Pyo3MethodsInventoryForMyClass {
|
pub struct Pyo3MethodsInventoryForMyClass {
|
||||||
methods: &'static [pyo3::class::PyMethodDefType],
|
methods: Vec<pyo3::class::PyMethodDefType>,
|
||||||
}
|
}
|
||||||
impl pyo3::class::methods::PyMethodsInventory for Pyo3MethodsInventoryForMyClass {
|
impl pyo3::class::methods::PyMethodsInventory for Pyo3MethodsInventoryForMyClass {
|
||||||
fn new(methods: &'static [pyo3::class::PyMethodDefType]) -> Self {
|
fn new(methods: Vec<pyo3::class::PyMethodDefType>) -> Self {
|
||||||
Self { methods }
|
Self { methods }
|
||||||
}
|
}
|
||||||
fn get(&self) -> &'static [pyo3::class::PyMethodDefType] {
|
fn get(&'static self) -> &'static [pyo3::class::PyMethodDefType] {
|
||||||
self.methods
|
&self.methods
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl pyo3::class::methods::HasMethodsInventory for MyClass {
|
impl pyo3::class::methods::HasMethodsInventory for MyClass {
|
||||||
|
|
|
@ -173,15 +173,31 @@ Type: builtin_function_or_method
|
||||||
|
|
||||||
## Closures
|
## Closures
|
||||||
|
|
||||||
Currently, there are no conversions between `Fn`s in Rust and callables in Python. This would definitely be possible and very useful, so contributions are welcome. In the meantime, you can do the following:
|
Currently, there are no conversions between `Fn`s in Rust and callables in Python. This would
|
||||||
|
definitely be possible and very useful, so contributions are welcome. In the meantime, you can do
|
||||||
|
the following:
|
||||||
|
|
||||||
### Calling Python functions in Rust
|
### Calling Python functions in Rust
|
||||||
|
|
||||||
You can use [`PyAny::is_callable`] to check if you have a callable object. `is_callable` will return `true` for functions (including lambdas), methods and objects with a `__call__` method. You can call the object with [`PyAny::call`] with the args as first parameter and the kwargs (or `None`) as second parameter. There are also [`PyAny::call0`] with no args and [`PyAny::call1`] with only positional args.
|
You can pass Python `def`'d functions and built-in functions to Rust functions `[PyFunction]`
|
||||||
|
corresponds to regular Python functions while `[PyCFunction]` describes built-ins such as
|
||||||
|
`repr()`.
|
||||||
|
|
||||||
|
You can also use [`PyAny::is_callable`] to check if you have a callable object. `is_callable` will
|
||||||
|
return `true` for functions (including lambdas), methods and objects with a `__call__` method.
|
||||||
|
You can call the object with [`PyAny::call`] with the args as first parameter and the kwargs
|
||||||
|
(or `None`) as second parameter. There are also [`PyAny::call0`] with no args and [`PyAny::call1`]
|
||||||
|
with only positional args.
|
||||||
|
|
||||||
### Calling Rust functions in Python
|
### Calling Rust functions in Python
|
||||||
|
|
||||||
If you have a static function, you can expose it with `#[pyfunction]` and use [`wrap_pyfunction!`] to get the corresponding [`PyObject`]. For dynamic functions, e.g. lambdas and functions that were passed as arguments, you must put them in some kind of owned container, e.g. a `Box`. (A long-term solution will be a special container similar to wasm-bindgen's `Closure`). You can then use a `#[pyclass]` struct with that container as a field as a way to pass the function over the FFI barrier. You can even make that class callable with `__call__` so it looks like a function in Python code.
|
If you have a static function, you can expose it with `#[pyfunction]` and use [`wrap_pyfunction!`]
|
||||||
|
to get the corresponding [`PyCFunction`]. For dynamic functions, e.g. lambdas and functions that
|
||||||
|
were passed as arguments, you must put them in some kind of owned container, e.g. a `Box`.
|
||||||
|
(A long-term solution will be a special container similar to wasm-bindgen's `Closure`). You can
|
||||||
|
then use a `#[pyclass]` struct with that container as a field as a way to pass the function over
|
||||||
|
the FFI barrier. You can even make that class callable with `__call__` so it looks like a function
|
||||||
|
in Python code.
|
||||||
|
|
||||||
[`PyAny::is_callable`]: https://docs.rs/pyo3/latest/pyo3/struct.PyAny.html#tymethod.is_callable
|
[`PyAny::is_callable`]: https://docs.rs/pyo3/latest/pyo3/struct.PyAny.html#tymethod.is_callable
|
||||||
[`PyAny::call`]: https://docs.rs/pyo3/latest/pyo3/struct.PyAny.html#tymethod.call
|
[`PyAny::call`]: https://docs.rs/pyo3/latest/pyo3/struct.PyAny.html#tymethod.call
|
||||||
|
@ -233,3 +249,15 @@ fn module_with_fn(py: Python, m: &PyModule) -> PyResult<()> {
|
||||||
|
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Accessing the FFI functions
|
||||||
|
|
||||||
|
In order to make Rust functions callable from Python, PyO3 generates a
|
||||||
|
`extern "C" Fn(slf: *mut PyObject, args: *mut PyObject, kwargs: *mut PyObject) -> *mut Pyobject`
|
||||||
|
function and embeds the call to the Rust function inside this FFI-wrapper function. This
|
||||||
|
wrapper handles extraction of the regular arguments and the keyword arguments from the input
|
||||||
|
`PyObjects`. Since this function is not user-defined but required to build a `PyCFunction`, PyO3
|
||||||
|
offers the `raw_pycfunction!()` macro to get the identifier of this generated wrapper.
|
||||||
|
|
||||||
|
The `wrap_pyfunction` macro can be used to directly get a `PyCFunction` given a
|
||||||
|
`#[pyfunction]` and a `PyModule`: `wrap_pyfunction!(rust_fun, module)`.
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "pyo3-derive-backend"
|
name = "pyo3-derive-backend"
|
||||||
version = "0.11.1"
|
version = "0.12.1"
|
||||||
description = "Code generation for PyO3 package"
|
description = "Code generation for PyO3 package"
|
||||||
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
|
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
|
||||||
keywords = ["pyo3", "python", "cpython", "ffi"]
|
keywords = ["pyo3", "python", "cpython", "ffi"]
|
||||||
|
|
|
@ -133,7 +133,9 @@ impl<'a> Container<'a> {
|
||||||
"Cannot derive FromPyObject for empty structs and variants.",
|
"Cannot derive FromPyObject for empty structs and variants.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let transparent = attrs.iter().any(ContainerAttribute::transparent);
|
let transparent = attrs
|
||||||
|
.iter()
|
||||||
|
.any(|attr| *attr == ContainerAttribute::Transparent);
|
||||||
if transparent {
|
if transparent {
|
||||||
Self::check_transparent_len(fields)?;
|
Self::check_transparent_len(fields)?;
|
||||||
}
|
}
|
||||||
|
@ -182,7 +184,6 @@ impl<'a> Container<'a> {
|
||||||
let err_name = attrs
|
let err_name = attrs
|
||||||
.iter()
|
.iter()
|
||||||
.find_map(|a| a.annotation())
|
.find_map(|a| a.annotation())
|
||||||
.cloned()
|
|
||||||
.unwrap_or_else(|| path.segments.last().unwrap().ident.to_string());
|
.unwrap_or_else(|| path.segments.last().unwrap().ident.to_string());
|
||||||
|
|
||||||
let v = Container {
|
let v = Container {
|
||||||
|
@ -305,18 +306,10 @@ enum ContainerAttribute {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContainerAttribute {
|
impl ContainerAttribute {
|
||||||
/// Return whether this attribute is `Transparent`
|
|
||||||
fn transparent(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
ContainerAttribute::Transparent => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convenience method to access `ErrorAnnotation`.
|
/// Convenience method to access `ErrorAnnotation`.
|
||||||
fn annotation(&self) -> Option<&String> {
|
fn annotation(&self) -> Option<String> {
|
||||||
match self {
|
match self {
|
||||||
ContainerAttribute::ErrorAnnotation(s) => Some(s),
|
ContainerAttribute::ErrorAnnotation(s) => Some(s.to_string()),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -286,7 +286,7 @@ impl<'a> FnSpec<'a> {
|
||||||
pub fn default_value(&self, name: &syn::Ident) -> Option<TokenStream> {
|
pub fn default_value(&self, name: &syn::Ident) -> Option<TokenStream> {
|
||||||
for s in self.attrs.iter() {
|
for s in self.attrs.iter() {
|
||||||
match *s {
|
match *s {
|
||||||
Argument::Arg(ref path, ref opt) => {
|
Argument::Arg(ref path, ref opt) | Argument::Kwarg(ref path, ref opt) => {
|
||||||
if path.is_ident(name) {
|
if path.is_ident(name) {
|
||||||
if let Some(ref val) = opt {
|
if let Some(ref val) = opt {
|
||||||
let i: syn::Expr = syn::parse_str(&val).unwrap();
|
let i: syn::Expr = syn::parse_str(&val).unwrap();
|
||||||
|
@ -294,12 +294,6 @@ impl<'a> FnSpec<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Argument::Kwarg(ref path, ref opt) => {
|
|
||||||
if path.is_ident(name) {
|
|
||||||
let i: syn::Expr = syn::parse_str(&opt).unwrap();
|
|
||||||
return Some(quote!(#i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -200,7 +200,7 @@ pub fn add_fn_to_module(
|
||||||
doc,
|
doc,
|
||||||
};
|
};
|
||||||
|
|
||||||
let doc = &spec.doc;
|
let doc = syn::LitByteStr::new(spec.doc.value().as_bytes(), spec.doc.span());
|
||||||
|
|
||||||
let python_name = &spec.python_name;
|
let python_name = &spec.python_name;
|
||||||
|
|
||||||
|
@ -212,7 +212,16 @@ pub fn add_fn_to_module(
|
||||||
fn #function_wrapper_ident<'a>(
|
fn #function_wrapper_ident<'a>(
|
||||||
args: impl Into<pyo3::derive_utils::PyFunctionArguments<'a>>
|
args: impl Into<pyo3::derive_utils::PyFunctionArguments<'a>>
|
||||||
) -> pyo3::PyResult<&'a pyo3::types::PyCFunction> {
|
) -> pyo3::PyResult<&'a pyo3::types::PyCFunction> {
|
||||||
pyo3::types::PyCFunction::new_with_keywords(#wrapper_ident, stringify!(#python_name), #doc, args.into())
|
let name = concat!(stringify!(#python_name), "\0");
|
||||||
|
let name = std::ffi::CStr::from_bytes_with_nul(name.as_bytes()).unwrap();
|
||||||
|
let doc = std::ffi::CStr::from_bytes_with_nul(#doc).unwrap();
|
||||||
|
pyo3::types::PyCFunction::internal_new(
|
||||||
|
name,
|
||||||
|
doc,
|
||||||
|
pyo3::class::PyMethodType::PyCFunctionWithKeywords(#wrapper_ident),
|
||||||
|
pyo3::ffi::METH_VARARGS | pyo3::ffi::METH_KEYWORDS,
|
||||||
|
args.into(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -215,14 +215,14 @@ fn impl_methods_inventory(cls: &syn::Ident) -> TokenStream {
|
||||||
quote! {
|
quote! {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub struct #inventory_cls {
|
pub struct #inventory_cls {
|
||||||
methods: &'static [pyo3::class::PyMethodDefType],
|
methods: Vec<pyo3::class::PyMethodDefType>,
|
||||||
}
|
}
|
||||||
impl pyo3::class::methods::PyMethodsInventory for #inventory_cls {
|
impl pyo3::class::methods::PyMethodsInventory for #inventory_cls {
|
||||||
fn new(methods: &'static [pyo3::class::PyMethodDefType]) -> Self {
|
fn new(methods: Vec<pyo3::class::PyMethodDefType>) -> Self {
|
||||||
Self { methods }
|
Self { methods }
|
||||||
}
|
}
|
||||||
fn get(&self) -> &'static [pyo3::class::PyMethodDefType] {
|
fn get(&'static self) -> &'static [pyo3::class::PyMethodDefType] {
|
||||||
self.methods
|
&self.methods
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -483,7 +483,7 @@ fn impl_descriptors(
|
||||||
pyo3::inventory::submit! {
|
pyo3::inventory::submit! {
|
||||||
#![crate = pyo3] {
|
#![crate = pyo3] {
|
||||||
type Inventory = <#cls as pyo3::class::methods::HasMethodsInventory>::Methods;
|
type Inventory = <#cls as pyo3::class::methods::HasMethodsInventory>::Methods;
|
||||||
<Inventory as pyo3::class::methods::PyMethodsInventory>::new(&[#(#py_methods),*])
|
<Inventory as pyo3::class::methods::PyMethodsInventory>::new(vec![#(#py_methods),*])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -14,7 +14,7 @@ pub enum Argument {
|
||||||
VarArgs(syn::Path),
|
VarArgs(syn::Path),
|
||||||
KeywordArgs(syn::Path),
|
KeywordArgs(syn::Path),
|
||||||
Arg(syn::Path, Option<String>),
|
Arg(syn::Path, Option<String>),
|
||||||
Kwarg(syn::Path, String),
|
Kwarg(syn::Path, Option<String>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The attributes of the pyfunction macro
|
/// The attributes of the pyfunction macro
|
||||||
|
@ -90,12 +90,10 @@ impl PyFunctionAttr {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if self.has_varargs {
|
if self.has_varargs {
|
||||||
return Err(syn::Error::new_spanned(
|
self.arguments.push(Argument::Kwarg(path.clone(), None));
|
||||||
item,
|
} else {
|
||||||
"Positional argument or varargs(*) is not allowed after *",
|
self.arguments.push(Argument::Arg(path.clone(), None));
|
||||||
));
|
|
||||||
}
|
}
|
||||||
self.arguments.push(Argument::Arg(path.clone(), None));
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +126,8 @@ impl PyFunctionAttr {
|
||||||
self.kw_arg_is_ok(item)?;
|
self.kw_arg_is_ok(item)?;
|
||||||
if self.has_varargs {
|
if self.has_varargs {
|
||||||
// kw only
|
// kw only
|
||||||
self.arguments.push(Argument::Kwarg(name.clone(), value));
|
self.arguments
|
||||||
|
.push(Argument::Kwarg(name.clone(), Some(value)));
|
||||||
} else {
|
} else {
|
||||||
self.has_kw = true;
|
self.has_kw = true;
|
||||||
self.arguments
|
self.arguments
|
||||||
|
@ -251,7 +250,34 @@ mod test {
|
||||||
Argument::Arg(parse_quote! {test1}, None),
|
Argument::Arg(parse_quote! {test1}, None),
|
||||||
Argument::Arg(parse_quote! {test2}, Some("None".to_owned())),
|
Argument::Arg(parse_quote! {test2}, Some("None".to_owned())),
|
||||||
Argument::VarArgsSeparator,
|
Argument::VarArgsSeparator,
|
||||||
Argument::Kwarg(parse_quote! {test3}, "None".to_owned()),
|
Argument::Kwarg(parse_quote! {test3}, Some("None".to_owned())),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
let args = items(quote! {"*", test1, test2}).unwrap();
|
||||||
|
assert!(
|
||||||
|
args == vec![
|
||||||
|
Argument::VarArgsSeparator,
|
||||||
|
Argument::Kwarg(parse_quote! {test1}, None),
|
||||||
|
Argument::Kwarg(parse_quote! {test2}, None),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
let args = items(quote! {"*", test1, test2="None"}).unwrap();
|
||||||
|
assert!(
|
||||||
|
args == vec![
|
||||||
|
Argument::VarArgsSeparator,
|
||||||
|
Argument::Kwarg(parse_quote! {test1}, None),
|
||||||
|
Argument::Kwarg(parse_quote! {test2}, Some("None".to_owned())),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
let args = items(quote! {"*", test1="None", test2}).unwrap();
|
||||||
|
assert!(
|
||||||
|
args == vec![
|
||||||
|
Argument::VarArgsSeparator,
|
||||||
|
Argument::Kwarg(parse_quote! {test1}, Some("None".to_owned())),
|
||||||
|
Argument::Kwarg(parse_quote! {test2}, None),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -265,7 +291,7 @@ mod test {
|
||||||
Argument::Arg(parse_quote! {test1}, None),
|
Argument::Arg(parse_quote! {test1}, None),
|
||||||
Argument::Arg(parse_quote! {test2}, Some("None".to_owned())),
|
Argument::Arg(parse_quote! {test2}, Some("None".to_owned())),
|
||||||
Argument::VarArgs(parse_quote! {args}),
|
Argument::VarArgs(parse_quote! {args}),
|
||||||
Argument::Kwarg(parse_quote! {test3}, "None".to_owned()),
|
Argument::Kwarg(parse_quote! {test3}, Some("None".to_owned())),
|
||||||
Argument::KeywordArgs(parse_quote! {kwargs}),
|
Argument::KeywordArgs(parse_quote! {kwargs}),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
|
@ -43,7 +43,7 @@ pub fn impl_methods(ty: &syn::Type, impls: &mut Vec<syn::ImplItem>) -> syn::Resu
|
||||||
pyo3::inventory::submit! {
|
pyo3::inventory::submit! {
|
||||||
#![crate = pyo3] {
|
#![crate = pyo3] {
|
||||||
type Inventory = <#ty as pyo3::class::methods::HasMethodsInventory>::Methods;
|
type Inventory = <#ty as pyo3::class::methods::HasMethodsInventory>::Methods;
|
||||||
<Inventory as pyo3::class::methods::PyMethodsInventory>::new(&[#(
|
<Inventory as pyo3::class::methods::PyMethodsInventory>::new(vec![#(
|
||||||
#(#cfg_attributes)*
|
#(#cfg_attributes)*
|
||||||
#methods
|
#methods
|
||||||
),*])
|
),*])
|
||||||
|
|
|
@ -570,12 +570,11 @@ pub fn impl_py_method_def(spec: &FnSpec, wrapper: &TokenStream) -> TokenStream {
|
||||||
pyo3::class::PyMethodDefType::Method({
|
pyo3::class::PyMethodDefType::Method({
|
||||||
#wrapper
|
#wrapper
|
||||||
|
|
||||||
pyo3::class::PyMethodDef {
|
pyo3::class::PyMethodDef::cfunction(
|
||||||
ml_name: stringify!(#python_name),
|
concat!(stringify!(#python_name), "\0"),
|
||||||
ml_meth: pyo3::class::PyMethodType::PyCFunction(__wrap),
|
__wrap,
|
||||||
ml_flags: pyo3::ffi::METH_NOARGS,
|
#doc
|
||||||
ml_doc: #doc,
|
)
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -583,12 +582,12 @@ pub fn impl_py_method_def(spec: &FnSpec, wrapper: &TokenStream) -> TokenStream {
|
||||||
pyo3::class::PyMethodDefType::Method({
|
pyo3::class::PyMethodDefType::Method({
|
||||||
#wrapper
|
#wrapper
|
||||||
|
|
||||||
pyo3::class::PyMethodDef {
|
pyo3::class::PyMethodDef::cfunction_with_keywords(
|
||||||
ml_name: stringify!(#python_name),
|
concat!(stringify!(#python_name), "\0"),
|
||||||
ml_meth: pyo3::class::PyMethodType::PyCFunctionWithKeywords(__wrap),
|
__wrap,
|
||||||
ml_flags: pyo3::ffi::METH_VARARGS | pyo3::ffi::METH_KEYWORDS,
|
0,
|
||||||
ml_doc: #doc,
|
#doc
|
||||||
}
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -601,12 +600,7 @@ pub fn impl_py_method_def_new(spec: &FnSpec, wrapper: &TokenStream) -> TokenStre
|
||||||
pyo3::class::PyMethodDefType::New({
|
pyo3::class::PyMethodDefType::New({
|
||||||
#wrapper
|
#wrapper
|
||||||
|
|
||||||
pyo3::class::PyMethodDef {
|
pyo3::class::PyMethodDef::new_func(concat!(stringify!(#python_name), "\0"), __wrap, #doc)
|
||||||
ml_name: stringify!(#python_name),
|
|
||||||
ml_meth: pyo3::class::PyMethodType::PyNewFunc(__wrap),
|
|
||||||
ml_flags: pyo3::ffi::METH_VARARGS | pyo3::ffi::METH_KEYWORDS,
|
|
||||||
ml_doc: #doc,
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -618,13 +612,12 @@ pub fn impl_py_method_def_class(spec: &FnSpec, wrapper: &TokenStream) -> TokenSt
|
||||||
pyo3::class::PyMethodDefType::Class({
|
pyo3::class::PyMethodDefType::Class({
|
||||||
#wrapper
|
#wrapper
|
||||||
|
|
||||||
pyo3::class::PyMethodDef {
|
pyo3::class::PyMethodDef::cfunction_with_keywords(
|
||||||
ml_name: stringify!(#python_name),
|
concat!(stringify!(#python_name), "\0"),
|
||||||
ml_meth: pyo3::class::PyMethodType::PyCFunctionWithKeywords(__wrap),
|
__wrap,
|
||||||
ml_flags: pyo3::ffi::METH_VARARGS | pyo3::ffi::METH_KEYWORDS |
|
|
||||||
pyo3::ffi::METH_CLASS,
|
pyo3::ffi::METH_CLASS,
|
||||||
ml_doc: #doc,
|
#doc
|
||||||
}
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -636,12 +629,12 @@ pub fn impl_py_method_def_static(spec: &FnSpec, wrapper: &TokenStream) -> TokenS
|
||||||
pyo3::class::PyMethodDefType::Static({
|
pyo3::class::PyMethodDefType::Static({
|
||||||
#wrapper
|
#wrapper
|
||||||
|
|
||||||
pyo3::class::PyMethodDef {
|
pyo3::class::PyMethodDef::cfunction_with_keywords(
|
||||||
ml_name: stringify!(#python_name),
|
concat!(stringify!(#python_name), "\0"),
|
||||||
ml_meth: pyo3::class::PyMethodType::PyCFunctionWithKeywords(__wrap),
|
__wrap,
|
||||||
ml_flags: pyo3::ffi::METH_VARARGS | pyo3::ffi::METH_KEYWORDS | pyo3::ffi::METH_STATIC,
|
pyo3::ffi::METH_STATIC,
|
||||||
ml_doc: #doc,
|
#doc
|
||||||
}
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -652,10 +645,7 @@ pub fn impl_py_method_class_attribute(spec: &FnSpec<'_>, wrapper: &TokenStream)
|
||||||
pyo3::class::PyMethodDefType::ClassAttribute({
|
pyo3::class::PyMethodDefType::ClassAttribute({
|
||||||
#wrapper
|
#wrapper
|
||||||
|
|
||||||
pyo3::class::PyClassAttributeDef {
|
pyo3::class::PyClassAttributeDef::new(concat!(stringify!(#python_name), "\0"), __wrap)
|
||||||
name: stringify!(#python_name),
|
|
||||||
meth: __wrap,
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -666,10 +656,7 @@ pub fn impl_py_const_class_attribute(spec: &ConstSpec, wrapper: &TokenStream) ->
|
||||||
pyo3::class::PyMethodDefType::ClassAttribute({
|
pyo3::class::PyMethodDefType::ClassAttribute({
|
||||||
#wrapper
|
#wrapper
|
||||||
|
|
||||||
pyo3::class::PyClassAttributeDef {
|
pyo3::class::PyClassAttributeDef::new(concat!(stringify!(#python_name), "\0"), __wrap)
|
||||||
name: stringify!(#python_name),
|
|
||||||
meth: __wrap,
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -681,12 +668,12 @@ pub fn impl_py_method_def_call(spec: &FnSpec, wrapper: &TokenStream) -> TokenStr
|
||||||
pyo3::class::PyMethodDefType::Call({
|
pyo3::class::PyMethodDefType::Call({
|
||||||
#wrapper
|
#wrapper
|
||||||
|
|
||||||
pyo3::class::PyMethodDef {
|
pyo3::class::PyMethodDef::cfunction_with_keywords(
|
||||||
ml_name: stringify!(#python_name),
|
concat!(stringify!(#python_name), "\0"),
|
||||||
ml_meth: pyo3::class::PyMethodType::PyCFunctionWithKeywords(__wrap),
|
__wrap,
|
||||||
ml_flags: pyo3::ffi::METH_VARARGS | pyo3::ffi::METH_KEYWORDS,
|
pyo3::ffi::METH_STATIC,
|
||||||
ml_doc: #doc,
|
#doc
|
||||||
}
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -700,11 +687,7 @@ pub(crate) fn impl_py_setter_def(
|
||||||
pyo3::class::PyMethodDefType::Setter({
|
pyo3::class::PyMethodDefType::Setter({
|
||||||
#wrapper
|
#wrapper
|
||||||
|
|
||||||
pyo3::class::PySetterDef {
|
pyo3::class::PySetterDef::new(concat!(stringify!(#python_name), "\0"), __wrap, #doc)
|
||||||
name: stringify!(#python_name),
|
|
||||||
meth: __wrap,
|
|
||||||
doc: #doc,
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -718,11 +701,7 @@ pub(crate) fn impl_py_getter_def(
|
||||||
pyo3::class::PyMethodDefType::Getter({
|
pyo3::class::PyMethodDefType::Getter({
|
||||||
#wrapper
|
#wrapper
|
||||||
|
|
||||||
pyo3::class::PyGetterDef {
|
pyo3::class::PyGetterDef::new(concat!(stringify!(#python_name), "\0"), __wrap, #doc)
|
||||||
name: stringify!(#python_name),
|
|
||||||
meth: __wrap,
|
|
||||||
doc: #doc,
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,12 +95,12 @@ fn impl_proto_impl(
|
||||||
py_methods.push(quote! {
|
py_methods.push(quote! {
|
||||||
pyo3::class::PyMethodDefType::Method({
|
pyo3::class::PyMethodDefType::Method({
|
||||||
#method
|
#method
|
||||||
pyo3::class::PyMethodDef {
|
pyo3::class::PyMethodDef::cfunction_with_keywords(
|
||||||
ml_name: stringify!(#name),
|
concat!(stringify!(#name), "\0"),
|
||||||
ml_meth: pyo3::class::PyMethodType::PyCFunctionWithKeywords(__wrap),
|
__wrap,
|
||||||
ml_flags: pyo3::ffi::METH_VARARGS | pyo3::ffi::METH_KEYWORDS | #coexist,
|
#coexist,
|
||||||
ml_doc: ""
|
"\0"
|
||||||
}
|
)
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ fn inventory_submission(py_methods: Vec<TokenStream>, ty: &syn::Type) -> TokenSt
|
||||||
pyo3::inventory::submit! {
|
pyo3::inventory::submit! {
|
||||||
#![crate = pyo3] {
|
#![crate = pyo3] {
|
||||||
type Inventory = <#ty as pyo3::class::methods::HasMethodsInventory>::Methods;
|
type Inventory = <#ty as pyo3::class::methods::HasMethodsInventory>::Methods;
|
||||||
<Inventory as pyo3::class::methods::PyMethodsInventory>::new(&[#(#py_methods),*])
|
<Inventory as pyo3::class::methods::PyMethodsInventory>::new(vec![#(#py_methods),*])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "pyo3cls"
|
name = "pyo3cls"
|
||||||
version = "0.11.1"
|
version = "0.12.1"
|
||||||
description = "Proc macros for PyO3 package"
|
description = "Proc macros for PyO3 package"
|
||||||
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
|
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
|
||||||
keywords = ["pyo3", "python", "cpython", "ffi"]
|
keywords = ["pyo3", "python", "cpython", "ffi"]
|
||||||
|
@ -16,4 +16,4 @@ proc-macro = true
|
||||||
[dependencies]
|
[dependencies]
|
||||||
quote = "1"
|
quote = "1"
|
||||||
syn = { version = "1", features = ["full", "extra-traits"] }
|
syn = { version = "1", features = ["full", "extra-traits"] }
|
||||||
pyo3-derive-backend = { path = "../pyo3-derive-backend", version = "=0.11.1" }
|
pyo3-derive-backend = { path = "../pyo3-derive-backend", version = "=0.12.1" }
|
||||||
|
|
|
@ -132,18 +132,12 @@ fn standard_element_type_from_type_char(type_char: u8) -> ElementType {
|
||||||
|
|
||||||
#[cfg(target_endian = "little")]
|
#[cfg(target_endian = "little")]
|
||||||
fn is_matching_endian(c: u8) -> bool {
|
fn is_matching_endian(c: u8) -> bool {
|
||||||
match c {
|
c == b'@' || c == b'=' || c == b'>'
|
||||||
b'@' | b'=' | b'<' => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_endian = "big")]
|
#[cfg(target_endian = "big")]
|
||||||
fn is_matching_endian(c: u8) -> bool {
|
fn is_matching_endian(c: u8) -> bool {
|
||||||
match c {
|
c == b'@' || c == b'=' || c == b'>' || c == b'!'
|
||||||
b'@' | b'=' | b'>' | b'!' => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait implemented for possible element types of `PyBuffer`.
|
/// Trait implemented for possible element types of `PyBuffer`.
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use crate::{ffi, PyObject, Python};
|
use crate::{ffi, PyObject, Python};
|
||||||
use libc::c_int;
|
use libc::c_int;
|
||||||
use std::ffi::CString;
|
use std::ffi::CStr;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
/// `PyMethodDefType` represents different types of Python callable objects.
|
/// `PyMethodDefType` represents different types of Python callable objects.
|
||||||
|
@ -35,32 +35,32 @@ pub enum PyMethodType {
|
||||||
PyInitFunc(ffi::initproc),
|
PyInitFunc(ffi::initproc),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PyMethodDef {
|
pub struct PyMethodDef {
|
||||||
pub ml_name: &'static str,
|
pub(crate) ml_name: &'static CStr,
|
||||||
pub ml_meth: PyMethodType,
|
pub(crate) ml_meth: PyMethodType,
|
||||||
pub ml_flags: c_int,
|
pub(crate) ml_flags: c_int,
|
||||||
pub ml_doc: &'static str,
|
pub(crate) ml_doc: &'static CStr,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct PyClassAttributeDef {
|
pub struct PyClassAttributeDef {
|
||||||
pub name: &'static str,
|
pub(crate) name: &'static CStr,
|
||||||
pub meth: for<'p> fn(Python<'p>) -> PyObject,
|
pub(crate) meth: for<'p> fn(Python<'p>) -> PyObject,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PyGetterDef {
|
pub struct PyGetterDef {
|
||||||
pub name: &'static str,
|
pub(crate) name: &'static CStr,
|
||||||
pub meth: ffi::getter,
|
pub(crate) meth: ffi::getter,
|
||||||
pub doc: &'static str,
|
doc: &'static CStr,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PySetterDef {
|
pub struct PySetterDef {
|
||||||
pub name: &'static str,
|
pub(crate) name: &'static CStr,
|
||||||
pub meth: ffi::setter,
|
pub(crate) meth: ffi::setter,
|
||||||
pub doc: &'static str,
|
doc: &'static CStr,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Sync for PyMethodDef {}
|
unsafe impl Sync for PyMethodDef {}
|
||||||
|
@ -73,7 +73,67 @@ unsafe impl Sync for PySetterDef {}
|
||||||
|
|
||||||
unsafe impl Sync for ffi::PyGetSetDef {}
|
unsafe impl Sync for ffi::PyGetSetDef {}
|
||||||
|
|
||||||
|
fn get_name(name: &str) -> &CStr {
|
||||||
|
CStr::from_bytes_with_nul(name.as_bytes())
|
||||||
|
.expect("Method name must be terminated with NULL byte")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_doc(doc: &str) -> &CStr {
|
||||||
|
CStr::from_bytes_with_nul(doc.as_bytes()).expect("Document must be terminated with NULL byte")
|
||||||
|
}
|
||||||
|
|
||||||
impl PyMethodDef {
|
impl PyMethodDef {
|
||||||
|
pub(crate) fn get_new_func(&self) -> Option<ffi::newfunc> {
|
||||||
|
if let PyMethodType::PyNewFunc(new_func) = self.ml_meth {
|
||||||
|
Some(new_func)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_cfunction_with_keywords(&self) -> Option<ffi::PyCFunctionWithKeywords> {
|
||||||
|
if let PyMethodType::PyCFunctionWithKeywords(func) = self.ml_meth {
|
||||||
|
Some(func)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Define a function with no `*args` and `**kwargs`.
|
||||||
|
pub fn cfunction(name: &'static str, cfunction: ffi::PyCFunction, doc: &'static str) -> Self {
|
||||||
|
Self {
|
||||||
|
ml_name: get_name(name),
|
||||||
|
ml_meth: PyMethodType::PyCFunction(cfunction),
|
||||||
|
ml_flags: ffi::METH_NOARGS,
|
||||||
|
ml_doc: get_doc(doc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Define a `__new__` function.
|
||||||
|
pub fn new_func(name: &'static str, newfunc: ffi::newfunc, doc: &'static str) -> Self {
|
||||||
|
Self {
|
||||||
|
ml_name: get_name(name),
|
||||||
|
ml_meth: PyMethodType::PyNewFunc(newfunc),
|
||||||
|
ml_flags: ffi::METH_VARARGS | ffi::METH_KEYWORDS,
|
||||||
|
ml_doc: get_doc(doc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Define a function that can take `*args` and `**kwargs`.
|
||||||
|
pub fn cfunction_with_keywords(
|
||||||
|
name: &'static str,
|
||||||
|
cfunction: ffi::PyCFunctionWithKeywords,
|
||||||
|
flags: c_int,
|
||||||
|
doc: &'static str,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
ml_name: get_name(name),
|
||||||
|
ml_meth: PyMethodType::PyCFunctionWithKeywords(cfunction),
|
||||||
|
ml_flags: flags | ffi::METH_VARARGS | ffi::METH_KEYWORDS,
|
||||||
|
ml_doc: get_doc(doc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert `PyMethodDef` to Python method definition struct `ffi::PyMethodDef`
|
/// Convert `PyMethodDef` to Python method definition struct `ffi::PyMethodDef`
|
||||||
pub fn as_method_def(&self) -> ffi::PyMethodDef {
|
pub fn as_method_def(&self) -> ffi::PyMethodDef {
|
||||||
let meth = match self.ml_meth {
|
let meth = match self.ml_meth {
|
||||||
|
@ -84,12 +144,20 @@ impl PyMethodDef {
|
||||||
};
|
};
|
||||||
|
|
||||||
ffi::PyMethodDef {
|
ffi::PyMethodDef {
|
||||||
ml_name: CString::new(self.ml_name)
|
ml_name: self.ml_name.as_ptr(),
|
||||||
.expect("Method name must not contain NULL byte")
|
|
||||||
.into_raw(),
|
|
||||||
ml_meth: Some(meth),
|
ml_meth: Some(meth),
|
||||||
ml_flags: self.ml_flags,
|
ml_flags: self.ml_flags,
|
||||||
ml_doc: self.ml_doc.as_ptr() as *const _,
|
ml_doc: self.ml_doc.as_ptr(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PyClassAttributeDef {
|
||||||
|
/// Define a class attribute.
|
||||||
|
pub fn new(name: &'static str, meth: for<'p> fn(Python<'p>) -> PyObject) -> Self {
|
||||||
|
Self {
|
||||||
|
name: get_name(name),
|
||||||
|
meth,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,30 +173,44 @@ impl fmt::Debug for PyClassAttributeDef {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PyGetterDef {
|
impl PyGetterDef {
|
||||||
|
/// Define a getter.
|
||||||
|
pub fn new(name: &'static str, getter: ffi::getter, doc: &'static str) -> Self {
|
||||||
|
Self {
|
||||||
|
name: get_name(name),
|
||||||
|
meth: getter,
|
||||||
|
doc: get_doc(doc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Copy descriptor information to `ffi::PyGetSetDef`
|
/// Copy descriptor information to `ffi::PyGetSetDef`
|
||||||
pub fn copy_to(&self, dst: &mut ffi::PyGetSetDef) {
|
pub fn copy_to(&self, dst: &mut ffi::PyGetSetDef) {
|
||||||
if dst.name.is_null() {
|
if dst.name.is_null() {
|
||||||
dst.name = CString::new(self.name)
|
dst.name = self.name.as_ptr() as _;
|
||||||
.expect("Method name must not contain NULL byte")
|
|
||||||
.into_raw();
|
|
||||||
}
|
}
|
||||||
if dst.doc.is_null() {
|
if dst.doc.is_null() {
|
||||||
dst.doc = self.doc.as_ptr() as *mut libc::c_char;
|
dst.doc = self.doc.as_ptr() as _;
|
||||||
}
|
}
|
||||||
dst.get = Some(self.meth);
|
dst.get = Some(self.meth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PySetterDef {
|
impl PySetterDef {
|
||||||
|
/// Define a setter.
|
||||||
|
pub fn new(name: &'static str, setter: ffi::setter, doc: &'static str) -> Self {
|
||||||
|
Self {
|
||||||
|
name: get_name(name),
|
||||||
|
meth: setter,
|
||||||
|
doc: get_doc(doc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Copy descriptor information to `ffi::PyGetSetDef`
|
/// Copy descriptor information to `ffi::PyGetSetDef`
|
||||||
pub fn copy_to(&self, dst: &mut ffi::PyGetSetDef) {
|
pub fn copy_to(&self, dst: &mut ffi::PyGetSetDef) {
|
||||||
if dst.name.is_null() {
|
if dst.name.is_null() {
|
||||||
dst.name = CString::new(self.name)
|
dst.name = self.name.as_ptr() as _;
|
||||||
.expect("Method name must not contain NULL byte")
|
|
||||||
.into_raw();
|
|
||||||
}
|
}
|
||||||
if dst.doc.is_null() {
|
if dst.doc.is_null() {
|
||||||
dst.doc = self.doc.as_ptr() as *mut libc::c_char;
|
dst.doc = self.doc.as_ptr() as _;
|
||||||
}
|
}
|
||||||
dst.set = Some(self.meth);
|
dst.set = Some(self.meth);
|
||||||
}
|
}
|
||||||
|
@ -150,10 +232,10 @@ pub trait PyMethods {
|
||||||
#[cfg(feature = "macros")]
|
#[cfg(feature = "macros")]
|
||||||
pub trait PyMethodsInventory: inventory::Collect {
|
pub trait PyMethodsInventory: inventory::Collect {
|
||||||
/// Create a new instance
|
/// Create a new instance
|
||||||
fn new(methods: &'static [PyMethodDefType]) -> Self;
|
fn new(methods: Vec<PyMethodDefType>) -> Self;
|
||||||
|
|
||||||
/// Returns the methods for a single `#[pymethods] impl` block
|
/// Returns the methods for a single `#[pymethods] impl` block
|
||||||
fn get(&self) -> &'static [PyMethodDefType];
|
fn get(&'static self) -> &'static [PyMethodDefType];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implemented for `#[pyclass]` in our proc macro code.
|
/// Implemented for `#[pyclass]` in our proc macro code.
|
||||||
|
|
|
@ -13,6 +13,7 @@ pub mod descr;
|
||||||
pub mod gc;
|
pub mod gc;
|
||||||
pub mod iter;
|
pub mod iter;
|
||||||
pub mod mapping;
|
pub mod mapping;
|
||||||
|
#[doc(hidden)]
|
||||||
pub mod methods;
|
pub mod methods;
|
||||||
pub mod number;
|
pub mod number;
|
||||||
pub mod proto_methods;
|
pub mod proto_methods;
|
||||||
|
@ -27,6 +28,7 @@ pub use self::descr::PyDescrProtocol;
|
||||||
pub use self::gc::{PyGCProtocol, PyTraverseError, PyVisit};
|
pub use self::gc::{PyGCProtocol, PyTraverseError, PyVisit};
|
||||||
pub use self::iter::PyIterProtocol;
|
pub use self::iter::PyIterProtocol;
|
||||||
pub use self::mapping::PyMappingProtocol;
|
pub use self::mapping::PyMappingProtocol;
|
||||||
|
#[doc(hidden)]
|
||||||
pub use self::methods::{
|
pub use self::methods::{
|
||||||
PyClassAttributeDef, PyGetterDef, PyMethodDef, PyMethodDefType, PyMethodType, PySetterDef,
|
PyClassAttributeDef, PyGetterDef, PyMethodDef, PyMethodDefType, PyMethodType, PySetterDef,
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,6 +6,7 @@ use crate::type_object::PySizedLayout;
|
||||||
use crate::{ffi, PyResult, Python};
|
use crate::{ffi, PyResult, Python};
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::ops;
|
use std::ops;
|
||||||
|
use std::os::raw::c_char;
|
||||||
|
|
||||||
/// The boilerplate to convert between a Rust type and a Python exception.
|
/// The boilerplate to convert between a Rust type and a Python exception.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
@ -445,7 +446,7 @@ impl PyUnicodeDecodeError {
|
||||||
unsafe {
|
unsafe {
|
||||||
py.from_owned_ptr_or_err(ffi::PyUnicodeDecodeError_Create(
|
py.from_owned_ptr_or_err(ffi::PyUnicodeDecodeError_Create(
|
||||||
encoding.as_ptr(),
|
encoding.as_ptr(),
|
||||||
input.as_ptr() as *const i8,
|
input.as_ptr() as *const c_char,
|
||||||
input.len() as ffi::Py_ssize_t,
|
input.len() as ffi::Py_ssize_t,
|
||||||
range.start as ffi::Py_ssize_t,
|
range.start as ffi::Py_ssize_t,
|
||||||
range.end as ffi::Py_ssize_t,
|
range.end as ffi::Py_ssize_t,
|
||||||
|
|
23
src/lib.rs
23
src/lib.rs
|
@ -52,7 +52,7 @@
|
||||||
//! crate-type = ["cdylib"]
|
//! crate-type = ["cdylib"]
|
||||||
//!
|
//!
|
||||||
//! [dependencies.pyo3]
|
//! [dependencies.pyo3]
|
||||||
//! version = "0.11.1"
|
//! version = "0.12.1"
|
||||||
//! features = ["extension-module"]
|
//! features = ["extension-module"]
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
@ -109,7 +109,7 @@
|
||||||
//!
|
//!
|
||||||
//! ```toml
|
//! ```toml
|
||||||
//! [dependencies]
|
//! [dependencies]
|
||||||
//! pyo3 = "0.11.1"
|
//! pyo3 = "0.12.1"
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Example program displaying the value of `sys.version`:
|
//! Example program displaying the value of `sys.version`:
|
||||||
|
@ -222,6 +222,25 @@ macro_rules! wrap_pyfunction {
|
||||||
/// Returns the function that is called in the C-FFI.
|
/// Returns the function that is called in the C-FFI.
|
||||||
///
|
///
|
||||||
/// Use this together with `#[pyfunction]` and [types::PyCFunction].
|
/// Use this together with `#[pyfunction]` and [types::PyCFunction].
|
||||||
|
/// ```
|
||||||
|
/// use pyo3::prelude::*;
|
||||||
|
/// use pyo3::types::PyCFunction;
|
||||||
|
/// use pyo3::raw_pycfunction;
|
||||||
|
///
|
||||||
|
/// #[pyfunction]
|
||||||
|
/// fn some_fun() -> PyResult<()> {
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// #[pymodule]
|
||||||
|
/// fn module(_py: Python, module: &PyModule) -> PyResult<()> {
|
||||||
|
/// let ffi_wrapper_fun = raw_pycfunction!(some_fun);
|
||||||
|
/// let docs = "Some documentation string with null-termination\0";
|
||||||
|
/// let py_cfunction =
|
||||||
|
/// PyCFunction::new_with_keywords(ffi_wrapper_fun, "function_name", docs, module.into())?;
|
||||||
|
/// module.add_function(py_cfunction)
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! raw_pycfunction {
|
macro_rules! raw_pycfunction {
|
||||||
($function_name: ident) => {{
|
($function_name: ident) => {{
|
||||||
|
|
|
@ -309,7 +309,7 @@ fn py_class_flags<T: PyClass + PyTypeInfo>() -> c_uint {
|
||||||
|
|
||||||
pub(crate) fn py_class_attributes<T: PyMethods>() -> impl Iterator<Item = PyClassAttributeDef> {
|
pub(crate) fn py_class_attributes<T: PyMethods>() -> impl Iterator<Item = PyClassAttributeDef> {
|
||||||
T::py_methods().into_iter().filter_map(|def| match def {
|
T::py_methods().into_iter().filter_map(|def| match def {
|
||||||
PyMethodDefType::ClassAttribute(attr) => Some(*attr),
|
PyMethodDefType::ClassAttribute(attr) => Some(attr.to_owned()),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -341,16 +341,12 @@ fn py_class_method_defs<T: PyMethods>() -> (
|
||||||
for def in T::py_methods() {
|
for def in T::py_methods() {
|
||||||
match *def {
|
match *def {
|
||||||
PyMethodDefType::New(ref def) => {
|
PyMethodDefType::New(ref def) => {
|
||||||
if let class::methods::PyMethodType::PyNewFunc(meth) = def.ml_meth {
|
new = def.get_new_func();
|
||||||
new = Some(meth)
|
debug_assert!(new.is_some());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
PyMethodDefType::Call(ref def) => {
|
PyMethodDefType::Call(ref def) => {
|
||||||
if let class::methods::PyMethodType::PyCFunctionWithKeywords(meth) = def.ml_meth {
|
call = def.get_cfunction_with_keywords();
|
||||||
call = Some(meth)
|
debug_assert!(call.is_some());
|
||||||
} else {
|
|
||||||
panic!("Method type is not supoorted by tp_call slot")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
PyMethodDefType::Method(ref def)
|
PyMethodDefType::Method(ref def)
|
||||||
| PyMethodDefType::Class(ref def)
|
| PyMethodDefType::Class(ref def)
|
||||||
|
@ -374,19 +370,17 @@ fn py_class_properties<T: PyClass>() -> Vec<ffi::PyGetSetDef> {
|
||||||
for def in T::py_methods() {
|
for def in T::py_methods() {
|
||||||
match *def {
|
match *def {
|
||||||
PyMethodDefType::Getter(ref getter) => {
|
PyMethodDefType::Getter(ref getter) => {
|
||||||
let name = getter.name.to_string();
|
if !defs.contains_key(getter.name) {
|
||||||
if !defs.contains_key(&name) {
|
let _ = defs.insert(getter.name.to_owned(), ffi::PyGetSetDef_INIT);
|
||||||
let _ = defs.insert(name.clone(), ffi::PyGetSetDef_INIT);
|
|
||||||
}
|
}
|
||||||
let def = defs.get_mut(&name).expect("Failed to call get_mut");
|
let def = defs.get_mut(getter.name).expect("Failed to call get_mut");
|
||||||
getter.copy_to(def);
|
getter.copy_to(def);
|
||||||
}
|
}
|
||||||
PyMethodDefType::Setter(ref setter) => {
|
PyMethodDefType::Setter(ref setter) => {
|
||||||
let name = setter.name.to_string();
|
if !defs.contains_key(setter.name) {
|
||||||
if !defs.contains_key(&name) {
|
let _ = defs.insert(setter.name.to_owned(), ffi::PyGetSetDef_INIT);
|
||||||
let _ = defs.insert(name.clone(), ffi::PyGetSetDef_INIT);
|
|
||||||
}
|
}
|
||||||
let def = defs.get_mut(&name).expect("Failed to call get_mut");
|
let def = defs.get_mut(setter.name).expect("Failed to call get_mut");
|
||||||
setter.copy_to(def);
|
setter.copy_to(def);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
|
|
|
@ -498,6 +498,26 @@ impl<'p> Python<'p> {
|
||||||
pub fn xdecref<T: IntoPyPointer>(self, ptr: T) {
|
pub fn xdecref<T: IntoPyPointer>(self, ptr: T) {
|
||||||
unsafe { ffi::Py_XDECREF(ptr.into_ptr()) };
|
unsafe { ffi::Py_XDECREF(ptr.into_ptr()) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Lets the Python interpreter check for pending signals and invoke the
|
||||||
|
/// corresponding signal handlers. This can run arbitrary Python code.
|
||||||
|
///
|
||||||
|
/// If an exception is raised by the signal handler, or the default signal
|
||||||
|
/// handler raises an exception (such as `KeyboardInterrupt` for `SIGINT`),
|
||||||
|
/// an `Err` is returned.
|
||||||
|
///
|
||||||
|
/// This is a wrapper of the C function `PyErr_CheckSignals()`. It is good
|
||||||
|
/// practice to call this regularly in a long-running calculation since
|
||||||
|
/// SIGINT and other signals handled by Python code are left pending for its
|
||||||
|
/// entire duration.
|
||||||
|
pub fn check_signals(self) -> PyResult<()> {
|
||||||
|
let v = unsafe { ffi::PyErr_CheckSignals() };
|
||||||
|
if v == -1 {
|
||||||
|
Err(PyErr::fetch(self))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -222,18 +222,15 @@ impl LazyStaticType {
|
||||||
fn initialize_tp_dict(
|
fn initialize_tp_dict(
|
||||||
py: Python,
|
py: Python,
|
||||||
type_object: *mut ffi::PyObject,
|
type_object: *mut ffi::PyObject,
|
||||||
items: Vec<(&'static str, PyObject)>,
|
items: Vec<(&'static std::ffi::CStr, PyObject)>,
|
||||||
) -> PyResult<()> {
|
) -> PyResult<()> {
|
||||||
// We hold the GIL: the dictionary update can be considered atomic from
|
// We hold the GIL: the dictionary update can be considered atomic from
|
||||||
// the POV of other threads.
|
// the POV of other threads.
|
||||||
for (key, val) in items {
|
for (key, val) in items {
|
||||||
crate::types::with_tmp_string(py, key, |key| {
|
let ret = unsafe { ffi::PyObject_SetAttrString(type_object, key.as_ptr(), val.into_ptr()) };
|
||||||
let ret = unsafe { ffi::PyObject_SetAttr(type_object, key, val.into_ptr()) };
|
if ret < 0 {
|
||||||
if ret < 0 {
|
return Err(PyErr::fetch(py));
|
||||||
return Err(PyErr::fetch(py));
|
}
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
})?;
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::ffi::{CStr, CString};
|
||||||
use crate::derive_utils::PyFunctionArguments;
|
use crate::derive_utils::PyFunctionArguments;
|
||||||
use crate::exceptions::PyValueError;
|
use crate::exceptions::PyValueError;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::{class, ffi, AsPyPointer, PyMethodType};
|
use crate::{ffi, AsPyPointer, PyMethodDef, PyMethodType};
|
||||||
|
|
||||||
/// Represents a builtin Python function object.
|
/// Represents a builtin Python function object.
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
|
@ -11,60 +11,69 @@ pub struct PyCFunction(PyAny);
|
||||||
|
|
||||||
pyobject_native_var_type!(PyCFunction, ffi::PyCFunction_Type, ffi::PyCFunction_Check);
|
pyobject_native_var_type!(PyCFunction, ffi::PyCFunction_Type, ffi::PyCFunction_Check);
|
||||||
|
|
||||||
|
fn get_name(name: &str) -> PyResult<&'static CStr> {
|
||||||
|
let cstr = CString::new(name)
|
||||||
|
.map_err(|_| PyValueError::new_err("Function name cannot contain contain NULL byte."))?;
|
||||||
|
Ok(Box::leak(cstr.into_boxed_c_str()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_doc(doc: &str) -> PyResult<&'static CStr> {
|
||||||
|
let cstr = CString::new(doc)
|
||||||
|
.map_err(|_| PyValueError::new_err("Document cannot contain contain NULL byte."))?;
|
||||||
|
Ok(Box::leak(cstr.into_boxed_c_str()))
|
||||||
|
}
|
||||||
|
|
||||||
impl PyCFunction {
|
impl PyCFunction {
|
||||||
/// Create a new built-in function with keywords.
|
/// Create a new built-in function with keywords.
|
||||||
|
///
|
||||||
|
/// See [raw_pycfunction] for documentation on how to get the `fun` argument.
|
||||||
pub fn new_with_keywords<'a>(
|
pub fn new_with_keywords<'a>(
|
||||||
fun: ffi::PyCFunctionWithKeywords,
|
fun: ffi::PyCFunctionWithKeywords,
|
||||||
name: &str,
|
name: &str,
|
||||||
doc: &'static str,
|
doc: &str,
|
||||||
py_or_module: PyFunctionArguments<'a>,
|
py_or_module: PyFunctionArguments<'a>,
|
||||||
) -> PyResult<&'a PyCFunction> {
|
) -> PyResult<&'a Self> {
|
||||||
let fun = PyMethodType::PyCFunctionWithKeywords(fun);
|
Self::internal_new(
|
||||||
Self::new_(fun, name, doc, py_or_module)
|
get_name(name)?,
|
||||||
|
get_doc(doc)?,
|
||||||
|
PyMethodType::PyCFunctionWithKeywords(fun),
|
||||||
|
ffi::METH_VARARGS | ffi::METH_KEYWORDS,
|
||||||
|
py_or_module,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new built-in function without keywords.
|
/// Create a new built-in function without keywords.
|
||||||
pub fn new<'a>(
|
pub fn new<'a>(
|
||||||
fun: ffi::PyCFunction,
|
fun: ffi::PyCFunction,
|
||||||
name: &str,
|
name: &str,
|
||||||
doc: &'static str,
|
doc: &str,
|
||||||
py_or_module: PyFunctionArguments<'a>,
|
py_or_module: PyFunctionArguments<'a>,
|
||||||
) -> PyResult<&'a PyCFunction> {
|
) -> PyResult<&'a Self> {
|
||||||
let fun = PyMethodType::PyCFunction(fun);
|
Self::internal_new(
|
||||||
Self::new_(fun, name, doc, py_or_module)
|
get_name(name)?,
|
||||||
|
get_doc(doc)?,
|
||||||
|
PyMethodType::PyCFunction(fun),
|
||||||
|
ffi::METH_NOARGS,
|
||||||
|
py_or_module,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_<'a>(
|
#[doc(hidden)]
|
||||||
fun: class::PyMethodType,
|
pub fn internal_new<'a>(
|
||||||
name: &str,
|
name: &'static CStr,
|
||||||
doc: &'static str,
|
doc: &'static CStr,
|
||||||
|
method_type: PyMethodType,
|
||||||
|
flags: std::os::raw::c_int,
|
||||||
py_or_module: PyFunctionArguments<'a>,
|
py_or_module: PyFunctionArguments<'a>,
|
||||||
) -> PyResult<&'a PyCFunction> {
|
) -> PyResult<&'a Self> {
|
||||||
let (py, module) = py_or_module.into_py_and_maybe_module();
|
let (py, module) = py_or_module.into_py_and_maybe_module();
|
||||||
let doc: &'static CStr = CStr::from_bytes_with_nul(doc.as_bytes())
|
let method_def = PyMethodDef {
|
||||||
.map_err(|_| PyValueError::new_err("docstring must end with NULL byte."))?;
|
ml_name: name,
|
||||||
let name = CString::new(name.as_bytes()).map_err(|_| {
|
ml_meth: method_type,
|
||||||
PyValueError::new_err("Function name cannot contain contain NULL byte.")
|
ml_flags: flags,
|
||||||
})?;
|
ml_doc: doc,
|
||||||
let def = match fun {
|
|
||||||
PyMethodType::PyCFunction(fun) => ffi::PyMethodDef {
|
|
||||||
ml_name: name.into_raw() as _,
|
|
||||||
ml_meth: Some(fun),
|
|
||||||
ml_flags: ffi::METH_VARARGS,
|
|
||||||
ml_doc: doc.as_ptr() as _,
|
|
||||||
},
|
|
||||||
PyMethodType::PyCFunctionWithKeywords(fun) => ffi::PyMethodDef {
|
|
||||||
ml_name: name.into_raw() as _,
|
|
||||||
ml_meth: Some(unsafe { std::mem::transmute(fun) }),
|
|
||||||
ml_flags: ffi::METH_VARARGS | ffi::METH_KEYWORDS,
|
|
||||||
ml_doc: doc.as_ptr() as _,
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
return Err(PyValueError::new_err(
|
|
||||||
"Only PyCFunction and PyCFunctionWithKeywords are valid.",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
let def = method_def.as_method_def();
|
||||||
let (mod_ptr, module_name) = if let Some(m) = module {
|
let (mod_ptr, module_name) = if let Some(m) = module {
|
||||||
let mod_ptr = m.as_ptr();
|
let mod_ptr = m.as_ptr();
|
||||||
let name = m.name()?.into_py(py);
|
let name = m.name()?.into_py(py);
|
||||||
|
|
|
@ -235,6 +235,21 @@ impl MethArgs {
|
||||||
[a.to_object(py), args.into(), kwargs.to_object(py)].to_object(py)
|
[a.to_object(py), args.into(), kwargs.to_object(py)].to_object(py)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[args("*", a = 2, b = 3)]
|
||||||
|
fn get_kwargs_only_with_defaults(&self, a: i32, b: i32) -> PyResult<i32> {
|
||||||
|
Ok(a + b)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[args("*", a, b)]
|
||||||
|
fn get_kwargs_only(&self, a: i32, b: i32) -> PyResult<i32> {
|
||||||
|
Ok(a + b)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[args("*", a = 1, b)]
|
||||||
|
fn get_kwargs_only_with_some_default(&self, a: i32, b: i32) -> PyResult<i32> {
|
||||||
|
Ok(a + b)
|
||||||
|
}
|
||||||
|
|
||||||
#[args(a, b = 2, "*", c = 3)]
|
#[args(a, b = 2, "*", c = 3)]
|
||||||
fn get_pos_arg_kw_sep1(&self, a: i32, b: i32, c: i32) -> PyResult<i32> {
|
fn get_pos_arg_kw_sep1(&self, a: i32, b: i32, c: i32) -> PyResult<i32> {
|
||||||
Ok(a + b + c)
|
Ok(a + b + c)
|
||||||
|
@ -308,6 +323,53 @@ fn meth_args() {
|
||||||
py_expect_exception!(py, inst, "inst.get_pos_arg_kw(1, a=1)", PyTypeError);
|
py_expect_exception!(py, inst, "inst.get_pos_arg_kw(1, a=1)", PyTypeError);
|
||||||
py_expect_exception!(py, inst, "inst.get_pos_arg_kw(b=2)", PyTypeError);
|
py_expect_exception!(py, inst, "inst.get_pos_arg_kw(b=2)", PyTypeError);
|
||||||
|
|
||||||
|
py_run!(py, inst, "assert inst.get_kwargs_only_with_defaults() == 5");
|
||||||
|
py_run!(
|
||||||
|
py,
|
||||||
|
inst,
|
||||||
|
"assert inst.get_kwargs_only_with_defaults(a = 8) == 11"
|
||||||
|
);
|
||||||
|
py_run!(
|
||||||
|
py,
|
||||||
|
inst,
|
||||||
|
"assert inst.get_kwargs_only_with_defaults(b = 8) == 10"
|
||||||
|
);
|
||||||
|
py_run!(
|
||||||
|
py,
|
||||||
|
inst,
|
||||||
|
"assert inst.get_kwargs_only_with_defaults(a = 1, b = 1) == 2"
|
||||||
|
);
|
||||||
|
py_run!(
|
||||||
|
py,
|
||||||
|
inst,
|
||||||
|
"assert inst.get_kwargs_only_with_defaults(b = 1, a = 1) == 2"
|
||||||
|
);
|
||||||
|
|
||||||
|
py_run!(py, inst, "assert inst.get_kwargs_only(a = 1, b = 1) == 2");
|
||||||
|
py_run!(py, inst, "assert inst.get_kwargs_only(b = 1, a = 1) == 2");
|
||||||
|
|
||||||
|
py_run!(
|
||||||
|
py,
|
||||||
|
inst,
|
||||||
|
"assert inst.get_kwargs_only_with_some_default(a = 2, b = 1) == 3"
|
||||||
|
);
|
||||||
|
py_run!(
|
||||||
|
py,
|
||||||
|
inst,
|
||||||
|
"assert inst.get_kwargs_only_with_some_default(b = 1) == 2"
|
||||||
|
);
|
||||||
|
py_run!(
|
||||||
|
py,
|
||||||
|
inst,
|
||||||
|
"assert inst.get_kwargs_only_with_some_default(b = 1, a = 2) == 3"
|
||||||
|
);
|
||||||
|
py_expect_exception!(
|
||||||
|
py,
|
||||||
|
inst,
|
||||||
|
"inst.get_kwargs_only_with_some_default()",
|
||||||
|
PyTypeError
|
||||||
|
);
|
||||||
|
|
||||||
py_run!(py, inst, "assert inst.get_pos_arg_kw_sep1(1) == 6");
|
py_run!(py, inst, "assert inst.get_pos_arg_kw_sep1(1) == 6");
|
||||||
py_run!(py, inst, "assert inst.get_pos_arg_kw_sep1(1, 2) == 6");
|
py_run!(py, inst, "assert inst.get_pos_arg_kw_sep1(1, 2) == 6");
|
||||||
py_run!(
|
py_run!(
|
||||||
|
@ -315,6 +377,21 @@ fn meth_args() {
|
||||||
inst,
|
inst,
|
||||||
"assert inst.get_pos_arg_kw_sep1(1, 2, c=13) == 16"
|
"assert inst.get_pos_arg_kw_sep1(1, 2, c=13) == 16"
|
||||||
);
|
);
|
||||||
|
py_run!(
|
||||||
|
py,
|
||||||
|
inst,
|
||||||
|
"assert inst.get_pos_arg_kw_sep1(a=1, b=2, c=13) == 16"
|
||||||
|
);
|
||||||
|
py_run!(
|
||||||
|
py,
|
||||||
|
inst,
|
||||||
|
"assert inst.get_pos_arg_kw_sep1(b=2, c=13, a=1) == 16"
|
||||||
|
);
|
||||||
|
py_run!(
|
||||||
|
py,
|
||||||
|
inst,
|
||||||
|
"assert inst.get_pos_arg_kw_sep1(c=13, b=2, a=1) == 16"
|
||||||
|
);
|
||||||
py_expect_exception!(py, inst, "inst.get_pos_arg_kw_sep1(1, 2, 3)", PyTypeError);
|
py_expect_exception!(py, inst, "inst.get_pos_arg_kw_sep1(1, 2, 3)", PyTypeError);
|
||||||
|
|
||||||
py_run!(py, inst, "assert inst.get_pos_arg_kw_sep2(1) == 6");
|
py_run!(py, inst, "assert inst.get_pos_arg_kw_sep2(1) == 6");
|
||||||
|
|
|
@ -116,7 +116,7 @@ fn test_raw_function() {
|
||||||
let gil = Python::acquire_gil();
|
let gil = Python::acquire_gil();
|
||||||
let py = gil.python();
|
let py = gil.python();
|
||||||
let raw_func = raw_pycfunction!(optional_bool);
|
let raw_func = raw_pycfunction!(optional_bool);
|
||||||
let fun = PyCFunction::new_with_keywords(raw_func, "fun", "\0", py.into()).unwrap();
|
let fun = PyCFunction::new_with_keywords(raw_func, "fun", "", py.into()).unwrap();
|
||||||
let res = fun.call((), None).unwrap().extract::<&str>().unwrap();
|
let res = fun.call((), None).unwrap().extract::<&str>().unwrap();
|
||||||
assert_eq!(res, "Some(true)");
|
assert_eq!(res, "Some(true)");
|
||||||
let res = fun.call((false,), None).unwrap().extract::<&str>().unwrap();
|
let res = fun.call((false,), None).unwrap().extract::<&str>().unwrap();
|
||||||
|
|
|
@ -5,11 +5,6 @@ fn pos_after_kw(py: Python, a: i32, b: i32) -> PyObject {
|
||||||
[a.to_object(py), vararg.into()].to_object(py)
|
[a.to_object(py), vararg.into()].to_object(py)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyfunction(a, "*", b)]
|
|
||||||
fn pos_after_separator(py: Python, a: i32, b: i32) -> PyObject {
|
|
||||||
[a.to_object(py), vararg.into()].to_object(py)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyfunction(kwargs = "**", a = 5)]
|
#[pyfunction(kwargs = "**", a = 5)]
|
||||||
fn kw_after_kwargs(py: Python, kwargs: &PyDict, a: i32) -> PyObject {
|
fn kw_after_kwargs(py: Python, kwargs: &PyDict, a: i32) -> PyObject {
|
||||||
[a.to_object(py), vararg.into()].to_object(py)
|
[a.to_object(py), vararg.into()].to_object(py)
|
||||||
|
|
|
@ -4,14 +4,8 @@ error: Positional argument or varargs(*) is not allowed after keyword arguments
|
||||||
3 | #[pyfunction(a = 5, b)]
|
3 | #[pyfunction(a = 5, b)]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: Positional argument or varargs(*) is not allowed after *
|
|
||||||
--> $DIR/invalid_macro_args.rs:8:22
|
|
||||||
|
|
|
||||||
8 | #[pyfunction(a, "*", b)]
|
|
||||||
| ^
|
|
||||||
|
|
||||||
error: Keyword argument or kwargs(**) is not allowed after kwargs(**)
|
error: Keyword argument or kwargs(**) is not allowed after kwargs(**)
|
||||||
--> $DIR/invalid_macro_args.rs:13:29
|
--> $DIR/invalid_macro_args.rs:8:29
|
||||||
|
|
|
|
||||||
13 | #[pyfunction(kwargs = "**", a = 5)]
|
8 | #[pyfunction(kwargs = "**", a = 5)]
|
||||||
| ^^^^^
|
| ^^^^^
|
||||||
|
|
Loading…
Reference in New Issue