-
Notifications
You must be signed in to change notification settings - Fork 930
Closed
Description
Say I have the following C++ Date class. Using pybind11 I can easily wrap it to be used in Python:
/* date.hpp */
#include <string>
class Date
{
public:
int year, month, day;
Date(int year, int month, int day);
Date(const std::string& yyyymmdd);
Date add_days(int N) const; // Returns new date with N days added
bool is_weekend() const; // Returns true if date is Sat or Sun
};/* date_bindings.cpp */
#include <pybind11/pybind11.h>
#include <date.hpp>
namespace py = pybind11;
PYBIND11_MODULE(date_py, m) {
py::class_<Date>(m, "Date")
.def(py::init<int, int, int>(), py::arg("year"), py::arg("month"), py::arg("day"))
.def(py::init<const std::string&>(), py::arg("yyyymmdd"))
.def("add_days", &Date::add_days, py::arg("N"))
.def("is_weekend", &Date::is_weekend)
.def_readwrite("year", &Date::year)
.def_readwrite("month", &Date::month)
.def_readwrite("day", &Date::day);
}That's 15 lines of code for my bindings using pybind11.
Now, I want to do the same in Rust. So I implemented my Date class and then used PyO3 to wrap it:
/* date.rs */
#[derive(Debug)]
pub struct Date {
pub year: u16,
pub month: u8,
pub day: u8
}
impl Date {
pub fn from_ymd(year: u16, month: u8, day: u8) -> Date {
// ...
}
pub fn from_str(s: &str) -> Date {
// ...
}
pub const fn add_days(&self, N: u8) -> Self {
// ...
}
pub const fn is_weekend(&self) -> bool {
// ...
}
}/* date_bindings.rs */
use pyo3::prelude::*;
use my_library::Date;
#[pyclass(name = "Date")]
struct DatePy {
inner: Date
}
#[pymethods]
impl DatePy {
#[new]
fn new(year: u16, month: u8, day: u8) -> Self {
DatePy{inner: Date::from_ymd(year, month, day)}
}
fn add_days(&self, N: u8) -> PyResult<Self> {
Ok(DatePy{inner: self._date.add_days(N)})
}
fn is_weekend(&self) -> PyResult<bool> {
Ok(self.inner.is_weekend())
}
#[getter]
fn get_year(&self) -> PyResult<u16> {
Ok(self.inner.year)
}
#[getter]
fn get_month(&self) -> PyResult<u8> {
Ok(self.inner.month)
}
#[getter]
fn get_day(&self) -> PyResult<u8> {
Ok(self.inner.day)
}
#[setter]
fn set_year(&mut self, year: u16) -> PyResult<()> {
self.inner.year = year;
Ok(())
}
#[setter]
fn set_month(&mut self, month: u8) -> PyResult<()> {
self.inner.month = month;
Ok(())
}
#[setter]
fn set_day(&mut self, day: u8) -> PyResult<()> {
self.inner.day = day;
Ok(())
}
}
#[pymodule]
fn date_py(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<DatePy>()?;
Ok(())
}I've noticed that the PyO3 bindings contain a lot of boilerplate, and hence they are much longer compared to pybind11:
- 15 lines with pybind11
- 54 lines with PyO3
Questions:
- Am I using PyO3 correctly? Are there ways to shorten the code to achieve a similar level of "conciseness" as is the case for pybind11?
- Is there is a way to create PyO3 bindings without the
DatePyhelper struct? Again, ideally similar to pybind where I don't need to manually create any helper structs. - How can I add two (or more) constructors such as
#[new] fn new(year: u16, month: u8, day: u8)and#[new] fn new(yyyymmdd: &str)similar to pybind11? RIght now, I am only able to expose 1 constructor with PyO3.
Question originates from Correct & concise way to use PyO3 Bindings (similar to pybind11), where a commenter suggested to raise an issue on the PyO3 GitHub.
Metadata
Metadata
Assignees
Labels
No labels