Search code examples
rustpyo3

Tell if object isinstance of PyDatetime or Pydate?


Here's my main.rs file:

use pyo3::prelude::*;
use pyo3::types::{PyDateTime, PyDate};

fn main() -> () {
    Python::with_gil(|py| {
        let datetime = py.import("datetime").unwrap();
        let my_datetime = datetime.call_method1("datetime", (2022, 3, 28, 0, 0, 0, 0)).unwrap();
        println!("isinstance pydatetime: {:?}", my_datetime.is_instance_of::<PyDateTime>().unwrap());
        println!("isinstance pydate: {:?}", my_datetime.is_instance_of::<PyDate>().unwrap());

        let my_date = datetime.call_method1("date", (2022, 3, 28)).unwrap();
        println!("isinstance pydatetime: {:?}", my_date.is_instance_of::<PyDateTime>().unwrap());
        println!("isinstance pydate: {:?}", my_date.is_instance_of::<PyDate>().unwrap());
    })
}

Here's my Cargo.toml file:

[package]
name = "tmp"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
pyo3 = { version = "0.18.0", features = ["auto-initialize"] }

If I run the above, I get

isinstance pydatetime: true
isinstance pydate: true
isinstance pydatetime: true
isinstance pydate: true

I find this a bit odd, because running in Python directly, I get:

In [27]: import datetime as dt

In [28]: isinstance(my_datetime, dt.datetime)
Out[28]: True

In [29]: isinstance(my_datetime, dt.date)
Out[29]: True

In [30]: isinstance(my_date, dt.datetime)
Out[30]: False

In [31]: isinstance(my_date, dt.date)
Out[31]: True

So, I would have expected

my_date.is_instance_of::<PyDateTime>().unwrap()

to also be false

Why is that not the case, and how can I distinguish between whether an object is datetime or date without resorting to more hacky methods like checking dt.get_type().name()?


Solution

  • I've submitted a pull request to PyO3 that should fix this: https://github.com/PyO3/pyo3/pull/3071

    diff --git a/src/types/datetime.rs b/src/types/datetime.rs
    index 85860728568..2c995356e2c 100644
    --- a/src/types/datetime.rs
    +++ b/src/types/datetime.rs
    @@ -228,7 +228,7 @@ pub struct PyDateTime(PyAny);
     pyobject_native_type!(
         PyDateTime,
         crate::ffi::PyDateTime_DateTime,
    -    *ensure_datetime_api(Python::assume_gil_acquired()).DateType,
    +    *ensure_datetime_api(Python::assume_gil_acquired()).DateTimeType,
         #module=Some("datetime"),
         #checkfunction=PyDateTime_Check
     );