rubyx-py 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +61 -6
- data/ext/rubyx/src/lib.rs +1 -0
- data/ext/rubyx/src/python_api.rs +1027 -0
- data/ext/rubyx/src/rubyx_object.rs +1075 -8
- data/ext/rubyx/src/rubyx_stream.rs +168 -0
- data/ext/rubyx/src/stream.rs +148 -2
- data/lib/rubyx/railtie.rb +2 -1
- data/lib/rubyx/tasks/rubyx.rake +127 -0
- data/lib/rubyx/version.rb +2 -2
- metadata +2 -1
data/ext/rubyx/src/python_api.rs
CHANGED
|
@@ -52,6 +52,29 @@ pub struct PythonApi {
|
|
|
52
52
|
Symbol<'static, unsafe extern "C" fn(*mut PyObject, *mut Py_ssize_t) -> *const c_char>,
|
|
53
53
|
py_unicode_type: *mut PyObject,
|
|
54
54
|
|
|
55
|
+
// bytes
|
|
56
|
+
py_bytes_type: *mut PyObject,
|
|
57
|
+
py_bytes_from_string_and_size:
|
|
58
|
+
Symbol<'static, unsafe extern "C" fn(v: *const c_char, len: Py_ssize_t) -> *mut PyObject>,
|
|
59
|
+
py_bytes_as_string_and_size: Symbol<
|
|
60
|
+
'static,
|
|
61
|
+
unsafe extern "C" fn(
|
|
62
|
+
o: *mut PyObject,
|
|
63
|
+
buffer: *mut *mut c_char,
|
|
64
|
+
len: *mut Py_ssize_t,
|
|
65
|
+
) -> c_int,
|
|
66
|
+
>,
|
|
67
|
+
py_bytes_size: Symbol<'static, unsafe extern "C" fn(o: *mut PyObject) -> Py_ssize_t>,
|
|
68
|
+
|
|
69
|
+
// bytearray
|
|
70
|
+
py_bytearray_type: *mut PyObject,
|
|
71
|
+
py_bytearray_from_object:
|
|
72
|
+
Symbol<'static, unsafe extern "C" fn(o: *mut PyObject) -> *mut PyObject>,
|
|
73
|
+
py_bytearray_from_string_and_size:
|
|
74
|
+
Symbol<'static, unsafe extern "C" fn(str: *const c_char, len: Py_ssize_t) -> *mut PyObject>,
|
|
75
|
+
py_bytearray_size: Symbol<'static, unsafe extern "C" fn(o: *mut PyObject) -> Py_ssize_t>,
|
|
76
|
+
py_bytearray_as_string: Symbol<'static, unsafe extern "C" fn(o: *mut PyObject) -> *mut c_char>,
|
|
77
|
+
|
|
55
78
|
// Collection Functions
|
|
56
79
|
|
|
57
80
|
// Tuple
|
|
@@ -65,6 +88,13 @@ pub struct PythonApi {
|
|
|
65
88
|
unsafe extern "C" fn(p: *mut PyObject, pos: isize, o: *mut PyObject) -> c_int,
|
|
66
89
|
>,
|
|
67
90
|
|
|
91
|
+
// Set
|
|
92
|
+
pub py_set_new: Symbol<'static, unsafe extern "C" fn(len: isize) -> *mut PyObject>,
|
|
93
|
+
pub py_set_type: *mut PyObject,
|
|
94
|
+
pub py_set_size: Symbol<'static, unsafe extern "C" fn(any_set: *mut PyObject) -> isize>,
|
|
95
|
+
pub py_frozen_set_new: Symbol<'static, unsafe extern "C" fn(len: isize) -> *mut PyObject>,
|
|
96
|
+
pub py_frozen_set_type: *mut PyObject,
|
|
97
|
+
|
|
68
98
|
// List
|
|
69
99
|
pub py_list_new: Symbol<'static, unsafe extern "C" fn(isize) -> *mut PyObject>,
|
|
70
100
|
pub py_list_size: Symbol<'static, unsafe extern "C" fn(*mut PyObject) -> isize>,
|
|
@@ -251,6 +281,34 @@ impl PythonApi {
|
|
|
251
281
|
unsafe extern "C" fn(*mut PyObject, *mut Py_ssize_t) -> *const c_char,
|
|
252
282
|
> = lib.get(b"PyUnicode_AsUTF8AndSize")?;
|
|
253
283
|
let py_unicode_type: *mut PyObject = *lib.get::<*mut PyObject>(b"PyUnicode_Type")?;
|
|
284
|
+
|
|
285
|
+
// bytes
|
|
286
|
+
let py_bytes_type = *lib.get::<*mut PyObject>(b"PyBytes_Type")?;
|
|
287
|
+
let py_bytes_from_string_and_size: Symbol<
|
|
288
|
+
unsafe extern "C" fn(*const c_char, Py_ssize_t) -> *mut PyObject,
|
|
289
|
+
> = lib.get(b"PyBytes_FromStringAndSize")?;
|
|
290
|
+
let py_bytes_as_string_and_size: Symbol<
|
|
291
|
+
unsafe extern "C" fn(
|
|
292
|
+
o: *mut PyObject,
|
|
293
|
+
buffer: *mut *mut c_char,
|
|
294
|
+
len: *mut Py_ssize_t,
|
|
295
|
+
) -> c_int,
|
|
296
|
+
> = lib.get(b"PyBytes_AsStringAndSize")?;
|
|
297
|
+
let py_bytes_size: Symbol<unsafe extern "C" fn(*mut PyObject) -> Py_ssize_t> =
|
|
298
|
+
lib.get(b"PyBytes_Size")?;
|
|
299
|
+
|
|
300
|
+
// bytearray
|
|
301
|
+
let py_bytearray_type = *lib.get::<*mut PyObject>(b"PyByteArray_Type")?;
|
|
302
|
+
let py_bytearray_from_object: Symbol<unsafe extern "C" fn(*mut PyObject) -> *mut PyObject> =
|
|
303
|
+
lib.get(b"PyByteArray_FromObject")?;
|
|
304
|
+
let py_bytearray_from_string_and_size: Symbol<
|
|
305
|
+
unsafe extern "C" fn(*const c_char, Py_ssize_t) -> *mut PyObject,
|
|
306
|
+
> = lib.get(b"PyByteArray_FromStringAndSize")?;
|
|
307
|
+
let py_bytearray_size: Symbol<unsafe extern "C" fn(*mut PyObject) -> Py_ssize_t> =
|
|
308
|
+
lib.get(b"PyByteArray_Size")?;
|
|
309
|
+
let py_bytearray_as_string: Symbol<unsafe extern "C" fn(*mut PyObject) -> *mut c_char> =
|
|
310
|
+
lib.get(b"PyByteArray_AsString")?;
|
|
311
|
+
|
|
254
312
|
// Collection Functions
|
|
255
313
|
// Tuple
|
|
256
314
|
let py_tuple_new: Symbol<unsafe extern "C" fn(isize) -> *mut PyObject> =
|
|
@@ -264,6 +322,16 @@ impl PythonApi {
|
|
|
264
322
|
unsafe extern "C" fn(*mut PyObject, isize, *mut PyObject) -> c_int,
|
|
265
323
|
> = lib.get(b"PyTuple_SetItem")?;
|
|
266
324
|
|
|
325
|
+
// Set
|
|
326
|
+
let py_set_new: Symbol<unsafe extern "C" fn(isize) -> *mut PyObject> =
|
|
327
|
+
lib.get(b"PySet_New")?;
|
|
328
|
+
let py_set_size: Symbol<unsafe extern "C" fn(*mut PyObject) -> isize> =
|
|
329
|
+
lib.get(b"PySet_Size")?;
|
|
330
|
+
let py_set_type: *mut PyObject = *lib.get::<*mut PyObject>(b"PySet_Type")?;
|
|
331
|
+
let py_frozen_set_new: Symbol<unsafe extern "C" fn(isize) -> *mut PyObject> =
|
|
332
|
+
lib.get(b"PyFrozenSet_New")?;
|
|
333
|
+
let py_frozen_set_type: *mut PyObject = *lib.get::<*mut PyObject>(b"PyFrozenSet_Type")?;
|
|
334
|
+
|
|
267
335
|
// List
|
|
268
336
|
let py_list_new: Symbol<unsafe extern "C" fn(isize) -> *mut PyObject> =
|
|
269
337
|
lib.get(b"PyList_New")?;
|
|
@@ -429,6 +497,22 @@ impl PythonApi {
|
|
|
429
497
|
py_unicode_from_string_and_size: std::mem::transmute(py_unicode_from_string_and_size),
|
|
430
498
|
py_unicode_as_utf8_and_size: std::mem::transmute(py_unicode_as_utf8_and_size),
|
|
431
499
|
py_unicode_type,
|
|
500
|
+
|
|
501
|
+
// bytes
|
|
502
|
+
py_bytes_type,
|
|
503
|
+
py_bytes_from_string_and_size: std::mem::transmute(py_bytes_from_string_and_size),
|
|
504
|
+
py_bytes_as_string_and_size: std::mem::transmute(py_bytes_as_string_and_size),
|
|
505
|
+
py_bytes_size: std::mem::transmute(py_bytes_size),
|
|
506
|
+
|
|
507
|
+
// bytearray
|
|
508
|
+
py_bytearray_type,
|
|
509
|
+
py_bytearray_from_object: std::mem::transmute(py_bytearray_from_object),
|
|
510
|
+
py_bytearray_from_string_and_size: std::mem::transmute(
|
|
511
|
+
py_bytearray_from_string_and_size,
|
|
512
|
+
),
|
|
513
|
+
py_bytearray_size: std::mem::transmute(py_bytearray_size),
|
|
514
|
+
py_bytearray_as_string: std::mem::transmute(py_bytearray_as_string),
|
|
515
|
+
|
|
432
516
|
// Collections
|
|
433
517
|
// Tuple
|
|
434
518
|
py_tuple_new: std::mem::transmute(py_tuple_new),
|
|
@@ -436,6 +520,12 @@ impl PythonApi {
|
|
|
436
520
|
py_tuple_size: std::mem::transmute(py_tuple_size),
|
|
437
521
|
py_tuple_get_item: std::mem::transmute(py_tuple_get_item),
|
|
438
522
|
py_tuple_set_item: std::mem::transmute(py_tuple_set_item),
|
|
523
|
+
// Set
|
|
524
|
+
py_set_new: std::mem::transmute(py_set_new),
|
|
525
|
+
py_set_size: std::mem::transmute(py_set_size),
|
|
526
|
+
py_set_type,
|
|
527
|
+
py_frozen_set_new: std::mem::transmute(py_frozen_set_new),
|
|
528
|
+
py_frozen_set_type,
|
|
439
529
|
// List
|
|
440
530
|
py_list_new: std::mem::transmute(py_list_new),
|
|
441
531
|
py_list_set_item: std::mem::transmute(py_list_set_item),
|
|
@@ -712,6 +802,75 @@ impl PythonApi {
|
|
|
712
802
|
}
|
|
713
803
|
}
|
|
714
804
|
|
|
805
|
+
pub fn bytes_check(&self, obj: *mut PyObject) -> bool {
|
|
806
|
+
if obj.is_null() {
|
|
807
|
+
return false;
|
|
808
|
+
}
|
|
809
|
+
unsafe { (self.py_object_is_instance)(obj, self.py_bytes_type) == 1 }
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
pub fn bytes_from_slice(&self, data: &[u8]) -> *mut PyObject {
|
|
813
|
+
let size = data.len() as Py_ssize_t;
|
|
814
|
+
unsafe { (self.py_bytes_from_string_and_size)(data.as_ptr() as *const c_char, size) }
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
pub fn bytes_to_vec(&self, obj: *mut PyObject) -> Option<Vec<u8>> {
|
|
818
|
+
if !self.bytes_check(obj) {
|
|
819
|
+
return None;
|
|
820
|
+
}
|
|
821
|
+
let mut size: Py_ssize_t = 0;
|
|
822
|
+
let mut buffer: *mut c_char = std::ptr::null_mut();
|
|
823
|
+
let result = unsafe { (self.py_bytes_as_string_and_size)(obj, &mut buffer, &mut size) };
|
|
824
|
+
if result != 0 {
|
|
825
|
+
if self.has_error() {
|
|
826
|
+
self.clear_error();
|
|
827
|
+
}
|
|
828
|
+
return None;
|
|
829
|
+
}
|
|
830
|
+
if buffer.is_null() {
|
|
831
|
+
return None;
|
|
832
|
+
}
|
|
833
|
+
let slice = unsafe { std::slice::from_raw_parts(buffer as *const u8, size as usize) };
|
|
834
|
+
Some(slice.to_vec())
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
pub fn bytearray_check(&self, obj: *mut PyObject) -> bool {
|
|
838
|
+
if obj.is_null() {
|
|
839
|
+
return false;
|
|
840
|
+
}
|
|
841
|
+
unsafe { (self.py_object_is_instance)(obj, self.py_bytearray_type) == 1 }
|
|
842
|
+
}
|
|
843
|
+
pub fn bytearray_from_slice(&self, data: &[u8]) -> *mut PyObject {
|
|
844
|
+
let size = data.len() as Py_ssize_t;
|
|
845
|
+
unsafe { (self.py_bytearray_from_string_and_size)(data.as_ptr() as *const c_char, size) }
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
pub fn bytearray_to_vec(&self, obj: *mut PyObject) -> Option<Vec<u8>> {
|
|
849
|
+
if !self.bytearray_check(obj) {
|
|
850
|
+
return None;
|
|
851
|
+
}
|
|
852
|
+
let size = unsafe { (self.py_bytearray_size)(obj) };
|
|
853
|
+
if size < 0 {
|
|
854
|
+
if self.has_error() {
|
|
855
|
+
self.clear_error();
|
|
856
|
+
}
|
|
857
|
+
return None;
|
|
858
|
+
}
|
|
859
|
+
if size == 0 {
|
|
860
|
+
return Some(Vec::new());
|
|
861
|
+
}
|
|
862
|
+
let buffer = unsafe { (self.py_bytearray_as_string)(obj) };
|
|
863
|
+
|
|
864
|
+
if buffer.is_null() {
|
|
865
|
+
if self.has_error() {
|
|
866
|
+
self.clear_error();
|
|
867
|
+
}
|
|
868
|
+
return None;
|
|
869
|
+
}
|
|
870
|
+
let slice = unsafe { std::slice::from_raw_parts(buffer as *const u8, size as usize) };
|
|
871
|
+
Some(slice.to_vec())
|
|
872
|
+
}
|
|
873
|
+
|
|
715
874
|
pub fn tuple_new(&self, size: isize) -> *mut PyObject {
|
|
716
875
|
unsafe { (self.py_tuple_new)(size) }
|
|
717
876
|
}
|
|
@@ -738,6 +897,35 @@ impl PythonApi {
|
|
|
738
897
|
unsafe { (self.py_tuple_get_item)(obj, pos) }
|
|
739
898
|
}
|
|
740
899
|
|
|
900
|
+
pub fn set_new(&self, size: isize) -> *mut PyObject {
|
|
901
|
+
unsafe { (self.py_set_new)(size) }
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
pub fn set_size(&self, obj: *mut PyObject) -> isize {
|
|
905
|
+
if obj.is_null() {
|
|
906
|
+
return 0;
|
|
907
|
+
}
|
|
908
|
+
unsafe { (self.py_set_size)(obj) }
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
pub fn is_set(&self, obj: *mut PyObject) -> bool {
|
|
912
|
+
if obj.is_null() {
|
|
913
|
+
return false;
|
|
914
|
+
}
|
|
915
|
+
unsafe { (self.py_object_is_instance)(obj, self.py_set_type) == 1 }
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
pub fn frozen_set_new(&self, size: isize) -> *mut PyObject {
|
|
919
|
+
unsafe { (self.py_frozen_set_new)(size) }
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
pub fn is_frozen_set(&self, obj: *mut PyObject) -> bool {
|
|
923
|
+
if obj.is_null() {
|
|
924
|
+
return false;
|
|
925
|
+
}
|
|
926
|
+
unsafe { (self.py_object_is_instance)(obj, self.py_frozen_set_type) == 1 }
|
|
927
|
+
}
|
|
928
|
+
|
|
741
929
|
pub fn list_new(&self, size: isize) -> *mut PyObject {
|
|
742
930
|
unsafe { (self.py_list_new)(size) }
|
|
743
931
|
}
|
|
@@ -6026,4 +6214,843 @@ _loop = asyncio.new_event_loop()
|
|
|
6026
6214
|
|
|
6027
6215
|
api.decref(py_int);
|
|
6028
6216
|
}
|
|
6217
|
+
|
|
6218
|
+
// ========== set / frozenset ==========
|
|
6219
|
+
|
|
6220
|
+
#[test]
|
|
6221
|
+
#[serial]
|
|
6222
|
+
fn test_is_set_returns_true_for_set() {
|
|
6223
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6224
|
+
return;
|
|
6225
|
+
};
|
|
6226
|
+
let api = guard.api();
|
|
6227
|
+
|
|
6228
|
+
let globals = make_globals(api);
|
|
6229
|
+
let py_set = api
|
|
6230
|
+
.run_string("{1, 2, 3}", PY_EVAL_INPUT, globals, globals)
|
|
6231
|
+
.expect("set eval should succeed");
|
|
6232
|
+
|
|
6233
|
+
assert!(api.is_set(py_set), "should detect set");
|
|
6234
|
+
assert!(!api.is_frozen_set(py_set), "set is not frozenset");
|
|
6235
|
+
|
|
6236
|
+
api.decref(py_set);
|
|
6237
|
+
api.decref(globals);
|
|
6238
|
+
}
|
|
6239
|
+
|
|
6240
|
+
#[test]
|
|
6241
|
+
#[serial]
|
|
6242
|
+
fn test_is_frozen_set_returns_true_for_frozenset() {
|
|
6243
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6244
|
+
return;
|
|
6245
|
+
};
|
|
6246
|
+
let api = guard.api();
|
|
6247
|
+
|
|
6248
|
+
let globals = make_globals(api);
|
|
6249
|
+
let py_fset = api
|
|
6250
|
+
.run_string("frozenset({10, 20})", PY_EVAL_INPUT, globals, globals)
|
|
6251
|
+
.expect("frozenset eval should succeed");
|
|
6252
|
+
|
|
6253
|
+
assert!(api.is_frozen_set(py_fset), "should detect frozenset");
|
|
6254
|
+
assert!(!api.is_set(py_fset), "frozenset is not set");
|
|
6255
|
+
|
|
6256
|
+
api.decref(py_fset);
|
|
6257
|
+
api.decref(globals);
|
|
6258
|
+
}
|
|
6259
|
+
|
|
6260
|
+
#[test]
|
|
6261
|
+
#[serial]
|
|
6262
|
+
fn test_is_set_returns_false_for_non_sets() {
|
|
6263
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6264
|
+
return;
|
|
6265
|
+
};
|
|
6266
|
+
let api = guard.api();
|
|
6267
|
+
|
|
6268
|
+
let py_int = api.long_from_i64(42);
|
|
6269
|
+
assert!(!api.is_set(py_int));
|
|
6270
|
+
assert!(!api.is_frozen_set(py_int));
|
|
6271
|
+
|
|
6272
|
+
let py_str = api.string_from_str("hello");
|
|
6273
|
+
assert!(!api.is_set(py_str));
|
|
6274
|
+
assert!(!api.is_frozen_set(py_str));
|
|
6275
|
+
|
|
6276
|
+
let py_list = api.list_new(0);
|
|
6277
|
+
assert!(!api.is_set(py_list));
|
|
6278
|
+
assert!(!api.is_frozen_set(py_list));
|
|
6279
|
+
|
|
6280
|
+
let py_dict = api.dict_new();
|
|
6281
|
+
assert!(!api.is_set(py_dict));
|
|
6282
|
+
assert!(!api.is_frozen_set(py_dict));
|
|
6283
|
+
|
|
6284
|
+
api.decref(py_int);
|
|
6285
|
+
api.decref(py_str);
|
|
6286
|
+
api.decref(py_list);
|
|
6287
|
+
api.decref(py_dict);
|
|
6288
|
+
}
|
|
6289
|
+
|
|
6290
|
+
#[test]
|
|
6291
|
+
#[serial]
|
|
6292
|
+
fn test_set_new_creates_empty_set() {
|
|
6293
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6294
|
+
return;
|
|
6295
|
+
};
|
|
6296
|
+
let api = guard.api();
|
|
6297
|
+
|
|
6298
|
+
let py_set = api.set_new(0);
|
|
6299
|
+
assert!(!py_set.is_null());
|
|
6300
|
+
assert!(api.is_set(py_set));
|
|
6301
|
+
|
|
6302
|
+
api.decref(py_set);
|
|
6303
|
+
}
|
|
6304
|
+
|
|
6305
|
+
#[test]
|
|
6306
|
+
#[serial]
|
|
6307
|
+
fn test_iterate_set_via_get_iter() {
|
|
6308
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6309
|
+
return;
|
|
6310
|
+
};
|
|
6311
|
+
let api = guard.api();
|
|
6312
|
+
|
|
6313
|
+
let globals = make_globals(api);
|
|
6314
|
+
let py_set = api
|
|
6315
|
+
.run_string("{10, 20, 30}", PY_EVAL_INPUT, globals, globals)
|
|
6316
|
+
.expect("set eval should succeed");
|
|
6317
|
+
|
|
6318
|
+
let py_iter = api.object_get_iter(py_set);
|
|
6319
|
+
assert!(!py_iter.is_null(), "set should be iterable");
|
|
6320
|
+
|
|
6321
|
+
let mut values = vec![];
|
|
6322
|
+
loop {
|
|
6323
|
+
let item = api.iter_next(py_iter);
|
|
6324
|
+
if item.is_null() {
|
|
6325
|
+
break;
|
|
6326
|
+
}
|
|
6327
|
+
values.push(api.long_to_i64(item));
|
|
6328
|
+
api.decref(item);
|
|
6329
|
+
}
|
|
6330
|
+
assert!(!api.has_error());
|
|
6331
|
+
|
|
6332
|
+
values.sort();
|
|
6333
|
+
assert_eq!(values, vec![10, 20, 30]);
|
|
6334
|
+
|
|
6335
|
+
api.decref(py_iter);
|
|
6336
|
+
api.decref(py_set);
|
|
6337
|
+
api.decref(globals);
|
|
6338
|
+
}
|
|
6339
|
+
|
|
6340
|
+
#[test]
|
|
6341
|
+
#[serial]
|
|
6342
|
+
fn test_iterate_frozenset_via_get_iter() {
|
|
6343
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6344
|
+
return;
|
|
6345
|
+
};
|
|
6346
|
+
let api = guard.api();
|
|
6347
|
+
|
|
6348
|
+
let globals = make_globals(api);
|
|
6349
|
+
let py_fset = api
|
|
6350
|
+
.run_string("frozenset({5, 15})", PY_EVAL_INPUT, globals, globals)
|
|
6351
|
+
.expect("frozenset eval should succeed");
|
|
6352
|
+
|
|
6353
|
+
let py_iter = api.object_get_iter(py_fset);
|
|
6354
|
+
assert!(!py_iter.is_null(), "frozenset should be iterable");
|
|
6355
|
+
|
|
6356
|
+
let mut values = vec![];
|
|
6357
|
+
loop {
|
|
6358
|
+
let item = api.iter_next(py_iter);
|
|
6359
|
+
if item.is_null() {
|
|
6360
|
+
break;
|
|
6361
|
+
}
|
|
6362
|
+
values.push(api.long_to_i64(item));
|
|
6363
|
+
api.decref(item);
|
|
6364
|
+
}
|
|
6365
|
+
assert!(!api.has_error());
|
|
6366
|
+
|
|
6367
|
+
values.sort();
|
|
6368
|
+
assert_eq!(values, vec![5, 15]);
|
|
6369
|
+
|
|
6370
|
+
api.decref(py_iter);
|
|
6371
|
+
api.decref(py_fset);
|
|
6372
|
+
api.decref(globals);
|
|
6373
|
+
}
|
|
6374
|
+
|
|
6375
|
+
#[test]
|
|
6376
|
+
#[serial]
|
|
6377
|
+
fn test_iterate_empty_set() {
|
|
6378
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6379
|
+
return;
|
|
6380
|
+
};
|
|
6381
|
+
let api = guard.api();
|
|
6382
|
+
|
|
6383
|
+
let py_set = api.set_new(0);
|
|
6384
|
+
assert!(!py_set.is_null());
|
|
6385
|
+
|
|
6386
|
+
let py_iter = api.object_get_iter(py_set);
|
|
6387
|
+
assert!(!py_iter.is_null());
|
|
6388
|
+
|
|
6389
|
+
let item = api.iter_next(py_iter);
|
|
6390
|
+
assert!(item.is_null(), "empty set should yield nothing");
|
|
6391
|
+
assert!(!api.has_error());
|
|
6392
|
+
|
|
6393
|
+
api.decref(py_iter);
|
|
6394
|
+
api.decref(py_set);
|
|
6395
|
+
}
|
|
6396
|
+
|
|
6397
|
+
#[test]
|
|
6398
|
+
#[serial]
|
|
6399
|
+
fn test_set_size() {
|
|
6400
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6401
|
+
return;
|
|
6402
|
+
};
|
|
6403
|
+
let api = guard.api();
|
|
6404
|
+
|
|
6405
|
+
let globals = make_globals(api);
|
|
6406
|
+
let py_set = api
|
|
6407
|
+
.run_string("{1, 2, 3, 4, 5}", PY_EVAL_INPUT, globals, globals)
|
|
6408
|
+
.expect("set eval should succeed");
|
|
6409
|
+
|
|
6410
|
+
let size = unsafe { (api.py_set_size)(py_set) };
|
|
6411
|
+
assert_eq!(size, 5);
|
|
6412
|
+
|
|
6413
|
+
api.decref(py_set);
|
|
6414
|
+
api.decref(globals);
|
|
6415
|
+
}
|
|
6416
|
+
|
|
6417
|
+
// ========== Bytes operations ==========
|
|
6418
|
+
|
|
6419
|
+
#[test]
|
|
6420
|
+
#[serial]
|
|
6421
|
+
fn test_bytes_from_slice_creates_object() {
|
|
6422
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6423
|
+
return;
|
|
6424
|
+
};
|
|
6425
|
+
let api = guard.api();
|
|
6426
|
+
|
|
6427
|
+
let py_bytes = api.bytes_from_slice(b"hello");
|
|
6428
|
+
assert!(!py_bytes.is_null(), "Should create a Python bytes object");
|
|
6429
|
+
assert!(api.bytes_check(py_bytes), "Should pass bytes_check");
|
|
6430
|
+
api.decref(py_bytes);
|
|
6431
|
+
}
|
|
6432
|
+
|
|
6433
|
+
#[test]
|
|
6434
|
+
#[serial]
|
|
6435
|
+
fn test_bytes_roundtrip() {
|
|
6436
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6437
|
+
return;
|
|
6438
|
+
};
|
|
6439
|
+
let api = guard.api();
|
|
6440
|
+
|
|
6441
|
+
for data in [
|
|
6442
|
+
b"hello" as &[u8],
|
|
6443
|
+
b"world",
|
|
6444
|
+
b"with spaces",
|
|
6445
|
+
b"unicode: caf\xc3\xa9",
|
|
6446
|
+
] {
|
|
6447
|
+
let py_bytes = api.bytes_from_slice(data);
|
|
6448
|
+
assert!(!py_bytes.is_null(), "Failed to create bytes for {:?}", data);
|
|
6449
|
+
|
|
6450
|
+
let back = api.bytes_to_vec(py_bytes);
|
|
6451
|
+
assert_eq!(
|
|
6452
|
+
back.as_deref(),
|
|
6453
|
+
Some(data),
|
|
6454
|
+
"Roundtrip failed for {:?}",
|
|
6455
|
+
data
|
|
6456
|
+
);
|
|
6457
|
+
api.decref(py_bytes);
|
|
6458
|
+
}
|
|
6459
|
+
}
|
|
6460
|
+
|
|
6461
|
+
#[test]
|
|
6462
|
+
#[serial]
|
|
6463
|
+
fn test_bytes_empty() {
|
|
6464
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6465
|
+
return;
|
|
6466
|
+
};
|
|
6467
|
+
let api = guard.api();
|
|
6468
|
+
|
|
6469
|
+
let py_bytes = api.bytes_from_slice(b"");
|
|
6470
|
+
assert!(!py_bytes.is_null(), "Should create empty Python bytes");
|
|
6471
|
+
assert!(api.bytes_check(py_bytes));
|
|
6472
|
+
|
|
6473
|
+
let back = api.bytes_to_vec(py_bytes);
|
|
6474
|
+
assert_eq!(back, Some(Vec::new()), "Empty bytes should roundtrip");
|
|
6475
|
+
api.decref(py_bytes);
|
|
6476
|
+
}
|
|
6477
|
+
|
|
6478
|
+
#[test]
|
|
6479
|
+
#[serial]
|
|
6480
|
+
fn test_bytes_with_null_bytes() {
|
|
6481
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6482
|
+
return;
|
|
6483
|
+
};
|
|
6484
|
+
let api = guard.api();
|
|
6485
|
+
|
|
6486
|
+
let data: &[u8] = b"\x00\x01\x02\x00\xff\xfe\x00";
|
|
6487
|
+
let py_bytes = api.bytes_from_slice(data);
|
|
6488
|
+
assert!(!py_bytes.is_null());
|
|
6489
|
+
|
|
6490
|
+
let back = api.bytes_to_vec(py_bytes);
|
|
6491
|
+
assert_eq!(
|
|
6492
|
+
back.as_deref(),
|
|
6493
|
+
Some(data),
|
|
6494
|
+
"Bytes with embedded NULs must roundtrip exactly"
|
|
6495
|
+
);
|
|
6496
|
+
api.decref(py_bytes);
|
|
6497
|
+
}
|
|
6498
|
+
|
|
6499
|
+
#[test]
|
|
6500
|
+
#[serial]
|
|
6501
|
+
fn test_bytes_with_all_byte_values() {
|
|
6502
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6503
|
+
return;
|
|
6504
|
+
};
|
|
6505
|
+
let api = guard.api();
|
|
6506
|
+
|
|
6507
|
+
let data: Vec<u8> = (0..=255).collect();
|
|
6508
|
+
let py_bytes = api.bytes_from_slice(&data);
|
|
6509
|
+
assert!(!py_bytes.is_null());
|
|
6510
|
+
|
|
6511
|
+
let back = api.bytes_to_vec(py_bytes);
|
|
6512
|
+
assert_eq!(
|
|
6513
|
+
back.as_deref(),
|
|
6514
|
+
Some(data.as_slice()),
|
|
6515
|
+
"All 256 byte values must roundtrip"
|
|
6516
|
+
);
|
|
6517
|
+
api.decref(py_bytes);
|
|
6518
|
+
}
|
|
6519
|
+
|
|
6520
|
+
#[test]
|
|
6521
|
+
#[serial]
|
|
6522
|
+
fn test_bytes_check_type_discrimination() {
|
|
6523
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6524
|
+
return;
|
|
6525
|
+
};
|
|
6526
|
+
let api = guard.api();
|
|
6527
|
+
|
|
6528
|
+
let py_bytes = api.bytes_from_slice(b"test");
|
|
6529
|
+
let py_str = api.string_from_str("test");
|
|
6530
|
+
let py_int = api.long_from_i64(42);
|
|
6531
|
+
let py_ba = api.bytearray_from_slice(b"test");
|
|
6532
|
+
|
|
6533
|
+
assert!(api.bytes_check(py_bytes), "bytes should pass bytes_check");
|
|
6534
|
+
assert!(!api.bytes_check(py_str), "str should fail bytes_check");
|
|
6535
|
+
assert!(!api.bytes_check(py_int), "int should fail bytes_check");
|
|
6536
|
+
assert!(!api.bytes_check(py_ba), "bytearray should fail bytes_check");
|
|
6537
|
+
|
|
6538
|
+
api.decref(py_bytes);
|
|
6539
|
+
api.decref(py_str);
|
|
6540
|
+
api.decref(py_int);
|
|
6541
|
+
api.decref(py_ba);
|
|
6542
|
+
}
|
|
6543
|
+
|
|
6544
|
+
#[test]
|
|
6545
|
+
#[serial]
|
|
6546
|
+
fn test_bytes_check_null_returns_false() {
|
|
6547
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6548
|
+
return;
|
|
6549
|
+
};
|
|
6550
|
+
assert!(!guard.api().bytes_check(std::ptr::null_mut()));
|
|
6551
|
+
}
|
|
6552
|
+
|
|
6553
|
+
#[test]
|
|
6554
|
+
#[serial]
|
|
6555
|
+
fn test_bytes_to_vec_returns_none_for_non_bytes() {
|
|
6556
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6557
|
+
return;
|
|
6558
|
+
};
|
|
6559
|
+
let api = guard.api();
|
|
6560
|
+
|
|
6561
|
+
let py_str = api.string_from_str("not bytes");
|
|
6562
|
+
assert!(api.bytes_to_vec(py_str).is_none());
|
|
6563
|
+
api.decref(py_str);
|
|
6564
|
+
|
|
6565
|
+
let py_int = api.long_from_i64(42);
|
|
6566
|
+
assert!(api.bytes_to_vec(py_int).is_none());
|
|
6567
|
+
api.decref(py_int);
|
|
6568
|
+
|
|
6569
|
+
assert!(api.bytes_to_vec(std::ptr::null_mut()).is_none());
|
|
6570
|
+
}
|
|
6571
|
+
|
|
6572
|
+
#[test]
|
|
6573
|
+
#[serial]
|
|
6574
|
+
fn test_bytes_size() {
|
|
6575
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6576
|
+
return;
|
|
6577
|
+
};
|
|
6578
|
+
let api = guard.api();
|
|
6579
|
+
|
|
6580
|
+
let py_empty = api.bytes_from_slice(b"");
|
|
6581
|
+
assert_eq!(
|
|
6582
|
+
unsafe { (api.py_bytes_size)(py_empty) },
|
|
6583
|
+
0,
|
|
6584
|
+
"Empty bytes should have size 0"
|
|
6585
|
+
);
|
|
6586
|
+
api.decref(py_empty);
|
|
6587
|
+
|
|
6588
|
+
let py_hello = api.bytes_from_slice(b"hello");
|
|
6589
|
+
assert_eq!(
|
|
6590
|
+
unsafe { (api.py_bytes_size)(py_hello) },
|
|
6591
|
+
5,
|
|
6592
|
+
"b\"hello\" should have size 5"
|
|
6593
|
+
);
|
|
6594
|
+
api.decref(py_hello);
|
|
6595
|
+
|
|
6596
|
+
let data_with_nulls = b"\x00\x00\x00";
|
|
6597
|
+
let py_nulls = api.bytes_from_slice(data_with_nulls);
|
|
6598
|
+
assert_eq!(
|
|
6599
|
+
unsafe { (api.py_bytes_size)(py_nulls) },
|
|
6600
|
+
3,
|
|
6601
|
+
"Size must count NUL bytes"
|
|
6602
|
+
);
|
|
6603
|
+
api.decref(py_nulls);
|
|
6604
|
+
}
|
|
6605
|
+
|
|
6606
|
+
#[test]
|
|
6607
|
+
#[serial]
|
|
6608
|
+
fn test_bytes_from_python_eval() {
|
|
6609
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6610
|
+
return;
|
|
6611
|
+
};
|
|
6612
|
+
let api = guard.api();
|
|
6613
|
+
let globals = make_globals(api);
|
|
6614
|
+
|
|
6615
|
+
let py_bytes = api
|
|
6616
|
+
.run_string("b'hello world'", PY_EVAL_INPUT, globals, globals)
|
|
6617
|
+
.expect("should eval bytes literal");
|
|
6618
|
+
assert!(!py_bytes.is_null());
|
|
6619
|
+
assert!(api.bytes_check(py_bytes));
|
|
6620
|
+
|
|
6621
|
+
let back = api.bytes_to_vec(py_bytes);
|
|
6622
|
+
assert_eq!(back, Some(b"hello world".to_vec()));
|
|
6623
|
+
|
|
6624
|
+
api.decref(py_bytes);
|
|
6625
|
+
api.decref(globals);
|
|
6626
|
+
}
|
|
6627
|
+
|
|
6628
|
+
#[test]
|
|
6629
|
+
#[serial]
|
|
6630
|
+
fn test_bytes_is_not_string() {
|
|
6631
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6632
|
+
return;
|
|
6633
|
+
};
|
|
6634
|
+
let api = guard.api();
|
|
6635
|
+
|
|
6636
|
+
let py_bytes = api.bytes_from_slice(b"hello");
|
|
6637
|
+
let py_str = api.string_from_str("hello");
|
|
6638
|
+
|
|
6639
|
+
assert!(!api.is_string(py_bytes), "bytes must not pass is_string");
|
|
6640
|
+
assert!(!api.bytes_check(py_str), "str must not pass bytes_check");
|
|
6641
|
+
|
|
6642
|
+
api.decref(py_bytes);
|
|
6643
|
+
api.decref(py_str);
|
|
6644
|
+
}
|
|
6645
|
+
|
|
6646
|
+
#[test]
|
|
6647
|
+
#[serial]
|
|
6648
|
+
fn test_bytes_large_payload() {
|
|
6649
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6650
|
+
return;
|
|
6651
|
+
};
|
|
6652
|
+
let api = guard.api();
|
|
6653
|
+
|
|
6654
|
+
let data: Vec<u8> = (0..10_000).map(|i| (i % 256) as u8).collect();
|
|
6655
|
+
let py_bytes = api.bytes_from_slice(&data);
|
|
6656
|
+
assert!(!py_bytes.is_null());
|
|
6657
|
+
|
|
6658
|
+
let back = api
|
|
6659
|
+
.bytes_to_vec(py_bytes)
|
|
6660
|
+
.expect("should extract large bytes");
|
|
6661
|
+
assert_eq!(back.len(), 10_000);
|
|
6662
|
+
assert_eq!(back, data);
|
|
6663
|
+
|
|
6664
|
+
api.decref(py_bytes);
|
|
6665
|
+
}
|
|
6666
|
+
|
|
6667
|
+
// ========== ByteArray operations ==========
|
|
6668
|
+
|
|
6669
|
+
#[test]
|
|
6670
|
+
#[serial]
|
|
6671
|
+
fn test_bytearray_from_slice_creates_object() {
|
|
6672
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6673
|
+
return;
|
|
6674
|
+
};
|
|
6675
|
+
let api = guard.api();
|
|
6676
|
+
|
|
6677
|
+
let py_ba = api.bytearray_from_slice(b"hello");
|
|
6678
|
+
assert!(!py_ba.is_null(), "Should create a Python bytearray object");
|
|
6679
|
+
assert!(api.bytearray_check(py_ba), "Should pass bytearray_check");
|
|
6680
|
+
api.decref(py_ba);
|
|
6681
|
+
}
|
|
6682
|
+
|
|
6683
|
+
#[test]
|
|
6684
|
+
#[serial]
|
|
6685
|
+
fn test_bytearray_roundtrip() {
|
|
6686
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6687
|
+
return;
|
|
6688
|
+
};
|
|
6689
|
+
let api = guard.api();
|
|
6690
|
+
|
|
6691
|
+
for data in [
|
|
6692
|
+
b"hello" as &[u8],
|
|
6693
|
+
b"world",
|
|
6694
|
+
b"with spaces",
|
|
6695
|
+
b"binary\xfe\xff",
|
|
6696
|
+
] {
|
|
6697
|
+
let py_ba = api.bytearray_from_slice(data);
|
|
6698
|
+
assert!(
|
|
6699
|
+
!py_ba.is_null(),
|
|
6700
|
+
"Failed to create bytearray for {:?}",
|
|
6701
|
+
data
|
|
6702
|
+
);
|
|
6703
|
+
|
|
6704
|
+
let back = api.bytearray_to_vec(py_ba);
|
|
6705
|
+
assert_eq!(
|
|
6706
|
+
back.as_deref(),
|
|
6707
|
+
Some(data),
|
|
6708
|
+
"Roundtrip failed for {:?}",
|
|
6709
|
+
data
|
|
6710
|
+
);
|
|
6711
|
+
api.decref(py_ba);
|
|
6712
|
+
}
|
|
6713
|
+
}
|
|
6714
|
+
|
|
6715
|
+
#[test]
|
|
6716
|
+
#[serial]
|
|
6717
|
+
fn test_bytearray_empty() {
|
|
6718
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6719
|
+
return;
|
|
6720
|
+
};
|
|
6721
|
+
let api = guard.api();
|
|
6722
|
+
|
|
6723
|
+
let py_ba = api.bytearray_from_slice(b"");
|
|
6724
|
+
assert!(!py_ba.is_null(), "Should create empty Python bytearray");
|
|
6725
|
+
assert!(api.bytearray_check(py_ba));
|
|
6726
|
+
|
|
6727
|
+
let back = api.bytearray_to_vec(py_ba);
|
|
6728
|
+
assert_eq!(back, Some(Vec::new()), "Empty bytearray should roundtrip");
|
|
6729
|
+
api.decref(py_ba);
|
|
6730
|
+
}
|
|
6731
|
+
|
|
6732
|
+
#[test]
|
|
6733
|
+
#[serial]
|
|
6734
|
+
fn test_bytearray_with_null_bytes() {
|
|
6735
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6736
|
+
return;
|
|
6737
|
+
};
|
|
6738
|
+
let api = guard.api();
|
|
6739
|
+
|
|
6740
|
+
let data: &[u8] = b"\x00\x01\x02\x00\xff\xfe\x00";
|
|
6741
|
+
let py_ba = api.bytearray_from_slice(data);
|
|
6742
|
+
assert!(!py_ba.is_null());
|
|
6743
|
+
|
|
6744
|
+
let back = api.bytearray_to_vec(py_ba);
|
|
6745
|
+
assert_eq!(
|
|
6746
|
+
back.as_deref(),
|
|
6747
|
+
Some(data),
|
|
6748
|
+
"Bytearray with embedded NULs must roundtrip exactly"
|
|
6749
|
+
);
|
|
6750
|
+
api.decref(py_ba);
|
|
6751
|
+
}
|
|
6752
|
+
|
|
6753
|
+
#[test]
|
|
6754
|
+
#[serial]
|
|
6755
|
+
fn test_bytearray_with_all_byte_values() {
|
|
6756
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6757
|
+
return;
|
|
6758
|
+
};
|
|
6759
|
+
let api = guard.api();
|
|
6760
|
+
|
|
6761
|
+
let data: Vec<u8> = (0..=255).collect();
|
|
6762
|
+
let py_ba = api.bytearray_from_slice(&data);
|
|
6763
|
+
assert!(!py_ba.is_null());
|
|
6764
|
+
|
|
6765
|
+
let back = api.bytearray_to_vec(py_ba);
|
|
6766
|
+
assert_eq!(
|
|
6767
|
+
back.as_deref(),
|
|
6768
|
+
Some(data.as_slice()),
|
|
6769
|
+
"All 256 byte values must roundtrip via bytearray"
|
|
6770
|
+
);
|
|
6771
|
+
api.decref(py_ba);
|
|
6772
|
+
}
|
|
6773
|
+
|
|
6774
|
+
#[test]
|
|
6775
|
+
#[serial]
|
|
6776
|
+
fn test_bytearray_check_type_discrimination() {
|
|
6777
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6778
|
+
return;
|
|
6779
|
+
};
|
|
6780
|
+
let api = guard.api();
|
|
6781
|
+
|
|
6782
|
+
let py_ba = api.bytearray_from_slice(b"test");
|
|
6783
|
+
let py_bytes = api.bytes_from_slice(b"test");
|
|
6784
|
+
let py_str = api.string_from_str("test");
|
|
6785
|
+
let py_int = api.long_from_i64(42);
|
|
6786
|
+
|
|
6787
|
+
assert!(
|
|
6788
|
+
api.bytearray_check(py_ba),
|
|
6789
|
+
"bytearray should pass bytearray_check"
|
|
6790
|
+
);
|
|
6791
|
+
assert!(
|
|
6792
|
+
!api.bytearray_check(py_bytes),
|
|
6793
|
+
"bytes should fail bytearray_check"
|
|
6794
|
+
);
|
|
6795
|
+
assert!(
|
|
6796
|
+
!api.bytearray_check(py_str),
|
|
6797
|
+
"str should fail bytearray_check"
|
|
6798
|
+
);
|
|
6799
|
+
assert!(
|
|
6800
|
+
!api.bytearray_check(py_int),
|
|
6801
|
+
"int should fail bytearray_check"
|
|
6802
|
+
);
|
|
6803
|
+
|
|
6804
|
+
api.decref(py_ba);
|
|
6805
|
+
api.decref(py_bytes);
|
|
6806
|
+
api.decref(py_str);
|
|
6807
|
+
api.decref(py_int);
|
|
6808
|
+
}
|
|
6809
|
+
|
|
6810
|
+
#[test]
|
|
6811
|
+
#[serial]
|
|
6812
|
+
fn test_bytearray_check_null_returns_false() {
|
|
6813
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6814
|
+
return;
|
|
6815
|
+
};
|
|
6816
|
+
assert!(!guard.api().bytearray_check(std::ptr::null_mut()));
|
|
6817
|
+
}
|
|
6818
|
+
|
|
6819
|
+
#[test]
|
|
6820
|
+
#[serial]
|
|
6821
|
+
fn test_bytearray_to_vec_returns_none_for_non_bytearray() {
|
|
6822
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6823
|
+
return;
|
|
6824
|
+
};
|
|
6825
|
+
let api = guard.api();
|
|
6826
|
+
|
|
6827
|
+
let py_str = api.string_from_str("not bytearray");
|
|
6828
|
+
assert!(api.bytearray_to_vec(py_str).is_none());
|
|
6829
|
+
api.decref(py_str);
|
|
6830
|
+
|
|
6831
|
+
let py_bytes = api.bytes_from_slice(b"not bytearray");
|
|
6832
|
+
assert!(api.bytearray_to_vec(py_bytes).is_none());
|
|
6833
|
+
api.decref(py_bytes);
|
|
6834
|
+
|
|
6835
|
+
assert!(api.bytearray_to_vec(std::ptr::null_mut()).is_none());
|
|
6836
|
+
}
|
|
6837
|
+
|
|
6838
|
+
#[test]
|
|
6839
|
+
#[serial]
|
|
6840
|
+
fn test_bytearray_size() {
|
|
6841
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6842
|
+
return;
|
|
6843
|
+
};
|
|
6844
|
+
let api = guard.api();
|
|
6845
|
+
|
|
6846
|
+
let py_empty = api.bytearray_from_slice(b"");
|
|
6847
|
+
assert_eq!(
|
|
6848
|
+
unsafe { (api.py_bytearray_size)(py_empty) },
|
|
6849
|
+
0,
|
|
6850
|
+
"Empty bytearray should have size 0"
|
|
6851
|
+
);
|
|
6852
|
+
api.decref(py_empty);
|
|
6853
|
+
|
|
6854
|
+
let py_hello = api.bytearray_from_slice(b"hello");
|
|
6855
|
+
assert_eq!(
|
|
6856
|
+
unsafe { (api.py_bytearray_size)(py_hello) },
|
|
6857
|
+
5,
|
|
6858
|
+
"bytearray(b\"hello\") should have size 5"
|
|
6859
|
+
);
|
|
6860
|
+
api.decref(py_hello);
|
|
6861
|
+
}
|
|
6862
|
+
|
|
6863
|
+
#[test]
|
|
6864
|
+
#[serial]
|
|
6865
|
+
fn test_bytearray_from_python_eval() {
|
|
6866
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6867
|
+
return;
|
|
6868
|
+
};
|
|
6869
|
+
let api = guard.api();
|
|
6870
|
+
let globals = make_globals(api);
|
|
6871
|
+
|
|
6872
|
+
let py_ba = api
|
|
6873
|
+
.run_string("bytearray(b'hello world')", PY_EVAL_INPUT, globals, globals)
|
|
6874
|
+
.expect("should eval bytearray");
|
|
6875
|
+
assert!(!py_ba.is_null());
|
|
6876
|
+
assert!(api.bytearray_check(py_ba));
|
|
6877
|
+
|
|
6878
|
+
let back = api.bytearray_to_vec(py_ba);
|
|
6879
|
+
assert_eq!(back, Some(b"hello world".to_vec()));
|
|
6880
|
+
|
|
6881
|
+
api.decref(py_ba);
|
|
6882
|
+
api.decref(globals);
|
|
6883
|
+
}
|
|
6884
|
+
|
|
6885
|
+
#[test]
|
|
6886
|
+
#[serial]
|
|
6887
|
+
fn test_bytearray_from_object() {
|
|
6888
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6889
|
+
return;
|
|
6890
|
+
};
|
|
6891
|
+
let api = guard.api();
|
|
6892
|
+
|
|
6893
|
+
let py_bytes = api.bytes_from_slice(b"convert me");
|
|
6894
|
+
let py_ba = unsafe { (api.py_bytearray_from_object)(py_bytes) };
|
|
6895
|
+
assert!(!py_ba.is_null(), "Should create bytearray from bytes");
|
|
6896
|
+
assert!(api.bytearray_check(py_ba));
|
|
6897
|
+
|
|
6898
|
+
let back = api.bytearray_to_vec(py_ba);
|
|
6899
|
+
assert_eq!(back, Some(b"convert me".to_vec()));
|
|
6900
|
+
|
|
6901
|
+
api.decref(py_ba);
|
|
6902
|
+
api.decref(py_bytes);
|
|
6903
|
+
}
|
|
6904
|
+
|
|
6905
|
+
#[test]
|
|
6906
|
+
#[serial]
|
|
6907
|
+
fn test_bytearray_is_not_bytes() {
|
|
6908
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6909
|
+
return;
|
|
6910
|
+
};
|
|
6911
|
+
let api = guard.api();
|
|
6912
|
+
|
|
6913
|
+
let py_ba = api.bytearray_from_slice(b"hello");
|
|
6914
|
+
let py_bytes = api.bytes_from_slice(b"hello");
|
|
6915
|
+
|
|
6916
|
+
assert!(
|
|
6917
|
+
!api.bytes_check(py_ba),
|
|
6918
|
+
"bytearray must not pass bytes_check"
|
|
6919
|
+
);
|
|
6920
|
+
assert!(
|
|
6921
|
+
!api.bytearray_check(py_bytes),
|
|
6922
|
+
"bytes must not pass bytearray_check"
|
|
6923
|
+
);
|
|
6924
|
+
assert!(!api.is_string(py_ba), "bytearray must not pass is_string");
|
|
6925
|
+
|
|
6926
|
+
api.decref(py_ba);
|
|
6927
|
+
api.decref(py_bytes);
|
|
6928
|
+
}
|
|
6929
|
+
|
|
6930
|
+
#[test]
|
|
6931
|
+
#[serial]
|
|
6932
|
+
fn test_bytearray_large_payload() {
|
|
6933
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6934
|
+
return;
|
|
6935
|
+
};
|
|
6936
|
+
let api = guard.api();
|
|
6937
|
+
|
|
6938
|
+
let data: Vec<u8> = (0..10_000).map(|i| (i % 256) as u8).collect();
|
|
6939
|
+
let py_ba = api.bytearray_from_slice(&data);
|
|
6940
|
+
assert!(!py_ba.is_null());
|
|
6941
|
+
|
|
6942
|
+
let back = api
|
|
6943
|
+
.bytearray_to_vec(py_ba)
|
|
6944
|
+
.expect("should extract large bytearray");
|
|
6945
|
+
assert_eq!(back.len(), 10_000);
|
|
6946
|
+
assert_eq!(back, data);
|
|
6947
|
+
|
|
6948
|
+
api.decref(py_ba);
|
|
6949
|
+
}
|
|
6950
|
+
|
|
6951
|
+
// ========== Cross-type bytes/bytearray tests ==========
|
|
6952
|
+
|
|
6953
|
+
#[test]
|
|
6954
|
+
#[serial]
|
|
6955
|
+
fn test_bytes_and_bytearray_same_content_different_types() {
|
|
6956
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6957
|
+
return;
|
|
6958
|
+
};
|
|
6959
|
+
let api = guard.api();
|
|
6960
|
+
|
|
6961
|
+
let data = b"identical content";
|
|
6962
|
+
let py_bytes = api.bytes_from_slice(data);
|
|
6963
|
+
let py_ba = api.bytearray_from_slice(data);
|
|
6964
|
+
|
|
6965
|
+
// Same content
|
|
6966
|
+
let bytes_vec = api.bytes_to_vec(py_bytes).unwrap();
|
|
6967
|
+
let ba_vec = api.bytearray_to_vec(py_ba).unwrap();
|
|
6968
|
+
assert_eq!(bytes_vec, ba_vec, "Same input should produce same content");
|
|
6969
|
+
|
|
6970
|
+
// Different types
|
|
6971
|
+
assert!(api.bytes_check(py_bytes));
|
|
6972
|
+
assert!(!api.bytearray_check(py_bytes));
|
|
6973
|
+
assert!(api.bytearray_check(py_ba));
|
|
6974
|
+
assert!(!api.bytes_check(py_ba));
|
|
6975
|
+
|
|
6976
|
+
api.decref(py_bytes);
|
|
6977
|
+
api.decref(py_ba);
|
|
6978
|
+
}
|
|
6979
|
+
|
|
6980
|
+
#[test]
|
|
6981
|
+
#[serial]
|
|
6982
|
+
fn test_bytes_and_bytearray_neither_is_string() {
|
|
6983
|
+
let Some(guard) = skip_if_no_python() else {
|
|
6984
|
+
return;
|
|
6985
|
+
};
|
|
6986
|
+
let api = guard.api();
|
|
6987
|
+
|
|
6988
|
+
let py_bytes = api.bytes_from_slice(b"data");
|
|
6989
|
+
let py_ba = api.bytearray_from_slice(b"data");
|
|
6990
|
+
let py_str = api.string_from_str("data");
|
|
6991
|
+
|
|
6992
|
+
assert!(!api.is_string(py_bytes));
|
|
6993
|
+
assert!(!api.is_string(py_ba));
|
|
6994
|
+
assert!(api.is_string(py_str));
|
|
6995
|
+
|
|
6996
|
+
assert!(!api.bytes_check(py_str));
|
|
6997
|
+
assert!(!api.bytearray_check(py_str));
|
|
6998
|
+
|
|
6999
|
+
api.decref(py_bytes);
|
|
7000
|
+
api.decref(py_ba);
|
|
7001
|
+
api.decref(py_str);
|
|
7002
|
+
}
|
|
7003
|
+
|
|
7004
|
+
#[test]
|
|
7005
|
+
#[serial]
|
|
7006
|
+
fn test_bytes_python_equality_with_rust() {
|
|
7007
|
+
let Some(guard) = skip_if_no_python() else {
|
|
7008
|
+
return;
|
|
7009
|
+
};
|
|
7010
|
+
let api = guard.api();
|
|
7011
|
+
let globals = make_globals(api);
|
|
7012
|
+
|
|
7013
|
+
// Create bytes in Python with hex escapes and verify Rust reads them correctly
|
|
7014
|
+
let py_bytes = api
|
|
7015
|
+
.run_string(
|
|
7016
|
+
"b'\\x00\\x01\\x02\\xfd\\xfe\\xff'",
|
|
7017
|
+
PY_EVAL_INPUT,
|
|
7018
|
+
globals,
|
|
7019
|
+
globals,
|
|
7020
|
+
)
|
|
7021
|
+
.expect("should eval hex bytes");
|
|
7022
|
+
assert!(api.bytes_check(py_bytes));
|
|
7023
|
+
|
|
7024
|
+
let back = api.bytes_to_vec(py_bytes).unwrap();
|
|
7025
|
+
assert_eq!(back, vec![0x00, 0x01, 0x02, 0xfd, 0xfe, 0xff]);
|
|
7026
|
+
|
|
7027
|
+
api.decref(py_bytes);
|
|
7028
|
+
api.decref(globals);
|
|
7029
|
+
}
|
|
7030
|
+
|
|
7031
|
+
#[test]
|
|
7032
|
+
#[serial]
|
|
7033
|
+
fn test_bytearray_python_concatenation() {
|
|
7034
|
+
let Some(guard) = skip_if_no_python() else {
|
|
7035
|
+
return;
|
|
7036
|
+
};
|
|
7037
|
+
let api = guard.api();
|
|
7038
|
+
let globals = make_globals(api);
|
|
7039
|
+
|
|
7040
|
+
let py_ba = api
|
|
7041
|
+
.run_string(
|
|
7042
|
+
"bytearray(b'hello') + bytearray(b' world')",
|
|
7043
|
+
PY_EVAL_INPUT,
|
|
7044
|
+
globals,
|
|
7045
|
+
globals,
|
|
7046
|
+
)
|
|
7047
|
+
.expect("should eval bytearray concat");
|
|
7048
|
+
assert!(api.bytearray_check(py_ba));
|
|
7049
|
+
|
|
7050
|
+
let back = api.bytearray_to_vec(py_ba).unwrap();
|
|
7051
|
+
assert_eq!(back, b"hello world");
|
|
7052
|
+
|
|
7053
|
+
api.decref(py_ba);
|
|
7054
|
+
api.decref(globals);
|
|
7055
|
+
}
|
|
6029
7056
|
}
|