1use crate::call::PyCallArgs;
2use crate::class::basic::CompareOp;
3use crate::conversion::{FromPyObject, IntoPyObject};
4use crate::err::{error_on_minusone, PyErr, PyResult};
5use crate::exceptions::PyTypeError;
6use crate::ffi_ptr_ext::FfiPtrExt;
7use crate::impl_::pycell::PyStaticClassObject;
8use crate::instance::Bound;
9use crate::internal::get_slot::TP_DESCR_GET;
10use crate::py_result_ext::PyResultExt;
11use crate::type_object::{PyTypeCheck, PyTypeInfo};
12use crate::types::PySuper;
13use crate::types::{PyDict, PyIterator, PyList, PyString, PyType};
14use crate::{err, ffi, Borrowed, BoundObject, IntoPyObjectExt, Py};
15#[cfg(RustPython)]
16use crate::{sync::PyOnceLock, types::typeobject::PyTypeMethods};
17use core::cell::UnsafeCell;
18use core::cmp::Ordering;
19use core::ffi::c_int;
20use core::ptr;
21
22#[doc = concat!("[the guide](https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/types.html#concrete-python-types)")]
32#[repr(transparent)]
34pub struct PyAny(UnsafeCell<ffi::PyObject>);
35
36#[allow(non_snake_case)]
37fn PyObject_Check(_: *mut ffi::PyObject) -> c_int {
40 1
41}
42
43#[cfg(not(RustPython))]
45pyobject_native_type_info!(
46 PyAny,
47 pyobject_native_static_type_object!(ffi::PyBaseObject_Type),
48 "typing",
49 "Any",
50 Some("builtins"),
51 #checkfunction=PyObject_Check
52);
53
54#[cfg(RustPython)]
55pyobject_native_type_info!(
56 PyAny,
57 |py| {
58 static TYPE: PyOnceLock<Py<PyType>> = PyOnceLock::new();
59 TYPE.import(py, "builtins", "object").unwrap().as_type_ptr()
60 },
61 "typing",
62 "Any",
63 Some("builtins"),
64 #checkfunction=PyObject_Check
65);
66
67pyobject_native_type_sized!(PyAny, ffi::PyObject);
68impl crate::impl_::pyclass::PyClassBaseType for PyAny {
70 type LayoutAsBase = crate::impl_::pycell::PyClassObjectBase<ffi::PyObject>;
71 type BaseNativeType = PyAny;
72 type Initializer = crate::impl_::pyclass_init::PyNativeTypeInitializer<Self>;
73 type PyClassMutability = crate::pycell::impl_::ImmutableClass;
74 type Layout<T: crate::impl_::pyclass::PyClassImpl> = PyStaticClassObject<T>;
75}
76
77#[doc(alias = "PyAny")]
82pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
83 fn is<T: AsRef<Py<PyAny>>>(&self, other: T) -> bool;
88
89 fn hasattr<N>(&self, attr_name: N) -> PyResult<bool>
112 where
113 N: IntoPyObject<'py, Target = PyString>;
114
115 fn getattr<N>(&self, attr_name: N) -> PyResult<Bound<'py, PyAny>>
138 where
139 N: IntoPyObject<'py, Target = PyString>;
140
141 fn getattr_opt<N>(&self, attr_name: N) -> PyResult<Option<Bound<'py, PyAny>>>
170 where
171 N: IntoPyObject<'py, Target = PyString>;
172
173 fn setattr<N, V>(&self, attr_name: N, value: V) -> PyResult<()>
196 where
197 N: IntoPyObject<'py, Target = PyString>,
198 V: IntoPyObject<'py>;
199
200 fn delattr<N>(&self, attr_name: N) -> PyResult<()>
207 where
208 N: IntoPyObject<'py, Target = PyString>;
209
210 fn compare<O>(&self, other: O) -> PyResult<Ordering>
257 where
258 O: IntoPyObject<'py>;
259
260 fn rich_compare<O>(&self, other: O, compare_op: CompareOp) -> PyResult<Bound<'py, PyAny>>
294 where
295 O: IntoPyObject<'py>;
296
297 fn neg(&self) -> PyResult<Bound<'py, PyAny>>;
301
302 fn pos(&self) -> PyResult<Bound<'py, PyAny>>;
306
307 fn abs(&self) -> PyResult<Bound<'py, PyAny>>;
311
312 fn bitnot(&self) -> PyResult<Bound<'py, PyAny>>;
314
315 fn lt<O>(&self, other: O) -> PyResult<bool>
319 where
320 O: IntoPyObject<'py>;
321
322 fn le<O>(&self, other: O) -> PyResult<bool>
326 where
327 O: IntoPyObject<'py>;
328
329 fn eq<O>(&self, other: O) -> PyResult<bool>
333 where
334 O: IntoPyObject<'py>;
335
336 fn ne<O>(&self, other: O) -> PyResult<bool>
340 where
341 O: IntoPyObject<'py>;
342
343 fn gt<O>(&self, other: O) -> PyResult<bool>
347 where
348 O: IntoPyObject<'py>;
349
350 fn ge<O>(&self, other: O) -> PyResult<bool>
354 where
355 O: IntoPyObject<'py>;
356
357 fn add<O>(&self, other: O) -> PyResult<Bound<'py, PyAny>>
359 where
360 O: IntoPyObject<'py>;
361
362 fn sub<O>(&self, other: O) -> PyResult<Bound<'py, PyAny>>
364 where
365 O: IntoPyObject<'py>;
366
367 fn mul<O>(&self, other: O) -> PyResult<Bound<'py, PyAny>>
369 where
370 O: IntoPyObject<'py>;
371
372 fn matmul<O>(&self, other: O) -> PyResult<Bound<'py, PyAny>>
374 where
375 O: IntoPyObject<'py>;
376
377 fn div<O>(&self, other: O) -> PyResult<Bound<'py, PyAny>>
379 where
380 O: IntoPyObject<'py>;
381
382 fn floor_div<O>(&self, other: O) -> PyResult<Bound<'py, PyAny>>
384 where
385 O: IntoPyObject<'py>;
386
387 fn rem<O>(&self, other: O) -> PyResult<Bound<'py, PyAny>>
389 where
390 O: IntoPyObject<'py>;
391
392 fn divmod<O>(&self, other: O) -> PyResult<Bound<'py, PyAny>>
394 where
395 O: IntoPyObject<'py>;
396
397 fn lshift<O>(&self, other: O) -> PyResult<Bound<'py, PyAny>>
399 where
400 O: IntoPyObject<'py>;
401
402 fn rshift<O>(&self, other: O) -> PyResult<Bound<'py, PyAny>>
404 where
405 O: IntoPyObject<'py>;
406
407 fn pow<O1, O2>(&self, other: O1, modulus: O2) -> PyResult<Bound<'py, PyAny>>
410 where
411 O1: IntoPyObject<'py>,
412 O2: IntoPyObject<'py>;
413
414 fn bitand<O>(&self, other: O) -> PyResult<Bound<'py, PyAny>>
416 where
417 O: IntoPyObject<'py>;
418
419 fn bitor<O>(&self, other: O) -> PyResult<Bound<'py, PyAny>>
421 where
422 O: IntoPyObject<'py>;
423
424 fn bitxor<O>(&self, other: O) -> PyResult<Bound<'py, PyAny>>
426 where
427 O: IntoPyObject<'py>;
428
429 fn is_callable(&self) -> bool;
457
458 fn call<A>(&self, args: A, kwargs: Option<&Bound<'py, PyDict>>) -> PyResult<Bound<'py, PyAny>>
491 where
492 A: PyCallArgs<'py>;
493
494 fn call0(&self) -> PyResult<Bound<'py, PyAny>>;
515
516 fn call1<A>(&self, args: A) -> PyResult<Bound<'py, PyAny>>
546 where
547 A: PyCallArgs<'py>;
548
549 fn call_method<N, A>(
587 &self,
588 name: N,
589 args: A,
590 kwargs: Option<&Bound<'py, PyDict>>,
591 ) -> PyResult<Bound<'py, PyAny>>
592 where
593 N: IntoPyObject<'py, Target = PyString>,
594 A: PyCallArgs<'py>;
595
596 fn call_method0<N>(&self, name: N) -> PyResult<Bound<'py, PyAny>>
630 where
631 N: IntoPyObject<'py, Target = PyString>;
632
633 fn call_method1<N, A>(&self, name: N, args: A) -> PyResult<Bound<'py, PyAny>>
668 where
669 N: IntoPyObject<'py, Target = PyString>,
670 A: PyCallArgs<'py>;
671
672 fn is_truthy(&self) -> PyResult<bool>;
676
677 fn is_none(&self) -> bool;
681
682 fn is_empty(&self) -> PyResult<bool>;
686
687 fn get_item<K>(&self, key: K) -> PyResult<Bound<'py, PyAny>>
691 where
692 K: IntoPyObject<'py>;
693
694 fn set_item<K, V>(&self, key: K, value: V) -> PyResult<()>
698 where
699 K: IntoPyObject<'py>,
700 V: IntoPyObject<'py>;
701
702 fn del_item<K>(&self, key: K) -> PyResult<()>
706 where
707 K: IntoPyObject<'py>;
708
709 fn try_iter(&self) -> PyResult<Bound<'py, PyIterator>>;
734
735 fn get_type(&self) -> Bound<'py, PyType>;
737
738 fn get_type_ptr(&self) -> *mut ffi::PyTypeObject;
740
741 fn extract<'a, T>(&'a self) -> Result<T, T::Error>
745 where
746 T: FromPyObject<'a, 'py>;
747
748 #[deprecated(
750 since = "0.29.0",
751 note = "use `pyo3::ffi::Py_REFCNT(obj.as_ptr())` instead"
752 )]
753 fn get_refcnt(&self) -> isize;
754
755 fn repr(&self) -> PyResult<Bound<'py, PyString>>;
759
760 fn str(&self) -> PyResult<Bound<'py, PyString>>;
764
765 fn hash(&self) -> PyResult<isize>;
769
770 fn len(&self) -> PyResult<usize>;
774
775 fn dir(&self) -> PyResult<Bound<'py, PyList>>;
779
780 fn is_instance(&self, ty: &Bound<'py, PyAny>) -> PyResult<bool>;
784
785 fn is_exact_instance(&self, ty: &Bound<'py, PyAny>) -> bool;
789
790 fn is_instance_of<T: PyTypeCheck>(&self) -> bool;
795
796 fn is_exact_instance_of<T: PyTypeInfo>(&self) -> bool;
801
802 fn contains<V>(&self, value: V) -> PyResult<bool>
806 where
807 V: IntoPyObject<'py>;
808
809 fn py_super(&self) -> PyResult<Bound<'py, PySuper>>;
813}
814
815macro_rules! implement_binop {
816 ($name:ident, $c_api:ident, $op:expr) => {
817 #[doc = concat!("Computes `self ", $op, " other`.")]
818 fn $name<O>(&self, other: O) -> PyResult<Bound<'py, PyAny>>
819 where
820 O: IntoPyObject<'py>,
821 {
822 fn inner<'py>(
823 any: &Bound<'py, PyAny>,
824 other: Borrowed<'_, 'py, PyAny>,
825 ) -> PyResult<Bound<'py, PyAny>> {
826 unsafe { ffi::$c_api(any.as_ptr(), other.as_ptr()).assume_owned_or_err(any.py()) }
827 }
828
829 let py = self.py();
830 inner(
831 self,
832 other.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
833 )
834 }
835 };
836}
837
838impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
839 #[inline]
840 fn is<T: AsRef<Py<PyAny>>>(&self, other: T) -> bool {
841 ptr::eq(self.as_ptr(), other.as_ref().as_ptr())
842 }
843
844 fn hasattr<N>(&self, attr_name: N) -> PyResult<bool>
845 where
846 N: IntoPyObject<'py, Target = PyString>,
847 {
848 fn inner<'py>(
849 any: &Bound<'py, PyAny>,
850 attr_name: Borrowed<'_, '_, PyString>,
851 ) -> PyResult<bool> {
852 let result =
853 unsafe { ffi::compat::PyObject_HasAttrWithError(any.as_ptr(), attr_name.as_ptr()) };
854 error_on_minusone(any.py(), result)?;
855 Ok(result > 0)
856 }
857
858 inner(
859 self,
860 attr_name
861 .into_pyobject(self.py())
862 .map_err(Into::into)?
863 .as_borrowed(),
864 )
865 }
866
867 fn getattr<N>(&self, attr_name: N) -> PyResult<Bound<'py, PyAny>>
868 where
869 N: IntoPyObject<'py, Target = PyString>,
870 {
871 fn inner<'py>(
872 any: &Bound<'py, PyAny>,
873 attr_name: Borrowed<'_, '_, PyString>,
874 ) -> PyResult<Bound<'py, PyAny>> {
875 unsafe {
876 ffi::PyObject_GetAttr(any.as_ptr(), attr_name.as_ptr())
877 .assume_owned_or_err(any.py())
878 }
879 }
880
881 inner(
882 self,
883 attr_name
884 .into_pyobject(self.py())
885 .map_err(Into::into)?
886 .as_borrowed(),
887 )
888 }
889
890 fn getattr_opt<N>(&self, attr_name: N) -> PyResult<Option<Bound<'py, PyAny>>>
891 where
892 N: IntoPyObject<'py, Target = PyString>,
893 {
894 fn inner<'py>(
895 any: &Bound<'py, PyAny>,
896 attr_name: Borrowed<'_, 'py, PyString>,
897 ) -> PyResult<Option<Bound<'py, PyAny>>> {
898 let mut resp_ptr: *mut ffi::PyObject = core::ptr::null_mut();
899 match unsafe {
900 ffi::compat::PyObject_GetOptionalAttr(
901 any.as_ptr(),
902 attr_name.as_ptr(),
903 &mut resp_ptr,
904 )
905 } {
906 1 => Ok(Some(unsafe { Bound::from_owned_ptr(any.py(), resp_ptr) })),
908 0 => Ok(None),
910 _ => Err(PyErr::fetch(any.py())),
912 }
913 }
914
915 let py = self.py();
916 inner(self, attr_name.into_pyobject_or_pyerr(py)?.as_borrowed())
917 }
918
919 fn setattr<N, V>(&self, attr_name: N, value: V) -> PyResult<()>
920 where
921 N: IntoPyObject<'py, Target = PyString>,
922 V: IntoPyObject<'py>,
923 {
924 fn inner(
925 any: &Bound<'_, PyAny>,
926 attr_name: Borrowed<'_, '_, PyString>,
927 value: Borrowed<'_, '_, PyAny>,
928 ) -> PyResult<()> {
929 err::error_on_minusone(any.py(), unsafe {
930 ffi::PyObject_SetAttr(any.as_ptr(), attr_name.as_ptr(), value.as_ptr())
931 })
932 }
933
934 let py = self.py();
935 inner(
936 self,
937 attr_name.into_pyobject_or_pyerr(py)?.as_borrowed(),
938 value.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
939 )
940 }
941
942 fn delattr<N>(&self, attr_name: N) -> PyResult<()>
943 where
944 N: IntoPyObject<'py, Target = PyString>,
945 {
946 fn inner(any: &Bound<'_, PyAny>, attr_name: Borrowed<'_, '_, PyString>) -> PyResult<()> {
947 err::error_on_minusone(any.py(), unsafe {
948 ffi::PyObject_DelAttr(any.as_ptr(), attr_name.as_ptr())
949 })
950 }
951
952 let py = self.py();
953 inner(self, attr_name.into_pyobject_or_pyerr(py)?.as_borrowed())
954 }
955
956 fn compare<O>(&self, other: O) -> PyResult<Ordering>
957 where
958 O: IntoPyObject<'py>,
959 {
960 fn inner(any: &Bound<'_, PyAny>, other: Borrowed<'_, '_, PyAny>) -> PyResult<Ordering> {
961 let other = other.as_ptr();
962 let do_compare = |other, op| unsafe {
965 ffi::PyObject_RichCompare(any.as_ptr(), other, op)
966 .assume_owned_or_err(any.py())
967 .and_then(|obj| obj.is_truthy())
968 };
969 if do_compare(other, ffi::Py_EQ)? {
970 Ok(Ordering::Equal)
971 } else if do_compare(other, ffi::Py_LT)? {
972 Ok(Ordering::Less)
973 } else if do_compare(other, ffi::Py_GT)? {
974 Ok(Ordering::Greater)
975 } else {
976 Err(PyTypeError::new_err(
977 "PyAny::compare(): All comparisons returned false",
978 ))
979 }
980 }
981
982 let py = self.py();
983 inner(
984 self,
985 other.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
986 )
987 }
988
989 fn rich_compare<O>(&self, other: O, compare_op: CompareOp) -> PyResult<Bound<'py, PyAny>>
990 where
991 O: IntoPyObject<'py>,
992 {
993 fn inner<'py>(
994 any: &Bound<'py, PyAny>,
995 other: Borrowed<'_, 'py, PyAny>,
996 compare_op: CompareOp,
997 ) -> PyResult<Bound<'py, PyAny>> {
998 unsafe {
999 ffi::PyObject_RichCompare(any.as_ptr(), other.as_ptr(), compare_op as c_int)
1000 .assume_owned_or_err(any.py())
1001 }
1002 }
1003
1004 let py = self.py();
1005 inner(
1006 self,
1007 other.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
1008 compare_op,
1009 )
1010 }
1011
1012 fn neg(&self) -> PyResult<Bound<'py, PyAny>> {
1013 unsafe { ffi::PyNumber_Negative(self.as_ptr()).assume_owned_or_err(self.py()) }
1014 }
1015
1016 fn pos(&self) -> PyResult<Bound<'py, PyAny>> {
1017 fn inner<'py>(any: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyAny>> {
1018 unsafe { ffi::PyNumber_Positive(any.as_ptr()).assume_owned_or_err(any.py()) }
1019 }
1020
1021 inner(self)
1022 }
1023
1024 fn abs(&self) -> PyResult<Bound<'py, PyAny>> {
1025 fn inner<'py>(any: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyAny>> {
1026 unsafe { ffi::PyNumber_Absolute(any.as_ptr()).assume_owned_or_err(any.py()) }
1027 }
1028
1029 inner(self)
1030 }
1031
1032 fn bitnot(&self) -> PyResult<Bound<'py, PyAny>> {
1033 fn inner<'py>(any: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyAny>> {
1034 unsafe { ffi::PyNumber_Invert(any.as_ptr()).assume_owned_or_err(any.py()) }
1035 }
1036
1037 inner(self)
1038 }
1039
1040 fn lt<O>(&self, other: O) -> PyResult<bool>
1041 where
1042 O: IntoPyObject<'py>,
1043 {
1044 self.rich_compare(other, CompareOp::Lt)
1045 .and_then(|any| any.is_truthy())
1046 }
1047
1048 fn le<O>(&self, other: O) -> PyResult<bool>
1049 where
1050 O: IntoPyObject<'py>,
1051 {
1052 self.rich_compare(other, CompareOp::Le)
1053 .and_then(|any| any.is_truthy())
1054 }
1055
1056 fn eq<O>(&self, other: O) -> PyResult<bool>
1057 where
1058 O: IntoPyObject<'py>,
1059 {
1060 self.rich_compare(other, CompareOp::Eq)
1061 .and_then(|any| any.is_truthy())
1062 }
1063
1064 fn ne<O>(&self, other: O) -> PyResult<bool>
1065 where
1066 O: IntoPyObject<'py>,
1067 {
1068 self.rich_compare(other, CompareOp::Ne)
1069 .and_then(|any| any.is_truthy())
1070 }
1071
1072 fn gt<O>(&self, other: O) -> PyResult<bool>
1073 where
1074 O: IntoPyObject<'py>,
1075 {
1076 self.rich_compare(other, CompareOp::Gt)
1077 .and_then(|any| any.is_truthy())
1078 }
1079
1080 fn ge<O>(&self, other: O) -> PyResult<bool>
1081 where
1082 O: IntoPyObject<'py>,
1083 {
1084 self.rich_compare(other, CompareOp::Ge)
1085 .and_then(|any| any.is_truthy())
1086 }
1087
1088 implement_binop!(add, PyNumber_Add, "+");
1089 implement_binop!(sub, PyNumber_Subtract, "-");
1090 implement_binop!(mul, PyNumber_Multiply, "*");
1091 implement_binop!(matmul, PyNumber_MatrixMultiply, "@");
1092 implement_binop!(div, PyNumber_TrueDivide, "/");
1093 implement_binop!(floor_div, PyNumber_FloorDivide, "//");
1094 implement_binop!(rem, PyNumber_Remainder, "%");
1095 implement_binop!(lshift, PyNumber_Lshift, "<<");
1096 implement_binop!(rshift, PyNumber_Rshift, ">>");
1097 implement_binop!(bitand, PyNumber_And, "&");
1098 implement_binop!(bitor, PyNumber_Or, "|");
1099 implement_binop!(bitxor, PyNumber_Xor, "^");
1100
1101 fn divmod<O>(&self, other: O) -> PyResult<Bound<'py, PyAny>>
1103 where
1104 O: IntoPyObject<'py>,
1105 {
1106 fn inner<'py>(
1107 any: &Bound<'py, PyAny>,
1108 other: Borrowed<'_, 'py, PyAny>,
1109 ) -> PyResult<Bound<'py, PyAny>> {
1110 unsafe {
1111 ffi::PyNumber_Divmod(any.as_ptr(), other.as_ptr()).assume_owned_or_err(any.py())
1112 }
1113 }
1114
1115 let py = self.py();
1116 inner(
1117 self,
1118 other.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
1119 )
1120 }
1121
1122 fn pow<O1, O2>(&self, other: O1, modulus: O2) -> PyResult<Bound<'py, PyAny>>
1125 where
1126 O1: IntoPyObject<'py>,
1127 O2: IntoPyObject<'py>,
1128 {
1129 fn inner<'py>(
1130 any: &Bound<'py, PyAny>,
1131 other: Borrowed<'_, 'py, PyAny>,
1132 modulus: Borrowed<'_, 'py, PyAny>,
1133 ) -> PyResult<Bound<'py, PyAny>> {
1134 unsafe {
1135 ffi::PyNumber_Power(any.as_ptr(), other.as_ptr(), modulus.as_ptr())
1136 .assume_owned_or_err(any.py())
1137 }
1138 }
1139
1140 let py = self.py();
1141 inner(
1142 self,
1143 other.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
1144 modulus.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
1145 )
1146 }
1147
1148 fn is_callable(&self) -> bool {
1149 unsafe { ffi::PyCallable_Check(self.as_ptr()) != 0 }
1150 }
1151
1152 fn call<A>(&self, args: A, kwargs: Option<&Bound<'py, PyDict>>) -> PyResult<Bound<'py, PyAny>>
1153 where
1154 A: PyCallArgs<'py>,
1155 {
1156 if let Some(kwargs) = kwargs {
1157 args.call(
1158 self.as_borrowed(),
1159 kwargs.as_borrowed(),
1160 crate::call::private::Token,
1161 )
1162 } else {
1163 args.call_positional(self.as_borrowed(), crate::call::private::Token)
1164 }
1165 }
1166
1167 #[inline]
1168 fn call0(&self) -> PyResult<Bound<'py, PyAny>> {
1169 unsafe { ffi::compat::PyObject_CallNoArgs(self.as_ptr()).assume_owned_or_err(self.py()) }
1170 }
1171
1172 fn call1<A>(&self, args: A) -> PyResult<Bound<'py, PyAny>>
1173 where
1174 A: PyCallArgs<'py>,
1175 {
1176 args.call_positional(self.as_borrowed(), crate::call::private::Token)
1177 }
1178
1179 #[inline]
1180 fn call_method<N, A>(
1181 &self,
1182 name: N,
1183 args: A,
1184 kwargs: Option<&Bound<'py, PyDict>>,
1185 ) -> PyResult<Bound<'py, PyAny>>
1186 where
1187 N: IntoPyObject<'py, Target = PyString>,
1188 A: PyCallArgs<'py>,
1189 {
1190 if kwargs.is_none() {
1191 self.call_method1(name, args)
1192 } else {
1193 self.getattr(name)
1194 .and_then(|method| method.call(args, kwargs))
1195 }
1196 }
1197
1198 #[inline]
1199 fn call_method0<N>(&self, name: N) -> PyResult<Bound<'py, PyAny>>
1200 where
1201 N: IntoPyObject<'py, Target = PyString>,
1202 {
1203 let py = self.py();
1204 let name = name.into_pyobject_or_pyerr(py)?.into_bound();
1205 unsafe {
1206 ffi::compat::PyObject_CallMethodNoArgs(self.as_ptr(), name.as_ptr())
1207 .assume_owned_or_err(py)
1208 }
1209 }
1210
1211 fn call_method1<N, A>(&self, name: N, args: A) -> PyResult<Bound<'py, PyAny>>
1212 where
1213 N: IntoPyObject<'py, Target = PyString>,
1214 A: PyCallArgs<'py>,
1215 {
1216 let name = name.into_pyobject_or_pyerr(self.py())?;
1217 args.call_method_positional(
1218 self.as_borrowed(),
1219 name.as_borrowed(),
1220 crate::call::private::Token,
1221 )
1222 }
1223
1224 fn is_truthy(&self) -> PyResult<bool> {
1225 let v = unsafe { ffi::PyObject_IsTrue(self.as_ptr()) };
1226 err::error_on_minusone(self.py(), v)?;
1227 Ok(v != 0)
1228 }
1229
1230 #[inline]
1231 fn is_none(&self) -> bool {
1232 unsafe { ptr::eq(ffi::Py_None(), self.as_ptr()) }
1233 }
1234
1235 fn is_empty(&self) -> PyResult<bool> {
1236 self.len().map(|l| l == 0)
1237 }
1238
1239 fn get_item<K>(&self, key: K) -> PyResult<Bound<'py, PyAny>>
1240 where
1241 K: IntoPyObject<'py>,
1242 {
1243 fn inner<'py>(
1244 any: &Bound<'py, PyAny>,
1245 key: Borrowed<'_, 'py, PyAny>,
1246 ) -> PyResult<Bound<'py, PyAny>> {
1247 unsafe {
1248 ffi::PyObject_GetItem(any.as_ptr(), key.as_ptr()).assume_owned_or_err(any.py())
1249 }
1250 }
1251
1252 let py = self.py();
1253 inner(
1254 self,
1255 key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
1256 )
1257 }
1258
1259 fn set_item<K, V>(&self, key: K, value: V) -> PyResult<()>
1260 where
1261 K: IntoPyObject<'py>,
1262 V: IntoPyObject<'py>,
1263 {
1264 fn inner(
1265 any: &Bound<'_, PyAny>,
1266 key: Borrowed<'_, '_, PyAny>,
1267 value: Borrowed<'_, '_, PyAny>,
1268 ) -> PyResult<()> {
1269 err::error_on_minusone(any.py(), unsafe {
1270 ffi::PyObject_SetItem(any.as_ptr(), key.as_ptr(), value.as_ptr())
1271 })
1272 }
1273
1274 let py = self.py();
1275 inner(
1276 self,
1277 key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
1278 value.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
1279 )
1280 }
1281
1282 fn del_item<K>(&self, key: K) -> PyResult<()>
1283 where
1284 K: IntoPyObject<'py>,
1285 {
1286 fn inner(any: &Bound<'_, PyAny>, key: Borrowed<'_, '_, PyAny>) -> PyResult<()> {
1287 err::error_on_minusone(any.py(), unsafe {
1288 ffi::PyObject_DelItem(any.as_ptr(), key.as_ptr())
1289 })
1290 }
1291
1292 let py = self.py();
1293 inner(
1294 self,
1295 key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
1296 )
1297 }
1298
1299 fn try_iter(&self) -> PyResult<Bound<'py, PyIterator>> {
1300 PyIterator::from_object(self)
1301 }
1302
1303 fn get_type(&self) -> Bound<'py, PyType> {
1304 unsafe { PyType::from_borrowed_type_ptr(self.py(), ffi::Py_TYPE(self.as_ptr())) }
1305 }
1306
1307 #[inline]
1308 fn get_type_ptr(&self) -> *mut ffi::PyTypeObject {
1309 unsafe { ffi::Py_TYPE(self.as_ptr()) }
1310 }
1311
1312 fn extract<'a, T>(&'a self) -> Result<T, T::Error>
1313 where
1314 T: FromPyObject<'a, 'py>,
1315 {
1316 FromPyObject::extract(self.as_borrowed())
1317 }
1318
1319 fn get_refcnt(&self) -> isize {
1320 self._get_refcnt()
1321 }
1322
1323 fn repr(&self) -> PyResult<Bound<'py, PyString>> {
1324 unsafe {
1325 ffi::PyObject_Repr(self.as_ptr())
1326 .assume_owned_or_err(self.py())
1327 .cast_into_unchecked()
1328 }
1329 }
1330
1331 fn str(&self) -> PyResult<Bound<'py, PyString>> {
1332 unsafe {
1333 ffi::PyObject_Str(self.as_ptr())
1334 .assume_owned_or_err(self.py())
1335 .cast_into_unchecked()
1336 }
1337 }
1338
1339 fn hash(&self) -> PyResult<isize> {
1340 let v = unsafe { ffi::PyObject_Hash(self.as_ptr()) };
1341 crate::err::error_on_minusone(self.py(), v)?;
1342 Ok(v)
1343 }
1344
1345 fn len(&self) -> PyResult<usize> {
1346 let v = unsafe { ffi::PyObject_Size(self.as_ptr()) };
1347 crate::err::error_on_minusone(self.py(), v)?;
1348 Ok(v as usize)
1349 }
1350
1351 fn dir(&self) -> PyResult<Bound<'py, PyList>> {
1352 unsafe {
1353 ffi::PyObject_Dir(self.as_ptr())
1354 .assume_owned_or_err(self.py())
1355 .cast_into_unchecked()
1356 }
1357 }
1358
1359 #[inline]
1360 fn is_instance(&self, ty: &Bound<'py, PyAny>) -> PyResult<bool> {
1361 let result = unsafe { ffi::PyObject_IsInstance(self.as_ptr(), ty.as_ptr()) };
1362 err::error_on_minusone(self.py(), result)?;
1363 Ok(result == 1)
1364 }
1365
1366 #[inline]
1367 fn is_exact_instance(&self, ty: &Bound<'py, PyAny>) -> bool {
1368 self.get_type().is(ty)
1369 }
1370
1371 #[inline]
1372 fn is_instance_of<T: PyTypeCheck>(&self) -> bool {
1373 T::type_check(self)
1374 }
1375
1376 #[inline]
1377 fn is_exact_instance_of<T: PyTypeInfo>(&self) -> bool {
1378 T::is_exact_type_of(self)
1379 }
1380
1381 fn contains<V>(&self, value: V) -> PyResult<bool>
1382 where
1383 V: IntoPyObject<'py>,
1384 {
1385 fn inner(any: &Bound<'_, PyAny>, value: Borrowed<'_, '_, PyAny>) -> PyResult<bool> {
1386 match unsafe { ffi::PySequence_Contains(any.as_ptr(), value.as_ptr()) } {
1387 0 => Ok(false),
1388 1 => Ok(true),
1389 _ => Err(PyErr::fetch(any.py())),
1390 }
1391 }
1392
1393 let py = self.py();
1394 inner(
1395 self,
1396 value.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
1397 )
1398 }
1399
1400 fn py_super(&self) -> PyResult<Bound<'py, PySuper>> {
1401 PySuper::new(&self.get_type(), self)
1402 }
1403}
1404
1405impl<'py> Bound<'py, PyAny> {
1406 #[allow(dead_code, reason = "currently only used with num-complex + abi3")]
1417 pub(crate) fn lookup_special<N>(&self, attr_name: N) -> PyResult<Option<Bound<'py, PyAny>>>
1418 where
1419 N: IntoPyObject<'py, Target = PyString>,
1420 {
1421 let py = self.py();
1422 let self_type = self.get_type();
1423 let attr = if let Ok(attr) = self_type.getattr(attr_name) {
1424 attr
1425 } else {
1426 return Ok(None);
1427 };
1428
1429 if let Some(descr_get) = attr.get_type().get_slot(TP_DESCR_GET) {
1431 unsafe {
1433 descr_get(attr.as_ptr(), self.as_ptr(), self_type.as_ptr())
1434 .assume_owned_or_err(py)
1435 .map(Some)
1436 }
1437 } else {
1438 Ok(Some(attr))
1439 }
1440 }
1441
1442 #[inline]
1443 pub(crate) fn _get_refcnt(&self) -> isize {
1444 unsafe { ffi::Py_REFCNT(self.as_ptr()) }
1445 }
1446}
1447
1448#[cfg(test)]
1449mod tests {
1450 use crate::{
1451 basic::CompareOp,
1452 test_utils::generate_unique_module_name,
1453 types::{IntoPyDict, PyAny, PyAnyMethods, PyBool, PyInt, PyList, PyModule, PyTypeMethods},
1454 Bound, BoundObject, IntoPyObject, PyTypeInfo, Python,
1455 };
1456 use core::fmt::Debug;
1457 use pyo3_ffi::c_str;
1458
1459 #[test]
1460 fn test_lookup_special() {
1461 Python::attach(|py| {
1462 let module = PyModule::from_code(
1463 py,
1464 cr#"
1465class CustomCallable:
1466 def __call__(self):
1467 return 1
1468
1469class SimpleInt:
1470 def __int__(self):
1471 return 1
1472
1473class InheritedInt(SimpleInt): pass
1474
1475class NoInt: pass
1476
1477class NoDescriptorInt:
1478 __int__ = CustomCallable()
1479
1480class InstanceOverrideInt:
1481 def __int__(self):
1482 return 1
1483instance_override = InstanceOverrideInt()
1484instance_override.__int__ = lambda self: 2
1485
1486class ErrorInDescriptorInt:
1487 @property
1488 def __int__(self):
1489 raise ValueError("uh-oh!")
1490
1491class NonHeapNonDescriptorInt:
1492 # A static-typed callable that doesn't implement `__get__`. These are pretty hard to come by.
1493 __int__ = int
1494 "#,
1495 c"test.py",
1496 &generate_unique_module_name("test"),
1497 )
1498 .unwrap();
1499
1500 let int = crate::intern!(py, "__int__");
1501 let eval_int =
1502 |obj: Bound<'_, PyAny>| obj.lookup_special(int)?.unwrap().call0()?.extract::<u32>();
1503
1504 let simple = module.getattr("SimpleInt").unwrap().call0().unwrap();
1505 assert_eq!(eval_int(simple).unwrap(), 1);
1506 let inherited = module.getattr("InheritedInt").unwrap().call0().unwrap();
1507 assert_eq!(eval_int(inherited).unwrap(), 1);
1508 let no_descriptor = module.getattr("NoDescriptorInt").unwrap().call0().unwrap();
1509 assert_eq!(eval_int(no_descriptor).unwrap(), 1);
1510 let missing = module.getattr("NoInt").unwrap().call0().unwrap();
1511 assert!(missing.lookup_special(int).unwrap().is_none());
1512 let instance_override = module.getattr("instance_override").unwrap();
1515 assert_eq!(eval_int(instance_override).unwrap(), 1);
1516 let descriptor_error = module
1517 .getattr("ErrorInDescriptorInt")
1518 .unwrap()
1519 .call0()
1520 .unwrap();
1521 assert!(descriptor_error.lookup_special(int).is_err());
1522 let nonheap_nondescriptor = module
1523 .getattr("NonHeapNonDescriptorInt")
1524 .unwrap()
1525 .call0()
1526 .unwrap();
1527 assert_eq!(eval_int(nonheap_nondescriptor).unwrap(), 0);
1528 })
1529 }
1530
1531 #[test]
1532 fn test_getattr_opt() {
1533 Python::attach(|py| {
1534 let module = PyModule::from_code(
1535 py,
1536 cr#"
1537class Test:
1538 class_str_attribute = "class_string"
1539
1540 @property
1541 def error(self):
1542 raise ValueError("This is an intentional error")
1543 "#,
1544 c"test.py",
1545 &generate_unique_module_name("test"),
1546 )
1547 .unwrap();
1548
1549 let class_test = module.getattr_opt("Test").unwrap().unwrap();
1551
1552 let cls_attr_str = class_test
1554 .getattr_opt("class_str_attribute")
1555 .unwrap()
1556 .unwrap();
1557 assert_eq!(cls_attr_str.extract::<String>().unwrap(), "class_string");
1558
1559 let do_not_exist = class_test.getattr_opt("doNotExist").unwrap();
1561 assert!(do_not_exist.is_none());
1562
1563 let instance = class_test.call0().unwrap();
1565 let error = instance.getattr_opt("error");
1566 assert!(error.is_err());
1567 assert!(error
1568 .unwrap_err()
1569 .to_string()
1570 .contains("This is an intentional error"));
1571 });
1572 }
1573
1574 #[test]
1575 fn test_getattr_opt_attribute_error_subclass() {
1576 Python::attach(|py| {
1577 let module = PyModule::from_code(
1578 py,
1579 cr#"
1580class CustomAttrError(AttributeError):
1581 pass
1582
1583class Obj:
1584 @property
1585 def missing(self):
1586 raise CustomAttrError("not here")
1587 "#,
1588 c"test.py",
1589 &generate_unique_module_name("test"),
1590 )
1591 .unwrap();
1592
1593 let obj = module.getattr("Obj").unwrap().call0().unwrap();
1594
1595 let result = obj.getattr_opt("missing").unwrap();
1597 assert!(result.is_none());
1598 });
1599 }
1600
1601 #[test]
1602 fn test_call_for_non_existing_method() {
1603 Python::attach(|py| {
1604 let a = py.eval(c"42", None, None).unwrap();
1605 a.call_method0("__str__").unwrap(); assert!(a.call_method("nonexistent_method", (1,), None).is_err());
1607 assert!(a.call_method0("nonexistent_method").is_err());
1608 assert!(a.call_method1("nonexistent_method", (1,)).is_err());
1609 });
1610 }
1611
1612 #[test]
1613 fn test_call_with_kwargs() {
1614 Python::attach(|py| {
1615 let list = vec![3, 6, 5, 4, 7].into_pyobject(py).unwrap();
1616 let dict = vec![("reverse", true)].into_py_dict(py).unwrap();
1617 list.call_method("sort", (), Some(&dict)).unwrap();
1618 assert_eq!(list.extract::<Vec<i32>>().unwrap(), vec![7, 6, 5, 4, 3]);
1619 });
1620 }
1621
1622 #[test]
1623 fn test_call_method0() {
1624 Python::attach(|py| {
1625 let module = PyModule::from_code(
1626 py,
1627 cr#"
1628class SimpleClass:
1629 def foo(self):
1630 return 42
1631"#,
1632 c_str!(file!()),
1633 &generate_unique_module_name("test_module"),
1634 )
1635 .expect("module creation failed");
1636
1637 let simple_class = module.getattr("SimpleClass").unwrap().call0().unwrap();
1638 assert_eq!(
1639 simple_class
1640 .call_method0("foo")
1641 .unwrap()
1642 .extract::<u32>()
1643 .unwrap(),
1644 42
1645 );
1646 })
1647 }
1648
1649 #[test]
1650 fn test_type() {
1651 Python::attach(|py| {
1652 let obj = py.eval(c"42", None, None).unwrap();
1653 assert_eq!(obj.get_type().as_type_ptr(), obj.get_type_ptr());
1654 });
1655 }
1656
1657 #[test]
1658 fn test_dir() {
1659 Python::attach(|py| {
1660 let obj = py.eval(c"42", None, None).unwrap();
1661 let dir = py
1662 .eval(c"dir(42)", None, None)
1663 .unwrap()
1664 .cast_into::<PyList>()
1665 .unwrap();
1666 let a = obj
1667 .dir()
1668 .unwrap()
1669 .into_iter()
1670 .map(|x| x.extract::<String>().unwrap());
1671 let b = dir.into_iter().map(|x| x.extract::<String>().unwrap());
1672 assert!(a.eq(b));
1673 });
1674 }
1675
1676 #[test]
1677 fn test_hasattr() {
1678 Python::attach(|py| {
1679 let x = 5i32.into_pyobject(py).unwrap();
1680 assert!(x.is_instance_of::<PyInt>());
1681
1682 assert!(x.hasattr("to_bytes").unwrap());
1683 assert!(!x.hasattr("bbbbbbytes").unwrap());
1684 })
1685 }
1686
1687 #[cfg(feature = "macros")]
1688 #[test]
1689 #[allow(unknown_lints, non_local_definitions)]
1690 fn test_hasattr_error() {
1691 use crate::exceptions::PyValueError;
1692 use crate::prelude::*;
1693
1694 #[pyclass(crate = "crate")]
1695 struct GetattrFail;
1696
1697 #[pymethods(crate = "crate")]
1698 impl GetattrFail {
1699 fn __getattr__(&self, attr: Py<PyAny>) -> PyResult<Py<PyAny>> {
1700 Err(PyValueError::new_err(attr))
1701 }
1702 }
1703
1704 Python::attach(|py| {
1705 let obj = Py::new(py, GetattrFail).unwrap();
1706 let obj = obj.bind(py).as_any();
1707
1708 assert!(obj
1709 .hasattr("foo")
1710 .unwrap_err()
1711 .is_instance_of::<PyValueError>(py));
1712 })
1713 }
1714
1715 #[test]
1716 fn test_nan_eq() {
1717 Python::attach(|py| {
1718 let nan = py.eval(c"float('nan')", None, None).unwrap();
1719 assert!(nan.compare(&nan).is_err());
1720 });
1721 }
1722
1723 #[test]
1724 fn test_any_is_instance_of() {
1725 Python::attach(|py| {
1726 let x = 5i32.into_pyobject(py).unwrap();
1727 assert!(x.is_instance_of::<PyInt>());
1728
1729 let l = vec![&x, &x].into_pyobject(py).unwrap();
1730 assert!(l.is_instance_of::<PyList>());
1731 });
1732 }
1733
1734 #[test]
1735 fn test_any_is_instance() {
1736 Python::attach(|py| {
1737 let l = vec![1i8, 2].into_pyobject(py).unwrap();
1738 assert!(l.is_instance(&py.get_type::<PyList>()).unwrap());
1739 });
1740 }
1741
1742 #[test]
1743 fn test_any_is_exact_instance_of() {
1744 Python::attach(|py| {
1745 let x = 5i32.into_pyobject(py).unwrap();
1746 assert!(x.is_exact_instance_of::<PyInt>());
1747
1748 let t = PyBool::new(py, true);
1749 assert!(t.is_instance_of::<PyInt>());
1750 assert!(!t.is_exact_instance_of::<PyInt>());
1751 assert!(t.is_exact_instance_of::<PyBool>());
1752
1753 let l = vec![&x, &x].into_pyobject(py).unwrap();
1754 assert!(l.is_exact_instance_of::<PyList>());
1755 });
1756 }
1757
1758 #[test]
1759 fn test_any_is_exact_instance() {
1760 Python::attach(|py| {
1761 let t = PyBool::new(py, true);
1762 assert!(t.is_instance(&py.get_type::<PyInt>()).unwrap());
1763 assert!(!t.is_exact_instance(&py.get_type::<PyInt>()));
1764 assert!(t.is_exact_instance(&py.get_type::<PyBool>()));
1765 });
1766 }
1767
1768 #[test]
1769 fn test_any_contains() {
1770 Python::attach(|py| {
1771 let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
1772 let ob = v.into_pyobject(py).unwrap();
1773
1774 let bad_needle = 7i32.into_pyobject(py).unwrap();
1775 assert!(!ob.contains(&bad_needle).unwrap());
1776
1777 let good_needle = 8i32.into_pyobject(py).unwrap();
1778 assert!(ob.contains(&good_needle).unwrap());
1779
1780 let type_coerced_needle = 8f32.into_pyobject(py).unwrap();
1781 assert!(ob.contains(&type_coerced_needle).unwrap());
1782
1783 let n: u32 = 42;
1784 let bad_haystack = n.into_pyobject(py).unwrap();
1785 let irrelevant_needle = 0i32.into_pyobject(py).unwrap();
1786 assert!(bad_haystack.contains(&irrelevant_needle).is_err());
1787 });
1788 }
1789
1790 fn test_eq_methods_generic<'a, T>(list: &'a [T])
1792 where
1793 T: PartialEq + PartialOrd,
1794 for<'py> &'a T: IntoPyObject<'py>,
1795 for<'py> <&'a T as IntoPyObject<'py>>::Error: Debug,
1796 {
1797 Python::attach(|py| {
1798 for a in list {
1799 for b in list {
1800 let a_py = a.into_pyobject(py).unwrap().into_any().into_bound();
1801 let b_py = b.into_pyobject(py).unwrap().into_any().into_bound();
1802
1803 assert_eq!(
1804 a.lt(b),
1805 a_py.lt(&b_py).unwrap(),
1806 "{} < {} should be {}.",
1807 a_py,
1808 b_py,
1809 a.lt(b)
1810 );
1811 assert_eq!(
1812 a.le(b),
1813 a_py.le(&b_py).unwrap(),
1814 "{} <= {} should be {}.",
1815 a_py,
1816 b_py,
1817 a.le(b)
1818 );
1819 assert_eq!(
1820 a.eq(b),
1821 a_py.eq(&b_py).unwrap(),
1822 "{} == {} should be {}.",
1823 a_py,
1824 b_py,
1825 a.eq(b)
1826 );
1827 assert_eq!(
1828 a.ne(b),
1829 a_py.ne(&b_py).unwrap(),
1830 "{} != {} should be {}.",
1831 a_py,
1832 b_py,
1833 a.ne(b)
1834 );
1835 assert_eq!(
1836 a.gt(b),
1837 a_py.gt(&b_py).unwrap(),
1838 "{} > {} should be {}.",
1839 a_py,
1840 b_py,
1841 a.gt(b)
1842 );
1843 assert_eq!(
1844 a.ge(b),
1845 a_py.ge(&b_py).unwrap(),
1846 "{} >= {} should be {}.",
1847 a_py,
1848 b_py,
1849 a.ge(b)
1850 );
1851 }
1852 }
1853 });
1854 }
1855
1856 #[test]
1857 fn test_eq_methods_integers() {
1858 let ints = [-4, -4, 1, 2, 0, -100, 1_000_000];
1859 test_eq_methods_generic::<i32>(&ints);
1860 }
1861
1862 #[test]
1863 fn test_eq_methods_strings() {
1864 let strings = ["Let's", "test", "some", "eq", "methods"];
1865 test_eq_methods_generic::<&str>(&strings);
1866 }
1867
1868 #[test]
1869 fn test_eq_methods_floats() {
1870 let floats = [
1871 -1.0,
1872 2.5,
1873 0.0,
1874 3.0,
1875 core::f64::consts::PI,
1876 10.0,
1877 10.0 / 3.0,
1878 -1_000_000.0,
1879 ];
1880 test_eq_methods_generic::<f64>(&floats);
1881 }
1882
1883 #[test]
1884 fn test_eq_methods_bools() {
1885 let bools = [true, false];
1886 test_eq_methods_generic::<bool>(&bools);
1887 }
1888
1889 #[test]
1890 fn test_rich_compare_type_error() {
1891 Python::attach(|py| {
1892 let py_int = 1i32.into_pyobject(py).unwrap();
1893 let py_str = "1".into_pyobject(py).unwrap();
1894
1895 assert!(py_int.rich_compare(&py_str, CompareOp::Lt).is_err());
1896 assert!(!py_int
1897 .rich_compare(py_str, CompareOp::Eq)
1898 .unwrap()
1899 .is_truthy()
1900 .unwrap());
1901 })
1902 }
1903
1904 #[test]
1905 fn test_is_callable() {
1906 Python::attach(|py| {
1907 assert!(PyList::type_object(py).is_callable());
1908
1909 let not_callable = 5i32.into_pyobject(py).unwrap();
1910 assert!(!not_callable.is_callable());
1911 });
1912 }
1913
1914 #[test]
1915 fn test_is_empty() {
1916 Python::attach(|py| {
1917 let empty_list = PyList::empty(py).into_any();
1918 assert!(empty_list.is_empty().unwrap());
1919
1920 let list = PyList::new(py, vec![1, 2, 3]).unwrap().into_any();
1921 assert!(!list.is_empty().unwrap());
1922
1923 let not_container = 5i32.into_pyobject(py).unwrap();
1924 assert!(not_container.is_empty().is_err());
1925 });
1926 }
1927
1928 #[cfg(feature = "macros")]
1929 #[test]
1930 #[allow(unknown_lints, non_local_definitions)]
1931 fn test_fallible_dir() {
1932 use crate::exceptions::PyValueError;
1933 use crate::prelude::*;
1934
1935 #[pyclass(crate = "crate")]
1936 struct DirFail;
1937
1938 #[pymethods(crate = "crate")]
1939 impl DirFail {
1940 fn __dir__(&self) -> PyResult<Py<PyAny>> {
1941 Err(PyValueError::new_err("uh-oh!"))
1942 }
1943 }
1944
1945 Python::attach(|py| {
1946 let obj = Bound::new(py, DirFail).unwrap();
1947 assert!(obj.dir().unwrap_err().is_instance_of::<PyValueError>(py));
1948 })
1949 }
1950}