Skip to content

Commit 363bb55

Browse files
committed
Properly coerce fractions as int
1 parent d523cf5 commit 363bb55

File tree

2 files changed

+26
-0
lines changed

2 files changed

+26
-0
lines changed

src/input/input_python.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::str::from_utf8;
33
use pyo3::intern;
44
use pyo3::prelude::*;
55

6+
use pyo3::sync::GILOnceCell;
67
use pyo3::types::PyType;
78
use pyo3::types::{
89
PyBool, PyByteArray, PyBytes, PyComplex, PyDate, PyDateTime, PyDict, PyFloat, PyFrozenSet, PyInt, PyIterator,
@@ -45,6 +46,20 @@ use super::{
4546
Input,
4647
};
4748

49+
static FRACTION_TYPE: GILOnceCell<Py<PyType>> = GILOnceCell::new();
50+
51+
pub fn get_fraction_type(py: Python) -> &Bound<'_, PyType> {
52+
FRACTION_TYPE
53+
.get_or_init(py, || {
54+
py.import("fractions")
55+
.and_then(|fractions_module| fractions_module.getattr("Fraction"))
56+
.unwrap()
57+
.extract()
58+
.unwrap()
59+
})
60+
.bind(py)
61+
}
62+
4863
pub(crate) fn downcast_python_input<'py, T: PyTypeCheck>(input: &(impl Input<'py> + ?Sized)) -> Option<&Bound<'py, T>> {
4964
input.as_python().and_then(|any| any.downcast::<T>().ok())
5065
}
@@ -269,6 +284,15 @@ impl<'py> Input<'py> for Bound<'py, PyAny> {
269284
float_as_int(self, self.extract::<f64>()?)
270285
} else if let Ok(decimal) = self.validate_decimal(true, self.py()) {
271286
decimal_as_int(self, &decimal.into_inner())
287+
} else if self.is_instance(get_fraction_type(self.py()))? {
288+
#[cfg(Py_3_11)]
289+
let as_int = self.call_method0("__int__");
290+
#[cfg(not(Py_3_11))]
291+
let as_int = self.call_method0("__trunc__");
292+
match as_int {
293+
Ok(i) => Ok(EitherInt::Py(i.as_any().to_owned())),
294+
Err(_) => break 'lax,
295+
}
272296
} else if let Ok(float) = self.extract::<f64>() {
273297
float_as_int(self, float)
274298
} else if let Some(enum_val) = maybe_as_enum(self) {

tests/validators/test_int.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import json
22
import re
33
from decimal import Decimal
4+
from fractions import Fraction
45
from typing import Any
56

67
import pytest
@@ -93,6 +94,7 @@ def test_constraints_schema_validation() -> None:
9394
(i64_max, i64_max),
9495
(i64_max + 1, i64_max + 1),
9596
(i64_max * 2, i64_max * 2),
97+
(Fraction(10_935_244_710_974_505), 10_935_244_710_974_505), # https://github.com/pydantic/pydantic/issues/12063
9698
pytest.param(
9799
12.5,
98100
Err('Input should be a valid integer, got a number with a fractional part [type=int_from_float'),

0 commit comments

Comments
 (0)