cpython/objects/
num.rs

1// Copyright (c) 2015 Daniel Grunwald
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy of this
4// software and associated documentation files (the "Software"), to deal in the Software
5// without restriction, including without limitation the rights to use, copy, modify, merge,
6// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7// to whom the Software is furnished to do so, subject to the following conditions:
8//
9// The above copyright notice and this permission notice shall be included in all copies or
10// substantial portions of the Software.
11//
12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17// DEALINGS IN THE SOFTWARE.
18
19#![allow(clippy::float_cmp)]
20
21use libc::{c_double, c_long};
22use num_traits::cast::cast;
23
24use super::exc;
25use super::object::PyObject;
26use crate::conversion::{FromPyObject, ToPyObject};
27use crate::err::{self, PyErr, PyResult};
28use crate::ffi;
29use crate::python::{PyClone, PyDrop, Python, PythonObject};
30
31/// Represents a Python `int` object.
32///
33/// Note that in Python 2.x, `int` and `long` are different types.
34/// When rust-cpython is compiled for Python 3.x,
35/// `PyInt` and `PyLong` are aliases for the same type, which
36/// corresponds to a Python `int`.
37///
38/// You can usually avoid directly working with this type
39/// by using [ToPyObject](trait.ToPyObject.html)
40/// and [extract](struct.PyObject.html#method.extract)
41/// with the primitive Rust integer types.
42#[cfg(feature = "python27-sys")]
43pub struct PyInt(PyObject);
44#[cfg(feature = "python27-sys")]
45pyobject_newtype!(PyInt, PyInt_Check, PyInt_Type);
46
47/// In Python 2.x, represents a Python `long` object.
48/// In Python 3.x, represents a Python `int` object.
49/// Both `PyInt` and `PyLong` refer to the same type on Python 3.x.
50///
51/// You can usually avoid directly working with this type
52/// by using [ToPyObject](trait.ToPyObject.html)
53/// and [extract](struct.PyObject.html#method.extract)
54/// with the primitive Rust integer types.
55pub struct PyLong(PyObject);
56pyobject_newtype!(PyLong, PyLong_Check, PyLong_Type);
57
58/// Represents a Python `float` object.
59///
60/// You can usually avoid directly working with this type
61/// by using [ToPyObject](trait.ToPyObject.html)
62/// and [extract](struct.PyObject.html#method.extract)
63/// with `f32`/`f64`.
64pub struct PyFloat(PyObject);
65pyobject_newtype!(PyFloat, PyFloat_Check, PyFloat_Type);
66
67#[cfg(feature = "python27-sys")]
68impl PyInt {
69    /// Creates a new Python 2.7 `int` object.
70    ///
71    /// Note: you might want to call `val.to_py_object(py)` instead
72    /// to avoid truncation if the value does not fit into a `c_long`,
73    /// and to make your code compatible with Python 3.x.
74    pub fn new(py: Python, val: c_long) -> PyInt {
75        unsafe { err::cast_from_owned_ptr_or_panic(py, ffi::PyInt_FromLong(val)) }
76    }
77
78    /// Gets the value of this integer.
79    ///
80    /// Warning: `PyInt::value()` is only supported for Python 2.7 `int` objects,
81    /// but not for `long` objects.
82    /// In almost all cases, you can avoid the distinction between these types
83    /// by simply calling `obj.extract::<i32>(py)`.
84    pub fn value(&self, _py: Python) -> c_long {
85        unsafe { ffi::PyInt_AS_LONG(self.0.as_ptr()) }
86    }
87}
88
89impl PyFloat {
90    /// Creates a new Python `float` object.
91    pub fn new(py: Python, val: c_double) -> PyFloat {
92        unsafe { err::cast_from_owned_ptr_or_panic(py, ffi::PyFloat_FromDouble(val)) }
93    }
94
95    /// Gets the value of this float.
96    pub fn value(&self, _py: Python) -> c_double {
97        unsafe { ffi::PyFloat_AsDouble(self.0.as_ptr()) }
98    }
99}
100
101macro_rules! int_fits_c_long(
102    ($rust_type:ty) => (
103        /// Conversion of Rust integer to Python `int`.
104        #[cfg(feature="python27-sys")]
105        impl ToPyObject for $rust_type {
106            type ObjectType = PyInt;
107
108            fn to_py_object(&self, py: Python) -> PyInt {
109                unsafe {
110                    err::cast_from_owned_ptr_or_panic(py,
111                        ffi::PyInt_FromLong(*self as c_long))
112                }
113            }
114        }
115
116        /// Conversion of Rust integer to Python `int`.
117        #[cfg(feature="python3-sys")]
118        impl ToPyObject for $rust_type {
119            type ObjectType = PyLong;
120
121            fn to_py_object(&self, py: Python) -> PyLong {
122                unsafe {
123                    err::cast_from_owned_ptr_or_panic(py,
124                        ffi::PyLong_FromLong(*self as c_long))
125                }
126            }
127        }
128
129        extract!(
130            obj to $rust_type;
131            /// Converts Python integers to Rust integers.
132            ///
133            /// Returns OverflowError if the input integer does not fit the Rust type;
134            /// or TypeError if the input is not an integer.
135            py => {
136                let ptr = obj.as_ptr();
137                let val;
138                unsafe {
139                    if ffi::PyLong_Check(ptr) != 0 {
140                        val = ffi::PyLong_AsLong(obj.as_ptr());
141                    } else {
142                        let num = err::result_from_owned_ptr(py, ffi::PyNumber_Index(ptr))?;
143                        val = ffi::PyLong_AsLong(num.as_ptr());
144                        num.release_ref(py);
145                    }
146                };
147                if val == -1 && PyErr::occurred(py) {
148                    return Err(PyErr::fetch(py));
149                }
150                match cast::<c_long, $rust_type>(val) {
151                    Some(v) => Ok(v),
152                    None => Err(overflow_error(py))
153                }
154            }
155        );
156    )
157);
158
159macro_rules! int_fits_larger_int(
160    ($rust_type:ty, $larger_type:ty) => (
161        /// Conversion of Rust integer to Python `int`.
162        /// On Python 2.x, may also result in a `long` if the value does not fit into a Python `int`.
163        impl ToPyObject for $rust_type {
164            type ObjectType = <$larger_type as ToPyObject>::ObjectType;
165
166            #[inline]
167            fn to_py_object(&self, py: Python) -> <$larger_type as ToPyObject>::ObjectType {
168                (*self as $larger_type).to_py_object(py)
169            }
170        }
171
172        extract!(
173            obj to $rust_type;
174            /// Converts Python integers to Rust integers.
175            ///
176            /// Returns OverflowError if the input integer does not fit the Rust type;
177            /// or TypeError if the input is not an integer.
178            py => {
179                let val = obj.extract::<$larger_type>(py)?;
180                match cast::<$larger_type, $rust_type>(val) {
181                    Some(v) => Ok(v),
182                    None => Err(overflow_error(py))
183                }
184            }
185        );
186    )
187);
188
189fn err_if_invalid_value<T: PartialEq>(
190    py: Python,
191    invalid_value: T,
192    actual_value: T,
193) -> PyResult<T> {
194    if actual_value == invalid_value && PyErr::occurred(py) {
195        Err(PyErr::fetch(py))
196    } else {
197        Ok(actual_value)
198    }
199}
200
201macro_rules! int_convert_u64_or_i64 (
202    ($rust_type:ty, $pylong_from_ll_or_ull:expr, $pylong_as_ull_or_ull:expr) => (
203        /// Conversion of Rust integer to Python `int`.
204        /// On Python 2.x, may also result in a `long` if the value does not fit into a Python `int`.
205        impl <'p> ToPyObject for $rust_type {
206            #[cfg(feature="python27-sys")]
207            type ObjectType = PyObject;
208
209            #[cfg(feature="python3-sys")]
210            type ObjectType = PyLong;
211
212            #[cfg(feature="python27-sys")]
213            fn to_py_object(&self, py: Python) -> PyObject {
214                unsafe {
215                    let ptr = match cast::<$rust_type, c_long>(*self) {
216                        Some(v) => ffi::PyInt_FromLong(v),
217                        None => $pylong_from_ll_or_ull(*self)
218                    };
219                    err::from_owned_ptr_or_panic(py, ptr)
220                }
221            }
222
223            #[cfg(feature="python3-sys")]
224            fn to_py_object(&self, py: Python) -> PyLong {
225                unsafe {
226                    err::cast_from_owned_ptr_or_panic(py, $pylong_from_ll_or_ull(*self))
227                }
228            }
229        }
230
231        /// Converts Python integers to Rust integers.
232        ///
233        /// Returns OverflowError if the input integer does not fit the Rust type;
234        /// or TypeError if the input is not an integer.
235        impl <'s> FromPyObject<'s> for $rust_type {
236            #[cfg(feature="python27-sys")]
237            fn extract(py: Python, obj: &'s PyObject) -> PyResult<$rust_type> {
238                let ptr = obj.as_ptr();
239
240                unsafe {
241                    if ffi::PyLong_Check(ptr) != 0 {
242                        err_if_invalid_value(py, !0, $pylong_as_ull_or_ull(ptr))
243                    } else if ffi::PyInt_Check(ptr) != 0 {
244                        match cast::<c_long, $rust_type>(ffi::PyInt_AS_LONG(ptr)) {
245                            Some(v) => Ok(v),
246                            None => Err(overflow_error(py))
247                        }
248                    } else {
249                        let num = err::result_from_owned_ptr(py, ffi::PyNumber_Index(ptr))?;
250                        let res = err_if_invalid_value(py, !0, $pylong_as_ull_or_ull(num.as_ptr()));
251                        num.release_ref(py);
252                        res
253                    }
254                }
255            }
256
257            #[cfg(feature="python3-sys")]
258            fn extract(py: Python, obj: &'s PyObject) -> PyResult<$rust_type> {
259                let ptr = obj.as_ptr();
260                unsafe {
261                    if ffi::PyLong_Check(ptr) != 0 {
262                        err_if_invalid_value(py, !0, $pylong_as_ull_or_ull(ptr))
263                    } else {
264                        let num = err::result_from_owned_ptr(py, ffi::PyNumber_Index(ptr))?;
265                        let res = err_if_invalid_value(py, !0, $pylong_as_ull_or_ull(num.as_ptr()));
266                        num.release_ref(py);
267                        res
268                    }
269                }
270            }
271        }
272    )
273);
274
275int_fits_c_long!(i8);
276int_fits_c_long!(u8);
277int_fits_c_long!(i16);
278int_fits_c_long!(u16);
279int_fits_c_long!(i32);
280
281// If c_long is 64-bits, we can use more types with int_fits_c_long!:
282#[cfg(all(target_pointer_width = "64", not(target_os = "windows")))]
283int_fits_c_long!(u32);
284#[cfg(any(target_pointer_width = "32", target_os = "windows"))]
285int_fits_larger_int!(u32, u64);
286
287#[cfg(all(target_pointer_width = "64", not(target_os = "windows")))]
288int_fits_c_long!(i64);
289
290// manual implementation for i64 on systems with 32-bit long
291#[cfg(any(target_pointer_width = "32", target_os = "windows"))]
292int_convert_u64_or_i64!(i64, ffi::PyLong_FromLongLong, ffi::PyLong_AsLongLong);
293
294#[cfg(all(target_pointer_width = "64", not(target_os = "windows")))]
295int_fits_c_long!(isize);
296#[cfg(any(target_pointer_width = "32", target_os = "windows"))]
297int_fits_larger_int!(isize, i64);
298
299int_fits_larger_int!(usize, u64);
300
301// u64 has a manual implementation as it never fits into signed long
302int_convert_u64_or_i64!(
303    u64,
304    ffi::PyLong_FromUnsignedLongLong,
305    ffi::PyLong_AsUnsignedLongLong
306);
307
308/// Conversion of Rust `f64` to Python `float`.
309impl ToPyObject for f64 {
310    type ObjectType = PyFloat;
311
312    fn to_py_object(&self, py: Python) -> PyFloat {
313        PyFloat::new(py, *self)
314    }
315}
316
317extract!(
318    obj to f64;
319    /// Converts Python `float` to Rust `f64`.
320    py => {
321        let v = unsafe { ffi::PyFloat_AsDouble(obj.as_ptr()) };
322        if v == -1.0 && PyErr::occurred(py) {
323            Err(PyErr::fetch(py))
324        } else {
325            Ok(v)
326        }
327    }
328);
329
330fn overflow_error(py: Python) -> PyErr {
331    PyErr::new_lazy_init(py.get_type::<exc::OverflowError>(), None)
332}
333
334/// Conversion of Rust `f32` to Python `float`.
335impl ToPyObject for f32 {
336    type ObjectType = PyFloat;
337
338    fn to_py_object(&self, py: Python) -> PyFloat {
339        PyFloat::new(py, *self as f64)
340    }
341}
342
343extract!(
344    obj to f32;
345    /// Converts Python `float` to Rust `f32`.
346    ///
347    /// This conversion loses precision as the 64-bit float from Python gets
348    /// converted to a 32-bit float. Out-of-range numbers may also overflow to infinity.
349    py => {
350        Ok(obj.extract::<f64>(py)? as f32)
351    }
352);
353
354#[cfg(test)]
355mod test {
356    use crate::exc;
357    use crate::conversion::ToPyObject;
358    use crate::python::{Python, PythonObject};
359
360    macro_rules! num_to_py_object_and_back (
361        ($func_name:ident, $t1:ty, $t2:ty) => (
362            #[test]
363            fn $func_name() {
364                let gil = Python::acquire_gil();
365                let py = gil.python();
366                let val = 123 as $t1;
367                let obj = val.to_py_object(py).into_object();
368                assert_eq!(obj.extract::<$t2>(py).unwrap(), val as $t2);
369            }
370        )
371    );
372
373    num_to_py_object_and_back!(to_from_f64, f64, f64);
374    num_to_py_object_and_back!(to_from_f32, f32, f32);
375    num_to_py_object_and_back!(to_from_i8, i8, i8);
376    num_to_py_object_and_back!(to_from_u8, u8, u8);
377    num_to_py_object_and_back!(to_from_i16, i16, i16);
378    num_to_py_object_and_back!(to_from_u16, u16, u16);
379    num_to_py_object_and_back!(to_from_i32, i32, i32);
380    num_to_py_object_and_back!(to_from_u32, u32, u32);
381    num_to_py_object_and_back!(to_from_i64, i64, i64);
382    num_to_py_object_and_back!(to_from_u64, u64, u64);
383    num_to_py_object_and_back!(to_from_isize, isize, isize);
384    num_to_py_object_and_back!(to_from_usize, usize, usize);
385    num_to_py_object_and_back!(int_to_float, i32, f64);
386
387
388    macro_rules! float_to_int_fails (
389        ($func_name:ident, $t:ty) => (
390            #[test]
391            fn $func_name() {
392                let gil = Python::acquire_gil();
393                let py = gil.python();
394                let obj = (1.0f64).to_py_object(py).into_object();
395                let err = obj.extract::<$t>(py).unwrap_err();
396                assert!(err.matches(py, py.get_type::<exc::TypeError>()));
397            }
398        )
399    );
400    float_to_int_fails!(float_to_i32, i32);
401    float_to_int_fails!(float_to_u32, u32);
402    float_to_int_fails!(float_to_i64, i64);
403    float_to_int_fails!(float_to_u64, u64);
404
405    macro_rules! str_to_int_fails (
406        ($func_name:ident, $t:ty) => (
407            #[test]
408            fn $func_name() {
409                let gil = Python::acquire_gil();
410                let py = gil.python();
411                // empty string
412                let obj = "".to_py_object(py).into_object();
413                let err = obj.extract::<$t>(py).unwrap_err();
414                assert!(err.matches(py, py.get_type::<exc::TypeError>()));
415
416                // numeric-looking string
417                let obj = "1".to_py_object(py).into_object();
418                let err = obj.extract::<$t>(py).unwrap_err();
419                assert!(err.matches(py, py.get_type::<exc::TypeError>()));
420            }
421        )
422    );
423
424    str_to_int_fails!(str_to_i32, i32);
425    str_to_int_fails!(str_to_u32, u32);
426    str_to_int_fails!(str_to_i64, i64);
427    str_to_int_fails!(str_to_u64, u64);
428
429    #[test]
430    fn test_u32_max() {
431        let gil = Python::acquire_gil();
432        let py = gil.python();
433        let v = std::u32::MAX;
434        let obj = v.to_py_object(py).into_object();
435        assert_eq!(v, obj.extract::<u32>(py).unwrap());
436        assert_eq!(v as u64, obj.extract::<u64>(py).unwrap());
437        assert!(obj.extract::<i32>(py).is_err());
438    }
439
440    #[test]
441    fn test_i64_max() {
442        let gil = Python::acquire_gil();
443        let py = gil.python();
444        let v = std::i64::MAX;
445        let obj = v.to_py_object(py).into_object();
446        assert_eq!(v, obj.extract::<i64>(py).unwrap());
447        assert_eq!(v as u64, obj.extract::<u64>(py).unwrap());
448        assert!(obj.extract::<u32>(py).is_err());
449    }
450
451    #[test]
452    fn test_i64_min() {
453        let gil = Python::acquire_gil();
454        let py = gil.python();
455        let v = std::i64::MIN;
456        let obj = v.to_py_object(py).into_object();
457        assert_eq!(v, obj.extract::<i64>(py).unwrap());
458        assert!(obj.extract::<i32>(py).is_err());
459        assert!(obj.extract::<u64>(py).is_err());
460    }
461
462    #[test]
463    fn test_u64_max() {
464        let gil = Python::acquire_gil();
465        let py = gil.python();
466        let v = std::u64::MAX;
467        let obj = v.to_py_object(py).into_object();
468        println!("{:?}", obj);
469        assert_eq!(v, obj.extract::<u64>(py).unwrap());
470        assert!(obj.extract::<i64>(py).is_err());
471    }
472}