pycall 1.5.1 → 1.5.3
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/.github/dependabot.yml +10 -0
- data/.github/workflows/ci.yml +26 -27
- data/.github/workflows/release.yml +48 -0
- data/.github/workflows/windows.yml +18 -17
- data/CHANGES.md +22 -0
- data/README.md +20 -46
- data/Rakefile +11 -0
- data/docker/Dockerfile +1 -1
- data/ext/pycall/extconf.rb +2 -0
- data/ext/pycall/gc.c +6 -2
- data/ext/pycall/libpython.c +4 -0
- data/ext/pycall/pycall.c +134 -14
- data/ext/pycall/pycall_internal.h +11 -1
- data/ext/pycall/range.c +8 -2
- data/ext/pycall/ruby_wrapper.c +27 -80
- data/lib/pycall/libpython/pytypeobject_struct.rb +1 -1
- data/lib/pycall/pyobject_wrapper.rb +2 -3
- data/lib/pycall/version.rb +1 -1
- data/lib/pycall.rb +1 -1
- data/pycall.gemspec +7 -15
- metadata +16 -36
- data/bin/guard +0 -17
- data/ci/travis_before_script.sh +0 -21
- data/ci/travis_install.sh +0 -69
- data/ci/travis_retry.sh +0 -19
- data/config/Guardfile +0 -30
data/ext/pycall/pycall.c
CHANGED
|
@@ -29,7 +29,6 @@ static int python_major_version;
|
|
|
29
29
|
static int python_has_stackless_extension;
|
|
30
30
|
static PyObject *python_builtins_module;
|
|
31
31
|
static VALUE python_type_mapping;
|
|
32
|
-
static VALUE python_type_mapping;
|
|
33
32
|
static ID id_python_type_mapping;
|
|
34
33
|
|
|
35
34
|
int
|
|
@@ -122,6 +121,58 @@ pycall_m_without_gvl(VALUE mod)
|
|
|
122
121
|
|
|
123
122
|
/* ==== PyCall::PyPtr ==== */
|
|
124
123
|
|
|
124
|
+
struct pending_decref_list {
|
|
125
|
+
rb_nativethread_lock_t lock;
|
|
126
|
+
PyObject **pyptrs;
|
|
127
|
+
size_t count;
|
|
128
|
+
size_t capacity;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
static struct pending_decref_list pending_decrefs;
|
|
132
|
+
|
|
133
|
+
static void
|
|
134
|
+
pycall_pending_decref_add(PyObject *pyobj)
|
|
135
|
+
{
|
|
136
|
+
rb_nativethread_lock_lock(&pending_decrefs.lock);
|
|
137
|
+
if (pending_decrefs.count == pending_decrefs.capacity) {
|
|
138
|
+
size_t new_capacity = pending_decrefs.capacity ? 2 * pending_decrefs.capacity : 64;
|
|
139
|
+
/* Not ruby_xrealloc: this function runs during GC, where xmalloc/xrealloc
|
|
140
|
+
* are prohibited (rb_bug "Cannot realloc during GC" since Ruby 3.3). */
|
|
141
|
+
PyObject **new_pyptrs = realloc(pending_decrefs.pyptrs, new_capacity * sizeof(PyObject *));
|
|
142
|
+
if (new_pyptrs == NULL) {
|
|
143
|
+
/* Leaking the object is preferable to blocking or crashing inside GC */
|
|
144
|
+
rb_nativethread_lock_unlock(&pending_decrefs.lock);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
pending_decrefs.pyptrs = new_pyptrs;
|
|
148
|
+
pending_decrefs.capacity = new_capacity;
|
|
149
|
+
}
|
|
150
|
+
pending_decrefs.pyptrs[pending_decrefs.count++] = pyobj;
|
|
151
|
+
rb_nativethread_lock_unlock(&pending_decrefs.lock);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
static void
|
|
155
|
+
pycall_drain_pending_decrefs(void)
|
|
156
|
+
{
|
|
157
|
+
PyObject **pyptrs;
|
|
158
|
+
size_t i, count;
|
|
159
|
+
|
|
160
|
+
if (pending_decrefs.count == 0) return;
|
|
161
|
+
|
|
162
|
+
rb_nativethread_lock_lock(&pending_decrefs.lock);
|
|
163
|
+
pyptrs = pending_decrefs.pyptrs;
|
|
164
|
+
count = pending_decrefs.count;
|
|
165
|
+
pending_decrefs.pyptrs = NULL;
|
|
166
|
+
pending_decrefs.count = 0;
|
|
167
|
+
pending_decrefs.capacity = 0;
|
|
168
|
+
rb_nativethread_lock_unlock(&pending_decrefs.lock);
|
|
169
|
+
|
|
170
|
+
for (i = 0; i < count; ++i) {
|
|
171
|
+
pycall_Py_DecRef(pyptrs[i]);
|
|
172
|
+
}
|
|
173
|
+
free(pyptrs);
|
|
174
|
+
}
|
|
175
|
+
|
|
125
176
|
const rb_data_type_t pycall_pyptr_data_type = {
|
|
126
177
|
"PyCall::PyPtr",
|
|
127
178
|
{ 0, pycall_pyptr_free, pycall_pyptr_memsize, },
|
|
@@ -130,6 +181,11 @@ const rb_data_type_t pycall_pyptr_data_type = {
|
|
|
130
181
|
#endif
|
|
131
182
|
};
|
|
132
183
|
|
|
184
|
+
/* pycall_pyptr_free may be called by the GC sweeper on a thread that does
|
|
185
|
+
* not hold the GIL. PyCall keeps the GIL held by the thread that
|
|
186
|
+
* initialized Python for the entire lifetime of the process, so waiting
|
|
187
|
+
* for the GIL there (PyGILState_Ensure) blocks forever while the sweeper
|
|
188
|
+
* holds the GVL, freezing every Ruby thread in the process. */
|
|
133
189
|
void
|
|
134
190
|
pycall_pyptr_free(void *ptr)
|
|
135
191
|
{
|
|
@@ -139,7 +195,15 @@ pycall_pyptr_free(void *ptr)
|
|
|
139
195
|
fprintf(stderr, "zero refcnt object %p of type %s\n", pyobj, Py_TYPE(pyobj)->tp_name);
|
|
140
196
|
}
|
|
141
197
|
#endif /* PYCALL_DEBUG_DUMP_REFCNT */
|
|
142
|
-
|
|
198
|
+
|
|
199
|
+
if (Py_API(PyGILState_Check())) {
|
|
200
|
+
pycall_Py_DecRef(pyobj);
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
/* Instead of taking the GIL here, enqueue the pointer; the list is
|
|
204
|
+
* drained on the next Python call. */
|
|
205
|
+
pycall_pending_decref_add(pyobj);
|
|
206
|
+
}
|
|
143
207
|
}
|
|
144
208
|
|
|
145
209
|
static size_t _PySys_GetSizeOf(PyObject *);
|
|
@@ -620,6 +684,8 @@ pycall_libpython_api_PyObject_Dir(VALUE mod, VALUE pyptr)
|
|
|
620
684
|
PyObject *dir;
|
|
621
685
|
PyObject *pyobj;
|
|
622
686
|
|
|
687
|
+
pycall_drain_pending_decrefs();
|
|
688
|
+
|
|
623
689
|
if (!is_pycall_pyptr(pyptr)) {
|
|
624
690
|
rb_raise(rb_eTypeError, "PyCall::PyPtr is required");
|
|
625
691
|
}
|
|
@@ -639,6 +705,8 @@ pycall_libpython_api_PyList_Size(VALUE mod, VALUE pyptr)
|
|
|
639
705
|
PyObject *pyobj;
|
|
640
706
|
Py_ssize_t size;
|
|
641
707
|
|
|
708
|
+
pycall_drain_pending_decrefs();
|
|
709
|
+
|
|
642
710
|
if (!is_pycall_pyptr(pyptr)) {
|
|
643
711
|
rb_raise(rb_eTypeError, "PyCall::PyPtr is required");
|
|
644
712
|
}
|
|
@@ -659,6 +727,8 @@ pycall_libpython_api_PyList_GetItem(VALUE mod, VALUE pyptr, VALUE idx)
|
|
|
659
727
|
PyObject *pyobj_item;
|
|
660
728
|
Py_ssize_t i;
|
|
661
729
|
|
|
730
|
+
pycall_drain_pending_decrefs();
|
|
731
|
+
|
|
662
732
|
if (!is_pycall_pyptr(pyptr)) {
|
|
663
733
|
rb_raise(rb_eTypeError, "PyCall::PyPtr is required");
|
|
664
734
|
}
|
|
@@ -724,6 +794,8 @@ pycall_libpython_helpers_m_import_module(int argc, VALUE *argv, VALUE mod)
|
|
|
724
794
|
VALUE name, globals, locals, fromlist, level;
|
|
725
795
|
char const *name_cstr;
|
|
726
796
|
|
|
797
|
+
pycall_drain_pending_decrefs();
|
|
798
|
+
|
|
727
799
|
rb_scan_args(argc, argv, "14", &name, &globals, &locals, &fromlist, &level);
|
|
728
800
|
|
|
729
801
|
if (RB_TYPE_P(name, T_SYMBOL)) {
|
|
@@ -776,6 +848,8 @@ pycall_libpython_helpers_m_compare(VALUE mod, VALUE op, VALUE pyptr_a, VALUE pyp
|
|
|
776
848
|
PyObject *pyobj_a, *pyobj_b, *res;
|
|
777
849
|
int opid;
|
|
778
850
|
|
|
851
|
+
pycall_drain_pending_decrefs();
|
|
852
|
+
|
|
779
853
|
opid = pycall_rich_compare_opid(op);
|
|
780
854
|
|
|
781
855
|
if (!is_pycall_pyptr(pyptr_a)) {
|
|
@@ -830,6 +904,8 @@ pycall_libpython_helpers_m_getattr(int argc, VALUE *argv, VALUE mod)
|
|
|
830
904
|
{
|
|
831
905
|
VALUE pyptr, name, default_value;
|
|
832
906
|
|
|
907
|
+
pycall_drain_pending_decrefs();
|
|
908
|
+
|
|
833
909
|
if (rb_scan_args(argc, argv, "21", &pyptr, &name, &default_value) == 2) {
|
|
834
910
|
default_value = Qundef;
|
|
835
911
|
}
|
|
@@ -851,6 +927,8 @@ pycall_libpython_helpers_m_hasattr_p(VALUE mod, VALUE pyptr, VALUE name)
|
|
|
851
927
|
PyObject *pyobj;
|
|
852
928
|
int res;
|
|
853
929
|
|
|
930
|
+
pycall_drain_pending_decrefs();
|
|
931
|
+
|
|
854
932
|
if (!is_pycall_pyptr(pyptr)) {
|
|
855
933
|
rb_raise(rb_eTypeError, "PyCall::PyPtr is required");
|
|
856
934
|
}
|
|
@@ -870,6 +948,8 @@ pycall_libpython_helpers_m_setattr(VALUE mod, VALUE pyptr, VALUE name, VALUE val
|
|
|
870
948
|
{
|
|
871
949
|
PyObject *pyobj, *pyval;
|
|
872
950
|
|
|
951
|
+
pycall_drain_pending_decrefs();
|
|
952
|
+
|
|
873
953
|
if (!is_pycall_pyptr(pyptr)) {
|
|
874
954
|
rb_raise(rb_eTypeError, "PyCall::PyPtr is required");
|
|
875
955
|
}
|
|
@@ -893,6 +973,8 @@ pycall_libpython_helpers_m_delattr(VALUE mod, VALUE pyptr, VALUE name)
|
|
|
893
973
|
{
|
|
894
974
|
PyObject *pyobj;
|
|
895
975
|
|
|
976
|
+
pycall_drain_pending_decrefs();
|
|
977
|
+
|
|
896
978
|
if (!is_pycall_pyptr(pyptr)) {
|
|
897
979
|
rb_raise(rb_eTypeError, "PyCall::PyPtr is required");
|
|
898
980
|
}
|
|
@@ -916,6 +998,8 @@ pycall_libpython_helpers_m_callable_p(VALUE mod, VALUE pyptr)
|
|
|
916
998
|
PyObject *pyobj;
|
|
917
999
|
int res;
|
|
918
1000
|
|
|
1001
|
+
pycall_drain_pending_decrefs();
|
|
1002
|
+
|
|
919
1003
|
if (!is_pycall_pyptr(pyptr)) {
|
|
920
1004
|
rb_raise(rb_eTypeError, "PyCall::PyPtr is required");
|
|
921
1005
|
}
|
|
@@ -932,6 +1016,8 @@ pycall_libpython_helpers_m_call_object(int argc, VALUE *argv, VALUE mod)
|
|
|
932
1016
|
VALUE pyptr;
|
|
933
1017
|
PyObject *pyobj;
|
|
934
1018
|
|
|
1019
|
+
pycall_drain_pending_decrefs();
|
|
1020
|
+
|
|
935
1021
|
if (argc < 1) {
|
|
936
1022
|
rb_raise(rb_eArgError, "too few arguments (%d for >=1)", argc);
|
|
937
1023
|
}
|
|
@@ -1081,6 +1167,8 @@ pycall_pyobject_wrapper_wrapper_method(int argc, VALUE *argv, VALUE wrapper)
|
|
|
1081
1167
|
char *name_cstr;
|
|
1082
1168
|
PyObject *pyobj, *attr;
|
|
1083
1169
|
|
|
1170
|
+
pycall_drain_pending_decrefs();
|
|
1171
|
+
|
|
1084
1172
|
pyptr = rb_attr_get(wrapper, rb_intern("@__pyptr__"));
|
|
1085
1173
|
if (NIL_P(pyptr) || !is_pycall_pyptr(pyptr)) {
|
|
1086
1174
|
rb_raise(rb_eTypeError, "Wrong wrapper object is given");
|
|
@@ -1136,6 +1224,8 @@ pycall_libpython_helpers_m_define_wrapper_method(VALUE mod, VALUE wrapper, VALUE
|
|
|
1136
1224
|
PyObject *pyobj, *attr;
|
|
1137
1225
|
char *name_cstr;
|
|
1138
1226
|
|
|
1227
|
+
pycall_drain_pending_decrefs();
|
|
1228
|
+
|
|
1139
1229
|
pyptr = rb_attr_get(wrapper, rb_intern("@__pyptr__"));
|
|
1140
1230
|
if (NIL_P(pyptr) || !is_pycall_pyptr(pyptr)) {
|
|
1141
1231
|
rb_raise(rb_eTypeError, "Wrong wrapper object is given");
|
|
@@ -1183,10 +1273,10 @@ pycall_convert_index(VALUE index)
|
|
|
1183
1273
|
}
|
|
1184
1274
|
}
|
|
1185
1275
|
else if (rb_obj_is_kind_of(index, rb_cRange)) {
|
|
1186
|
-
pyobj = pycall_pyslice_from_ruby(index); /* New
|
|
1276
|
+
pyobj = pycall_pyslice_from_ruby(index); /* New reference */
|
|
1187
1277
|
}
|
|
1188
1278
|
else if (pycall_obj_is_step_range(index)) {
|
|
1189
|
-
pyobj = pycall_pyslice_from_ruby(index); /* New
|
|
1279
|
+
pyobj = pycall_pyslice_from_ruby(index); /* New reference */
|
|
1190
1280
|
}
|
|
1191
1281
|
else {
|
|
1192
1282
|
pyobj = pycall_pyobject_from_ruby(index); /* New reference */
|
|
@@ -1201,6 +1291,8 @@ pycall_libpython_helpers_m_getitem(VALUE mod, VALUE pyptr, VALUE key)
|
|
|
1201
1291
|
PyObject *pyobj, *pyobj_key, *pyobj_v;
|
|
1202
1292
|
VALUE obj;
|
|
1203
1293
|
|
|
1294
|
+
pycall_drain_pending_decrefs();
|
|
1295
|
+
|
|
1204
1296
|
if (!is_pycall_pyptr(pyptr)) {
|
|
1205
1297
|
rb_raise(rb_eTypeError, "PyCall::PyPtr is required");
|
|
1206
1298
|
}
|
|
@@ -1226,6 +1318,8 @@ pycall_libpython_helpers_m_setitem(VALUE mod, VALUE pyptr, VALUE key, VALUE v)
|
|
|
1226
1318
|
PyObject *pyobj, *pyobj_key, *pyobj_value;
|
|
1227
1319
|
int res;
|
|
1228
1320
|
|
|
1321
|
+
pycall_drain_pending_decrefs();
|
|
1322
|
+
|
|
1229
1323
|
pyobj = check_get_pyobj_ptr(pyptr, NULL);
|
|
1230
1324
|
pyobj_key = pycall_convert_index(key);
|
|
1231
1325
|
pyobj_value = pycall_pyobject_from_ruby(v);
|
|
@@ -1246,6 +1340,8 @@ pycall_libpython_helpers_m_delitem(VALUE mod, VALUE pyptr, VALUE key)
|
|
|
1246
1340
|
PyObject *pyobj, *pyobj_key;
|
|
1247
1341
|
int res;
|
|
1248
1342
|
|
|
1343
|
+
pycall_drain_pending_decrefs();
|
|
1344
|
+
|
|
1249
1345
|
pyobj = check_get_pyobj_ptr(pyptr, NULL);
|
|
1250
1346
|
pyobj_key = pycall_convert_index(key);
|
|
1251
1347
|
|
|
@@ -1263,6 +1359,8 @@ pycall_libpython_helpers_m_str(VALUE mod, VALUE pyptr)
|
|
|
1263
1359
|
{
|
|
1264
1360
|
PyObject *pyobj, *pyobj_str;
|
|
1265
1361
|
|
|
1362
|
+
pycall_drain_pending_decrefs();
|
|
1363
|
+
|
|
1266
1364
|
pyobj = check_get_pyobj_ptr(pyptr, NULL);
|
|
1267
1365
|
|
|
1268
1366
|
pyobj_str = Py_API(PyObject_Str)(pyobj);
|
|
@@ -1270,7 +1368,9 @@ pycall_libpython_helpers_m_str(VALUE mod, VALUE pyptr)
|
|
|
1270
1368
|
pycall_pyerror_fetch_and_raise("PyObject_Str");
|
|
1271
1369
|
}
|
|
1272
1370
|
|
|
1273
|
-
|
|
1371
|
+
VALUE v = pycall_pyobject_to_ruby(pyobj_str);
|
|
1372
|
+
pycall_Py_DecRef(pyobj_str);
|
|
1373
|
+
return v;
|
|
1274
1374
|
}
|
|
1275
1375
|
|
|
1276
1376
|
static VALUE
|
|
@@ -1279,6 +1379,8 @@ pycall_libpython_helpers_m_dict_contains(VALUE mod, VALUE pyptr, VALUE key)
|
|
|
1279
1379
|
PyObject *pyobj, *pyobj_key;
|
|
1280
1380
|
int res;
|
|
1281
1381
|
|
|
1382
|
+
pycall_drain_pending_decrefs();
|
|
1383
|
+
|
|
1282
1384
|
pyobj = check_get_pyobj_ptr(pyptr, Py_API(PyDict_Type));
|
|
1283
1385
|
pyobj_key = pycall_pyobject_from_ruby(key);
|
|
1284
1386
|
res = Py_API(PyDict_Contains)(pyobj, pyobj_key);
|
|
@@ -1296,6 +1398,8 @@ pycall_libpython_helpers_m_dict_each(VALUE mod, VALUE pyptr)
|
|
|
1296
1398
|
PyObject *pyobj, *pyobj_key, *pyobj_value;
|
|
1297
1399
|
Py_ssize_t pos;
|
|
1298
1400
|
|
|
1401
|
+
pycall_drain_pending_decrefs();
|
|
1402
|
+
|
|
1299
1403
|
pyobj = check_get_pyobj_ptr(pyptr, Py_API(PyDict_Type));
|
|
1300
1404
|
|
|
1301
1405
|
pos = 0;
|
|
@@ -1315,6 +1419,8 @@ pycall_libpython_helpers_m_sequence_contains(VALUE mod, VALUE pyptr, VALUE key)
|
|
|
1315
1419
|
PyObject *pyobj, *pyobj_key;
|
|
1316
1420
|
int res;
|
|
1317
1421
|
|
|
1422
|
+
pycall_drain_pending_decrefs();
|
|
1423
|
+
|
|
1318
1424
|
pyobj = check_get_pyobj_ptr(pyptr, NULL);
|
|
1319
1425
|
if (!Py_API(PySequence_Check)(pyobj))
|
|
1320
1426
|
rb_raise(rb_eTypeError, "unexpected Python type %s (expected a Python sequence object)", Py_TYPE(pyobj)->tp_name);
|
|
@@ -1334,6 +1440,8 @@ pycall_libpython_helpers_m_sequence_each(VALUE mod, VALUE pyptr)
|
|
|
1334
1440
|
{
|
|
1335
1441
|
PyObject *pyobj, *pyobj_iter, *pyobj_item;
|
|
1336
1442
|
|
|
1443
|
+
pycall_drain_pending_decrefs();
|
|
1444
|
+
|
|
1337
1445
|
pyobj = check_get_pyobj_ptr(pyptr, NULL);
|
|
1338
1446
|
if (!Py_API(PySequence_Check)(pyobj))
|
|
1339
1447
|
rb_raise(rb_eTypeError, "unexpected Python type %s (expected a Python sequence object)", Py_TYPE(pyobj)->tp_name);
|
|
@@ -1518,7 +1626,7 @@ pycall_pyobject_to_ruby(PyObject *pyobj)
|
|
|
1518
1626
|
Py_API(Py_IncRef)((PyObject *)pyobj->ob_type);
|
|
1519
1627
|
cls = pycall_python_type_mapping_get_mapped_class(pycall_pytypeptr_new((PyObject *)pyobj->ob_type));
|
|
1520
1628
|
if (NIL_P(cls)) {
|
|
1521
|
-
rb_warning("
|
|
1629
|
+
rb_warning("Currently do not support to convert %s to Ruby object", Py_TYPE(pyobj)->tp_name);
|
|
1522
1630
|
return pycall_pyobject_wrapper_object_new(rb_cObject, pyobj);
|
|
1523
1631
|
}
|
|
1524
1632
|
|
|
@@ -1653,7 +1761,7 @@ pycall_pylong_to_ruby(PyObject *pyobj)
|
|
|
1653
1761
|
}
|
|
1654
1762
|
#endif
|
|
1655
1763
|
|
|
1656
|
-
rb_warning("
|
|
1764
|
+
rb_warning("Currently do not support to convert multi-precision PyLong number to Ruby object");
|
|
1657
1765
|
|
|
1658
1766
|
return Qnil;
|
|
1659
1767
|
}
|
|
@@ -1690,19 +1798,21 @@ pycall_pyunicode_to_ruby(PyObject *pyobj)
|
|
|
1690
1798
|
return Qnil;
|
|
1691
1799
|
}
|
|
1692
1800
|
|
|
1693
|
-
|
|
1694
|
-
if (!
|
|
1801
|
+
PyObject *pyobj_uni = Py_API(PyUnicode_AsUTF8String)(pyobj);
|
|
1802
|
+
if (!pyobj_uni) {
|
|
1695
1803
|
Py_API(PyErr_Clear)();
|
|
1696
1804
|
return Qnil;
|
|
1697
1805
|
}
|
|
1698
1806
|
|
|
1699
|
-
res = Py_API(PyString_AsStringAndSize)(
|
|
1807
|
+
res = Py_API(PyString_AsStringAndSize)(pyobj_uni, &str, &len);
|
|
1700
1808
|
if (res < 0) {
|
|
1701
|
-
pycall_Py_DecRef(
|
|
1809
|
+
pycall_Py_DecRef(pyobj_uni);
|
|
1702
1810
|
return Qnil;
|
|
1703
1811
|
}
|
|
1704
1812
|
|
|
1705
|
-
|
|
1813
|
+
VALUE v = rb_enc_str_new(str, len, rb_enc_from_index(rb_utf8_encindex()));
|
|
1814
|
+
pycall_Py_DecRef(pyobj_uni);
|
|
1815
|
+
return v;
|
|
1706
1816
|
}
|
|
1707
1817
|
|
|
1708
1818
|
static VALUE
|
|
@@ -1775,7 +1885,11 @@ pycall_conv_m_unregister_python_type_mapping(VALUE mod, VALUE pytypeptr)
|
|
|
1775
1885
|
static VALUE
|
|
1776
1886
|
pycall_conv_m_from_ruby(VALUE mod, VALUE obj)
|
|
1777
1887
|
{
|
|
1778
|
-
PyObject *pyobj
|
|
1888
|
+
PyObject *pyobj;
|
|
1889
|
+
|
|
1890
|
+
pycall_drain_pending_decrefs();
|
|
1891
|
+
|
|
1892
|
+
pyobj = pycall_pyobject_from_ruby(obj);
|
|
1779
1893
|
if (PyType_Check(pyobj) || PyClass_Check(pyobj))
|
|
1780
1894
|
return pycall_pytypeptr_new(pyobj);
|
|
1781
1895
|
if (PyRuby_Check(pyobj))
|
|
@@ -1787,7 +1901,11 @@ static VALUE
|
|
|
1787
1901
|
pycall_conv_m_to_ruby(VALUE mod, VALUE pyptr)
|
|
1788
1902
|
{
|
|
1789
1903
|
VALUE obj, obj_pyptr;
|
|
1790
|
-
PyObject *pyobj
|
|
1904
|
+
PyObject *pyobj;
|
|
1905
|
+
|
|
1906
|
+
pycall_drain_pending_decrefs();
|
|
1907
|
+
|
|
1908
|
+
pyobj = check_get_pyobj_ptr(pyptr, NULL);
|
|
1791
1909
|
obj = obj_pyptr = pycall_pyobject_to_ruby(pyobj);
|
|
1792
1910
|
if (is_pyobject_wrapper(obj)) {
|
|
1793
1911
|
obj_pyptr = pycall_pyobject_wrapper_get_pyptr(obj);
|
|
@@ -2309,6 +2427,8 @@ Init_pycall(void)
|
|
|
2309
2427
|
pycall_tls_create((pycall_tls_key *)&without_gvl_key);
|
|
2310
2428
|
rb_define_module_function(mPyCall, "without_gvl", pycall_m_without_gvl, 0);
|
|
2311
2429
|
|
|
2430
|
+
rb_nativethread_lock_initialize(&pending_decrefs.lock);
|
|
2431
|
+
|
|
2312
2432
|
/* PyCall::PyPtr */
|
|
2313
2433
|
|
|
2314
2434
|
cPyPtr = rb_define_class_under(mPyCall, "PyPtr", rb_cBasicObject);
|
|
@@ -11,6 +11,7 @@ extern "C" {
|
|
|
11
11
|
#include <ruby.h>
|
|
12
12
|
#include <ruby/encoding.h>
|
|
13
13
|
#include <ruby/thread.h>
|
|
14
|
+
#include <ruby/thread_native.h>
|
|
14
15
|
|
|
15
16
|
#include <assert.h>
|
|
16
17
|
#include <inttypes.h>
|
|
@@ -71,7 +72,7 @@ typedef intptr_t Py_intptr_t;
|
|
|
71
72
|
#ifdef HAVE_SSIZE_T
|
|
72
73
|
typedef ssize_t Py_ssize_t;
|
|
73
74
|
#elif SIZEOF_SIZE_T == SIZEOF_VOIDP
|
|
74
|
-
typedef
|
|
75
|
+
typedef Py_intptr_t Py_ssize_t;
|
|
75
76
|
#elif SIZEOF_SIZE_T == SIZEOF_INT
|
|
76
77
|
typedef int Py_ssize_t;
|
|
77
78
|
#elif SIZEOF_SIZE_T == SIZEOF_LONG
|
|
@@ -519,6 +520,11 @@ PyObject * PyRuby_New(VALUE ruby_object);
|
|
|
519
520
|
|
|
520
521
|
/* ==== thread support ==== */
|
|
521
522
|
|
|
523
|
+
typedef enum {
|
|
524
|
+
PyGILState_LOCKED,
|
|
525
|
+
PyGILState_UNLOCKED
|
|
526
|
+
} PyGILState_STATE;
|
|
527
|
+
|
|
522
528
|
#if defined(PYCALL_THREAD_WIN32)
|
|
523
529
|
typedef DWORD pycall_tls_key;
|
|
524
530
|
#elif defined(PYCALL_THREAD_PTHREAD)
|
|
@@ -669,6 +675,10 @@ typedef struct {
|
|
|
669
675
|
PyObject * (* PyUnicode_AsUTF8String)(PyObject *);
|
|
670
676
|
PyObject * (* PyUnicode_DecodeUTF8)(char const*, Py_ssize_t, char const *);
|
|
671
677
|
PyObject * (* PyUnicode_FromFormatV)(char const*, ...);
|
|
678
|
+
|
|
679
|
+
int (* PyGILState_Check)(void);
|
|
680
|
+
PyGILState_STATE (* PyGILState_Ensure)(void);
|
|
681
|
+
void (* PyGILState_Release)(PyGILState_STATE);
|
|
672
682
|
} pycall_libpython_api_table_t;
|
|
673
683
|
|
|
674
684
|
pycall_libpython_api_table_t *pycall_libpython_api_table(void);
|
data/ext/pycall/range.c
CHANGED
|
@@ -6,6 +6,12 @@ struct enumerator_head {
|
|
|
6
6
|
VALUE args;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
+
#ifdef HAVE_RTYPEDDATA_GET_DATA
|
|
10
|
+
# define GET_ENUMERATOR_DATA(obj) ((struct enumerator_head *)RTYPEDDATA_GET_DATA(obj))
|
|
11
|
+
#else
|
|
12
|
+
# define GET_ENUMERATOR_DATA(obj) ((struct enumerator_head *)DATA_PTR(obj))
|
|
13
|
+
#endif
|
|
14
|
+
|
|
9
15
|
int
|
|
10
16
|
pycall_obj_is_step_range(VALUE obj)
|
|
11
17
|
{
|
|
@@ -19,7 +25,7 @@ pycall_obj_is_step_range(VALUE obj)
|
|
|
19
25
|
return 0;
|
|
20
26
|
}
|
|
21
27
|
|
|
22
|
-
eh = (
|
|
28
|
+
eh = GET_ENUMERATOR_DATA(obj);
|
|
23
29
|
|
|
24
30
|
if (!rb_obj_is_kind_of(eh->obj, rb_cRange)) {
|
|
25
31
|
return 0;
|
|
@@ -50,7 +56,7 @@ pycall_extract_range(VALUE obj, VALUE *pbegin, VALUE *pend, int *pexclude_end, V
|
|
|
50
56
|
exclude_end = rb_funcallv(obj, id_exclude_end, 0, 0);
|
|
51
57
|
}
|
|
52
58
|
else if (pycall_obj_is_step_range(obj)) {
|
|
53
|
-
struct enumerator_head *eh = (
|
|
59
|
+
struct enumerator_head *eh = GET_ENUMERATOR_DATA(obj);
|
|
54
60
|
begin = rb_funcallv(eh->obj, id_begin, 0, 0);
|
|
55
61
|
end = rb_funcallv(eh->obj, id_end, 0, 0);
|
|
56
62
|
exclude_end = rb_funcallv(eh->obj, id_exclude_end, 0, 0);
|
data/ext/pycall/ruby_wrapper.c
CHANGED
|
@@ -6,7 +6,6 @@ static PyMemberDef PyRuby_members[] = {
|
|
|
6
6
|
{NULL} /* sentinel */
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
-
static VALUE PyRuby_get_ruby_object_and_set_pyerr(PyObject *pyobj);
|
|
10
9
|
static void PyRuby_dealloc_with_gvl(PyRubyObject *);
|
|
11
10
|
static PyObject * PyRuby_repr_with_gvl(PyRubyObject *);
|
|
12
11
|
static PyObject * PyRuby_call_with_gvl(PyRubyObject *, PyObject *, PyObject *);
|
|
@@ -59,10 +58,11 @@ PyObject *
|
|
|
59
58
|
PyRuby_New(VALUE ruby_object)
|
|
60
59
|
{
|
|
61
60
|
if (!ruby_thread_has_gvl_p()) {
|
|
62
|
-
CALL_WITH_GVL(PyRuby_New_impl, ruby_object);
|
|
61
|
+
return CALL_WITH_GVL(PyRuby_New_impl, ruby_object);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
return PyRuby_New_impl(ruby_object);
|
|
63
65
|
}
|
|
64
|
-
|
|
65
|
-
return PyRuby_New_impl(ruby_object);
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
static void *
|
|
@@ -90,7 +90,9 @@ PyRuby_dealloc_with_gvl(PyRubyObject *pyro)
|
|
|
90
90
|
if (!ruby_thread_has_gvl_p()) {
|
|
91
91
|
CALL_WITH_GVL(PyRuby_dealloc, pyro);
|
|
92
92
|
}
|
|
93
|
-
|
|
93
|
+
else {
|
|
94
|
+
PyRuby_dealloc(pyro);
|
|
95
|
+
}
|
|
94
96
|
}
|
|
95
97
|
|
|
96
98
|
static PyObject *
|
|
@@ -99,9 +101,7 @@ PyRuby_repr(PyRubyObject *pyro)
|
|
|
99
101
|
VALUE obj, str;
|
|
100
102
|
PyObject *res;
|
|
101
103
|
|
|
102
|
-
obj =
|
|
103
|
-
if (obj == Qundef)
|
|
104
|
-
return NULL;
|
|
104
|
+
obj = PyRuby_get_ruby_object((PyObject *)pyro);
|
|
105
105
|
|
|
106
106
|
str = rb_inspect(obj);
|
|
107
107
|
res = pycall_pystring_from_format("<PyCall.ruby_object %s>", StringValueCStr(str));
|
|
@@ -114,7 +114,9 @@ PyRuby_repr_with_gvl(PyRubyObject *pyro)
|
|
|
114
114
|
if (!ruby_thread_has_gvl_p()) {
|
|
115
115
|
return CALL_WITH_GVL(PyRuby_repr, pyro);
|
|
116
116
|
}
|
|
117
|
-
|
|
117
|
+
else {
|
|
118
|
+
return PyRuby_repr(pyro);
|
|
119
|
+
}
|
|
118
120
|
}
|
|
119
121
|
|
|
120
122
|
#if SIZEOF_SSIZE_T < 8
|
|
@@ -138,9 +140,7 @@ PyRuby_hash_long(PyRubyObject *pyro)
|
|
|
138
140
|
VALUE obj, rbhash;
|
|
139
141
|
intptr_t h;
|
|
140
142
|
|
|
141
|
-
obj =
|
|
142
|
-
if (obj == Qundef)
|
|
143
|
-
return (void *)-1;
|
|
143
|
+
obj = PyRuby_get_ruby_object((PyObject *)pyro);
|
|
144
144
|
|
|
145
145
|
rbhash = rb_hash(obj);
|
|
146
146
|
h = FIX2LONG(rbhash); /* Ruby's hash value is a Fixnum */
|
|
@@ -154,7 +154,9 @@ PyRuby_hash_long_with_gvl(PyRubyObject *pyro)
|
|
|
154
154
|
if (!ruby_thread_has_gvl_p()) {
|
|
155
155
|
return (long)(intptr_t)CALL_WITH_GVL(PyRuby_hash_long, pyro);
|
|
156
156
|
}
|
|
157
|
-
|
|
157
|
+
else {
|
|
158
|
+
return (long)(intptr_t)PyRuby_hash_long(pyro);
|
|
159
|
+
}
|
|
158
160
|
}
|
|
159
161
|
|
|
160
162
|
static void *
|
|
@@ -163,9 +165,7 @@ PyRuby_hash_hash_t(PyRubyObject *pyro)
|
|
|
163
165
|
VALUE obj, rbhash;
|
|
164
166
|
Py_hash_t h;
|
|
165
167
|
|
|
166
|
-
obj =
|
|
167
|
-
if (obj == Qundef)
|
|
168
|
-
return (void *)-1;
|
|
168
|
+
obj = PyRuby_get_ruby_object((PyObject *)pyro);
|
|
169
169
|
|
|
170
170
|
rbhash = rb_hash(obj);
|
|
171
171
|
#if SIZEOF_PY_HASH_T == SIZEOF_LONG
|
|
@@ -185,7 +185,9 @@ PyRuby_hash_hash_t_with_gvl(PyRubyObject *pyro)
|
|
|
185
185
|
if (!ruby_thread_has_gvl_p()) {
|
|
186
186
|
return (Py_hash_t)CALL_WITH_GVL(PyRuby_hash_hash_t, pyro);
|
|
187
187
|
}
|
|
188
|
-
|
|
188
|
+
else {
|
|
189
|
+
return (Py_hash_t)PyRuby_hash_hash_t(pyro);
|
|
190
|
+
}
|
|
189
191
|
}
|
|
190
192
|
|
|
191
193
|
struct call_rb_funcallv_params {
|
|
@@ -233,9 +235,7 @@ PyRuby_call(struct PyRuby_call_params *params)
|
|
|
233
235
|
PyObject *pyobj_res;
|
|
234
236
|
int state;
|
|
235
237
|
|
|
236
|
-
obj =
|
|
237
|
-
if (obj == Qundef)
|
|
238
|
-
return NULL;
|
|
238
|
+
obj = PyRuby_get_ruby_object((PyObject *)params->pyro);
|
|
239
239
|
|
|
240
240
|
id_call = rb_intern("call");
|
|
241
241
|
if (!rb_respond_to(obj, id_call)) {
|
|
@@ -269,8 +269,9 @@ PyRuby_call_with_gvl(PyRubyObject *pyro, PyObject *pyobj_args, PyObject *pyobj_k
|
|
|
269
269
|
if (!ruby_thread_has_gvl_p()) {
|
|
270
270
|
return CALL_WITH_GVL(PyRuby_call, ¶ms);
|
|
271
271
|
}
|
|
272
|
-
|
|
273
|
-
|
|
272
|
+
else {
|
|
273
|
+
return PyRuby_call(¶ms);
|
|
274
|
+
}
|
|
274
275
|
}
|
|
275
276
|
|
|
276
277
|
struct PyRuby_getattro_params {
|
|
@@ -286,9 +287,7 @@ PyRuby_getattro(struct PyRuby_getattro_params *params)
|
|
|
286
287
|
ID name_id;
|
|
287
288
|
PyObject *pyobj_res;
|
|
288
289
|
|
|
289
|
-
obj =
|
|
290
|
-
if (obj == Qundef)
|
|
291
|
-
return NULL;
|
|
290
|
+
obj = PyRuby_get_ruby_object((PyObject *)params->pyro);
|
|
292
291
|
|
|
293
292
|
name = pycall_pyobject_to_ruby(params->pyobj_name);
|
|
294
293
|
name_cstr = StringValueCStr(name);
|
|
@@ -348,8 +347,9 @@ PyRuby_getattro_with_gvl(PyRubyObject *pyro, PyObject *pyobj_name)
|
|
|
348
347
|
if (!ruby_thread_has_gvl_p()) {
|
|
349
348
|
return CALL_WITH_GVL(PyRuby_getattro, ¶ms);
|
|
350
349
|
}
|
|
351
|
-
|
|
352
|
-
|
|
350
|
+
else {
|
|
351
|
+
return PyRuby_getattro(¶ms);
|
|
352
|
+
}
|
|
353
353
|
}
|
|
354
354
|
|
|
355
355
|
/* ==== PyCall::PyRubyPtr ==== */
|
|
@@ -478,56 +478,3 @@ pycall_init_ruby_wrapper(void)
|
|
|
478
478
|
rb_define_module_function(mPyCall, "wrap_ruby_object", pycall_m_wrap_ruby_object, 1);
|
|
479
479
|
}
|
|
480
480
|
|
|
481
|
-
/* --- File internal utilities --- */
|
|
482
|
-
|
|
483
|
-
static VALUE
|
|
484
|
-
funcall_id2ref(VALUE object_id)
|
|
485
|
-
{
|
|
486
|
-
VALUE rb_mObjSpace;
|
|
487
|
-
object_id = rb_check_to_integer(object_id, "to_int");
|
|
488
|
-
rb_mObjSpace = rb_const_get(rb_cObject, rb_intern("ObjectSpace"));
|
|
489
|
-
return rb_funcall(rb_mObjSpace, rb_intern("_id2ref"), 1, object_id);
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
static VALUE
|
|
493
|
-
protect_id2ref(VALUE object_id)
|
|
494
|
-
{
|
|
495
|
-
VALUE obj;
|
|
496
|
-
int state;
|
|
497
|
-
|
|
498
|
-
obj = rb_protect((VALUE (*)(VALUE))funcall_id2ref, object_id, &state);
|
|
499
|
-
if (state)
|
|
500
|
-
return Qundef;
|
|
501
|
-
|
|
502
|
-
return obj;
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
static VALUE
|
|
506
|
-
protect_id2ref_and_set_pyerr(VALUE object_id)
|
|
507
|
-
{
|
|
508
|
-
VALUE obj = protect_id2ref(object_id);
|
|
509
|
-
if (obj != Qundef)
|
|
510
|
-
return obj;
|
|
511
|
-
|
|
512
|
-
obj = rb_errinfo();
|
|
513
|
-
if (RTEST(rb_obj_is_kind_of(obj, rb_eRangeError))) {
|
|
514
|
-
Py_API(PyErr_SetString)(Py_API(PyExc_RuntimeError), "[BUG] referenced object was garbage-collected");
|
|
515
|
-
}
|
|
516
|
-
else {
|
|
517
|
-
VALUE emesg = rb_check_funcall(obj, rb_intern("message"), 0, 0);
|
|
518
|
-
Py_API(PyErr_Format)(Py_API(PyExc_RuntimeError),
|
|
519
|
-
"[BUG] Unable to obtain ruby object from ID: %s (%s)",
|
|
520
|
-
StringValueCStr(emesg), rb_class2name(CLASS_OF(obj)));
|
|
521
|
-
}
|
|
522
|
-
return Qundef;
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
static VALUE
|
|
526
|
-
PyRuby_get_ruby_object_and_set_pyerr(PyObject *pyobj)
|
|
527
|
-
{
|
|
528
|
-
VALUE obj_id;
|
|
529
|
-
if (!PyRuby_Check(pyobj))
|
|
530
|
-
return Qundef;
|
|
531
|
-
obj_id = rb_obj_id(PyRuby_get_ruby_object(pyobj));
|
|
532
|
-
return protect_id2ref_and_set_pyerr(obj_id);
|
|
533
|
-
}
|
|
@@ -231,7 +231,7 @@ module PyCall
|
|
|
231
231
|
else
|
|
232
232
|
name, basic_size = *args
|
|
233
233
|
new.tap do |t|
|
|
234
|
-
# NOTE: Disable autorelease for avoiding SEGV
|
|
234
|
+
# NOTE: Disable autorelease for avoiding SEGV occurrence in Python's GC collect function
|
|
235
235
|
# at which the __new__ method object of this type object is freed.
|
|
236
236
|
t.pointer.autorelease = false
|
|
237
237
|
|
|
@@ -33,8 +33,6 @@ module PyCall
|
|
|
33
33
|
}.freeze
|
|
34
34
|
|
|
35
35
|
def method_missing(name, *args)
|
|
36
|
-
name_str = name.to_s if name.kind_of?(Symbol)
|
|
37
|
-
name_str.chop! if name_str.end_with?('=')
|
|
38
36
|
case name
|
|
39
37
|
when *OPERATOR_METHOD_NAMES.keys
|
|
40
38
|
op_name = OPERATOR_METHOD_NAMES[name]
|
|
@@ -44,7 +42,8 @@ module PyCall
|
|
|
44
42
|
return self.__send__(name, *args)
|
|
45
43
|
end
|
|
46
44
|
else
|
|
47
|
-
|
|
45
|
+
name_without_last_equal = name.to_s.chomp('=')
|
|
46
|
+
if LibPython::Helpers.hasattr?(__pyptr__, name_without_last_equal)
|
|
48
47
|
LibPython::Helpers.define_wrapper_method(self, name)
|
|
49
48
|
return self.__send__(name, *args)
|
|
50
49
|
end
|
data/lib/pycall/version.rb
CHANGED
data/lib/pycall.rb
CHANGED
|
@@ -116,7 +116,7 @@ module PyCall
|
|
|
116
116
|
rescue PyError => err
|
|
117
117
|
raise err unless ctx.__exit__(err.type, err.value, err.traceback)
|
|
118
118
|
rescue Exception => err
|
|
119
|
-
# TODO: support telling what exception has been
|
|
119
|
+
# TODO: support telling what exception has been caught
|
|
120
120
|
raise err unless ctx.__exit__(err.class, err, err.backtrace_locations)
|
|
121
121
|
else
|
|
122
122
|
ctx.__exit__(nil, nil, nil)
|