pycall 1.0.3 → 1.3.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.
@@ -2,7 +2,7 @@ require 'pycall/import'
2
2
  include PyCall::Import
3
3
 
4
4
  pyimport 'numpy', as: :np
5
- pyfrom 'sklearn.cross_validation', import: :train_test_split
5
+ pyfrom 'sklearn.model_selection', import: :train_test_split
6
6
  pyfrom 'sklearn.preprocessing', import: :StandardScaler
7
7
  pyfrom 'sklearn.datasets', import: %i(make_moons make_circles make_classification)
8
8
  pyfrom 'sklearn.neighbors', import: :KNeighborsClassifier
data/examples/hist.rb CHANGED
@@ -20,7 +20,7 @@ num_bins = 50
20
20
 
21
21
  fig, ax = *plt.subplots
22
22
 
23
- n, bins, patches = *ax.hist(x, num_bins, normed: 1)
23
+ n, bins, patches = *ax.hist(x, num_bins, density: 1)
24
24
 
25
25
  y = mlab.normpdf(bins, mu, sigma)
26
26
  ax.plot(bins, y, '--')
@@ -38,7 +38,7 @@
38
38
  "include PyCall::Import\n",
39
39
  "\n",
40
40
  "pyimport 'numpy', as: :np\n",
41
- "pyfrom 'sklearn.cross_validation', import: :train_test_split\n",
41
+ "pyfrom 'sklearn.model_selection', import: :train_test_split\n",
42
42
  "pyfrom 'sklearn.preprocessing', import: :StandardScaler\n",
43
43
  "pyfrom 'sklearn.datasets', import: %i(make_moons make_circles make_classification)\n",
44
44
  "pyfrom 'sklearn.neighbors', import: :KNeighborsClassifier\n",
@@ -30,7 +30,13 @@ lookup_libpython_api(VALUE libpython_handle, char const *name)
30
30
  arg.libpython_handle = libpython_handle;
31
31
  arg.name = name;
32
32
  addr = rb_protect((VALUE (*)(VALUE))lookup_libpython_api_0, (VALUE)&arg, &state);
33
- return (state || NIL_P(addr)) ? NULL : NUM2PTR(addr);
33
+ if (state) {
34
+ rb_set_errinfo(Qnil);
35
+ return NULL;
36
+ }
37
+ else {
38
+ return NIL_P(addr) ? NULL : NUM2PTR(addr);
39
+ }
34
40
  }
35
41
 
36
42
  #define LOOKUP_API_ENTRY(api_name) lookup_libpython_api(libpython_handle, #api_name)
@@ -94,6 +100,7 @@ pycall_init_libpython_api_table(VALUE libpython_handle)
94
100
  INIT_API_TABLE_ENTRY(_PyObject_New, required);
95
101
  INIT_API_TABLE_ENTRY(PyCallable_Check, required);
96
102
  INIT_API_TABLE_ENTRY(PyObject_IsInstance, required);
103
+ INIT_API_TABLE_ENTRY(PyObject_IsSubclass, required);
97
104
  INIT_API_TABLE_ENTRY2(PyObject_Hash._hash_t, PyObject_Hash, required);
98
105
  INIT_API_TABLE_ENTRY(PyObject_RichCompare, required);
99
106
  INIT_API_TABLE_ENTRY(PyObject_Call, required);
@@ -154,12 +161,16 @@ pycall_init_libpython_api_table(VALUE libpython_handle)
154
161
 
155
162
  INIT_API_TABLE_ENTRY(PyIter_Next, required);
156
163
 
164
+ INIT_API_TABLE_ENTRY(PyEval_ThreadsInitialized, required);
165
+ INIT_API_TABLE_ENTRY(PyEval_InitThreads, required);
166
+
157
167
  INIT_API_TABLE_ENTRY(PyErr_Occurred, required);
158
168
  INIT_API_TABLE_ENTRY(PyErr_Fetch, required);
159
169
  INIT_API_TABLE_ENTRY(PyErr_Restore, required);
160
170
  INIT_API_TABLE_ENTRY(PyErr_Clear, required);
161
171
  INIT_API_TABLE_ENTRY(PyErr_SetString, required);
162
172
  INIT_API_TABLE_ENTRY(PyErr_Format, required);
173
+ INIT_API_TABLE_ENTRY(PyErr_SetInterrupt, required);
163
174
 
164
175
  INIT_API_TABLE_ENTRY(PyImport_ImportModule, required);
165
176
  INIT_API_TABLE_ENTRY(PyImport_ImportModuleLevel, required);
data/ext/pycall/pycall.c CHANGED
@@ -1,4 +1,5 @@
1
1
  #include "pycall_internal.h"
2
+ #include "pycall.h"
2
3
  #include <ruby/encoding.h>
3
4
 
4
5
  #include <stdarg.h>
@@ -69,6 +70,52 @@ pycall_after_fork(VALUE mod)
69
70
  return Qnil;
70
71
  }
71
72
 
73
+ static volatile pycall_tls_key without_gvl_key;
74
+
75
+ int
76
+ pycall_without_gvl_p(void)
77
+ {
78
+ /*
79
+ * In pthread, the default value is NULL (== 0).
80
+ *
81
+ * In Win32 thread, the default value is 0 (initialized by TlsAlloc).
82
+ */
83
+ return (int)pycall_tls_get(without_gvl_key);
84
+ }
85
+
86
+ static inline int
87
+ pycall_set_without_gvl(void)
88
+ {
89
+ return pycall_tls_set(without_gvl_key, (void *)1);
90
+ }
91
+
92
+ static inline int
93
+ pycall_set_with_gvl(void)
94
+ {
95
+ return pycall_tls_set(without_gvl_key, (void *)0);
96
+ }
97
+
98
+ VALUE
99
+ pycall_without_gvl(VALUE (* func)(VALUE), VALUE arg)
100
+ {
101
+ int state;
102
+ VALUE result;
103
+
104
+ pycall_set_without_gvl();
105
+
106
+ result = rb_protect(func, arg, &state);
107
+
108
+ pycall_set_with_gvl();
109
+
110
+ return result;
111
+ }
112
+
113
+ static VALUE
114
+ pycall_m_without_gvl(VALUE mod)
115
+ {
116
+ return pycall_without_gvl(rb_yield, Qnil);
117
+ }
118
+
72
119
  /* ==== PyCall::PyPtr ==== */
73
120
 
74
121
  const rb_data_type_t pycall_pyptr_data_type = {
@@ -143,6 +190,12 @@ get_pyobj_ptr(VALUE obj)
143
190
  return pyobj;
144
191
  }
145
192
 
193
+ PyObject *
194
+ pycall_pyptr_get_pyobj_ptr(VALUE pyptr)
195
+ {
196
+ return get_pyobj_ptr(pyptr);
197
+ }
198
+
146
199
  static inline PyObject*
147
200
  try_get_pyobj_ptr(VALUE obj)
148
201
  {
@@ -445,7 +498,7 @@ get_pytypeobj_ptr(VALUE obj)
445
498
  static inline PyTypeObject*
446
499
  try_get_pytypeobj_ptr(VALUE obj)
447
500
  {
448
- if (is_pycall_pytypeptr(obj)) return NULL;
501
+ if (!is_pycall_pytypeptr(obj)) return NULL;
449
502
  return (PyTypeObject*)DATA_PTR(obj);
450
503
  }
451
504
 
@@ -534,6 +587,20 @@ pycall_pytypeptr_eqq(VALUE obj, VALUE other)
534
587
  return Qfalse;
535
588
  }
536
589
 
590
+ static VALUE
591
+ pycall_pytypeptr_subclass_p(VALUE obj, VALUE other)
592
+ {
593
+ PyTypeObject* pytype = get_pytypeobj_ptr(obj);
594
+ if (is_pycall_pyptr(other)) {
595
+ PyTypeObject* pytype_other = try_get_pytypeobj_ptr(other);
596
+ if (pytype_other) {
597
+ int res = Py_API(PyObject_IsSubclass)((PyObject *)pytype, (PyObject *)pytype_other);
598
+ return res ? Qtrue : Qfalse;
599
+ }
600
+ }
601
+ return Qfalse;
602
+ }
603
+
537
604
  /* ==== PyCall::LibPython::API ==== */
538
605
 
539
606
  static VALUE
@@ -727,7 +794,6 @@ pycall_libpython_helpers_m_compare(VALUE mod, VALUE op, VALUE pyptr_a, VALUE pyp
727
794
  }
728
795
 
729
796
  static int is_pyobject_wrapper(VALUE obj);
730
- static PyObject * pycall_pyobject_wrapper_get_pyobj_ptr(VALUE obj);
731
797
 
732
798
  VALUE
733
799
  pycall_getattr_default(VALUE obj, char const *name, VALUE default_value)
@@ -858,6 +924,47 @@ pycall_extract_kwargs_from_ruby_hash(VALUE key, VALUE value, VALUE arg)
858
924
  return ST_CONTINUE;
859
925
  }
860
926
 
927
+ static void
928
+ pycall_interrupt_python_thread(void *ptr)
929
+ {
930
+ Py_API(PyErr_SetInterrupt)();
931
+ }
932
+
933
+ struct call_pyobject_call_params {
934
+ PyObject *pycallable;
935
+ PyObject *args;
936
+ PyObject *kwargs;
937
+ };
938
+
939
+ static inline PyObject *
940
+ call_pyobject_call(struct call_pyobject_call_params *params)
941
+ {
942
+ PyObject *res;
943
+ res = Py_API(PyObject_Call)(params->pycallable, params->args, params->kwargs); /* New reference */
944
+ return res;
945
+ }
946
+
947
+ PyObject *
948
+ pyobject_call(PyObject *pycallable, PyObject *args, PyObject *kwargs)
949
+ {
950
+ PyObject *res;
951
+ struct call_pyobject_call_params params;
952
+ params.pycallable = pycallable;
953
+ params.args = args;
954
+ params.kwargs = kwargs;
955
+
956
+ if (pycall_without_gvl_p()) {
957
+ res = (PyObject *)rb_thread_call_without_gvl(
958
+ (void * (*)(void *))call_pyobject_call, (void *)&params,
959
+ (rb_unblock_function_t *)pycall_interrupt_python_thread, NULL);
960
+ }
961
+ else {
962
+ res = call_pyobject_call(&params);
963
+ }
964
+
965
+ return res;
966
+ }
967
+
861
968
  static VALUE
862
969
  pycall_call_python_callable(PyObject *pycallable, int argc, VALUE *argv)
863
970
  {
@@ -905,7 +1012,7 @@ pycall_call_python_callable(PyObject *pycallable, int argc, VALUE *argv)
905
1012
  }
906
1013
  }
907
1014
 
908
- res = Py_API(PyObject_Call)(pycallable, args, kwargs); /* New reference */
1015
+ res = pyobject_call(pycallable, args, kwargs); /* New reference */
909
1016
  if (!res) {
910
1017
  pycall_pyerror_fetch_and_raise("PyObject_Call in pycall_call_python_callable");
911
1018
  }
@@ -1213,7 +1320,7 @@ pycall_pyobject_wrapper_get_pyptr(VALUE obj)
1213
1320
  return rb_funcall(obj, rb_intern("__pyptr__"), 0);
1214
1321
  }
1215
1322
 
1216
- static PyObject *
1323
+ PyObject *
1217
1324
  pycall_pyobject_wrapper_get_pyobj_ptr(VALUE obj)
1218
1325
  {
1219
1326
  VALUE pyptr = pycall_pyobject_wrapper_get_pyptr(obj);
@@ -1235,11 +1342,39 @@ pycall_pyobject_wrapper_check_get_pyobj_ptr(VALUE obj, PyTypeObject *pytypeobj)
1235
1342
 
1236
1343
  /* ==== PyCall::Conversion ==== */
1237
1344
 
1345
+ static int
1346
+ get_mapped_ancestor_class_iter(VALUE key, VALUE value, VALUE arg)
1347
+ {
1348
+ VALUE *args = (VALUE *)arg;
1349
+ if (RTEST(pycall_pytypeptr_subclass_p(args[0], key))) {
1350
+ args[1] = value;
1351
+ return ST_STOP;
1352
+ }
1353
+ return ST_CONTINUE;
1354
+ }
1355
+
1356
+ static VALUE
1357
+ pycall_python_type_mapping_get_mapped_ancestor_class(VALUE pytypeptr)
1358
+ {
1359
+ VALUE args[2];
1360
+ args[0] = pytypeptr;
1361
+ args[1] = Qnil;
1362
+
1363
+ rb_hash_foreach(python_type_mapping, get_mapped_ancestor_class_iter, (VALUE)args);
1364
+
1365
+ return args[1];
1366
+ }
1367
+
1238
1368
  static VALUE
1239
1369
  pycall_python_type_mapping_get_mapped_class(VALUE pytypeptr)
1240
1370
  {
1371
+ VALUE mapped;
1241
1372
  (void)check_get_pytypeobj_ptr(pytypeptr);
1242
- return rb_hash_lookup(python_type_mapping, pytypeptr);
1373
+ mapped = rb_hash_lookup(python_type_mapping, pytypeptr);
1374
+ if (NIL_P(mapped)) {
1375
+ mapped = pycall_python_type_mapping_get_mapped_ancestor_class(pytypeptr);
1376
+ }
1377
+ return mapped;
1243
1378
  }
1244
1379
 
1245
1380
  static int
@@ -1947,6 +2082,10 @@ init_python(void)
1947
2082
  Py_API(Py_InitializeEx)(0);
1948
2083
  Py_API(PySys_SetArgvEx)(0, (char **)argv, 0);
1949
2084
 
2085
+ if (!Py_API(PyEval_ThreadsInitialized)()) {
2086
+ Py_API(PyEval_InitThreads)();
2087
+ }
2088
+
1950
2089
  /* check the availability of stackless extension */
1951
2090
  python_has_stackless_extension = (Py_API(PyImport_ImportModule)("stackless") != NULL);
1952
2091
  if (!python_has_stackless_extension) {
@@ -2078,6 +2217,7 @@ init_tuple(void)
2078
2217
  rb_define_singleton_method(cTuple, "new", pycall_tuple_s_new, -1);
2079
2218
  rb_define_method(cTuple, "length", pycall_tuple_length, 0);
2080
2219
  rb_define_method(cTuple, "to_a", pycall_tuple_to_a, 0);
2220
+ rb_define_alias(cTuple, "to_ary", "to_a");
2081
2221
  }
2082
2222
 
2083
2223
  void
@@ -2096,6 +2236,9 @@ Init_pycall(void)
2096
2236
 
2097
2237
  rb_define_module_function(mPyCall, "after_fork", pycall_after_fork, 0);
2098
2238
 
2239
+ pycall_tls_create(&without_gvl_key);
2240
+ rb_define_module_function(mPyCall, "without_gvl", pycall_m_without_gvl, 0);
2241
+
2099
2242
  /* PyCall::PyPtr */
2100
2243
 
2101
2244
  cPyPtr = rb_define_class_under(mPyCall, "PyPtr", rb_cBasicObject);
@@ -2132,6 +2275,7 @@ Init_pycall(void)
2132
2275
  rb_define_method(cPyTypePtr, "__tp_basicsize__", pycall_pytypeptr_get_tp_basicsize, 0);
2133
2276
  rb_define_method(cPyTypePtr, "__tp_flags__", pycall_pytypeptr_get_tp_flags, 0);
2134
2277
  rb_define_method(cPyTypePtr, "===", pycall_pytypeptr_eqq, 1);
2278
+ rb_define_method(cPyTypePtr, "<", pycall_pytypeptr_subclass_p, 1);
2135
2279
 
2136
2280
  /* PyCall::LibPython::API */
2137
2281
 
@@ -0,0 +1,23 @@
1
+ #ifndef PYCALL_H
2
+ #define PYCALL_H 1
3
+
4
+ #if defined(__cplusplus)
5
+ extern "C" {
6
+ #if 0
7
+ } /* satisfy cc-mode */
8
+ #endif
9
+ #endif
10
+
11
+ VALUE pycall_pyptr_new(PyObject *pyobj);
12
+ PyObject *pycall_pyptr_get_pyobj_ptr(VALUE pyptr);
13
+ VALUE pycall_pyobject_to_ruby(PyObject *pyobj);
14
+ PyObject *pycall_pyobject_wrapper_get_pyobj_ptr(VALUE obj);
15
+
16
+ #if defined(__cplusplus)
17
+ #if 0
18
+ { /* satisfy cc-mode */
19
+ #endif
20
+ } /* extern "C" { */
21
+ #endif
22
+
23
+ #endif /* PYCALL_H */
@@ -10,10 +10,20 @@ extern "C" {
10
10
 
11
11
  #include <ruby.h>
12
12
  #include <ruby/encoding.h>
13
+ #include <ruby/thread.h>
14
+
13
15
  #include <assert.h>
14
16
  #include <inttypes.h>
15
17
  #include <limits.h>
16
18
 
19
+ #if defined(_WIN32)
20
+ # define PYCALL_THREAD_WIN32
21
+ # include <ruby/win32.h>
22
+ #elif defined(HAVE_PTHREAD_H)
23
+ # define PYCALL_THREAD_PTHREAD
24
+ # include <pthread.h>
25
+ #endif
26
+
17
27
  #if SIZEOF_LONG == SIZEOF_VOIDP
18
28
  # define PTR2NUM(x) (LONG2NUM((long)(x)))
19
29
  # define NUM2PTR(x) ((void*)(NUM2ULONG(x)))
@@ -34,6 +44,9 @@ pycall_integer_type_p(VALUE obj)
34
44
  #endif
35
45
 
36
46
  void ruby_debug_breakpoint();
47
+ int ruby_thread_has_gvl_p(void);
48
+
49
+ #define CALL_WITH_GVL(func, data) rb_thread_call_with_gvl((void * (*)(void *))(func), (void *)(data))
37
50
 
38
51
  /* ==== python ==== */
39
52
 
@@ -488,6 +501,23 @@ extern PyTypeObject PyRuby_Type;
488
501
 
489
502
  PyObject * PyRuby_New(VALUE ruby_object);
490
503
 
504
+ /* ==== thread support ==== */
505
+
506
+ #if defined(PYCALL_THREAD_WIN32)
507
+ typedef DWORD pycall_tls_key;
508
+ #elif defined(PYCALL_THREAD_PTHREAD)
509
+ typedef pthread_key_t pycall_tls_key;
510
+ #else
511
+ # error "unsupported thread type"
512
+ #endif
513
+
514
+ int pycall_tls_create(pycall_tls_key* tls_key);
515
+ void *pycall_tls_get(pycall_tls_key tls_key);
516
+ int pycall_tls_set(pycall_tls_key tls_key, void *ptr);
517
+
518
+ int pycall_without_gvl_p(void);
519
+ VALUE pycall_without_gvl(VALUE (* func)(VALUE), VALUE arg);
520
+
491
521
  /* ==== pycall ==== */
492
522
 
493
523
  typedef struct {
@@ -525,6 +555,7 @@ typedef struct {
525
555
  PyObject * (* _PyObject_New)(PyTypeObject *);
526
556
  int (* PyCallable_Check)(PyObject *);
527
557
  int (* PyObject_IsInstance)(PyObject *, PyObject *);
558
+ int (* PyObject_IsSubclass)(PyObject *, PyObject *);
528
559
  union {
529
560
  long (* _long)(PyObject *);
530
561
  Py_hash_t (* _hash_t)(PyObject *);
@@ -581,12 +612,16 @@ typedef struct {
581
612
 
582
613
  PyObject * (* PyIter_Next)(PyObject *);
583
614
 
615
+ int (* PyEval_ThreadsInitialized)(void);
616
+ void (* PyEval_InitThreads)(void);
617
+
584
618
  PyObject * (* PyErr_Occurred)(void);
585
619
  void (* PyErr_Fetch)(PyObject **, PyObject **, PyObject **);
586
620
  void (* PyErr_Restore)(PyObject *, PyObject *, PyObject *);
587
621
  void (* PyErr_Clear)(void);
588
622
  void (* PyErr_SetString)(PyObject *, const char *); /* decoded from utf-8 */
589
623
  void (* PyErr_Format)(PyObject *, const char *, ...); /* ASCII-encoded string */
624
+ void (* PyErr_SetInterrupt)(void);
590
625
 
591
626
  PyObject * (* PyImport_ImportModule)(char const*);
592
627
  PyObject * (* PyImport_ImportModuleLevel)(char const*, PyObject *, PyObject *, PyObject *, int);
@@ -637,7 +672,6 @@ VALUE pycall_import_module_level(char const *name, VALUE globals, VALUE locals,
637
672
  VALUE pycall_getattr_default(VALUE pyobj, char const *name, VALUE default_value);
638
673
  VALUE pycall_getattr(VALUE pyobj, char const *name);
639
674
 
640
- VALUE pycall_pyobject_to_ruby(PyObject *);
641
675
  VALUE pycall_pytype_to_ruby(PyObject *);
642
676
  VALUE pycall_pymodule_to_ruby(PyObject *);
643
677
  VALUE pycall_pybool_to_ruby(PyObject *);
@@ -1,33 +1,35 @@
1
1
  #include "pycall_internal.h"
2
+ #include "pycall.h"
2
3
 
3
4
  static PyMemberDef PyRuby_members[] = {
4
5
  {"ruby_object_ptr", Py_T_PYSSIZET, offsetof(PyRubyObject, ruby_object), Py_READONLY},
5
6
  {NULL} /* sentinel */
6
7
  };
7
8
 
8
- static void PyRuby_dealloc(PyRubyObject *);
9
- static PyObject * PyRuby_repr(PyRubyObject *);
10
- static PyObject * PyRuby_call(PyRubyObject *, PyObject *, PyObject *);
11
- static PyObject * PyRuby_getattro(PyRubyObject *, PyObject *);
9
+ static VALUE PyRuby_get_ruby_object_and_set_pyerr(PyObject *pyobj);
10
+ static void PyRuby_dealloc_with_gvl(PyRubyObject *);
11
+ static PyObject * PyRuby_repr_with_gvl(PyRubyObject *);
12
+ static PyObject * PyRuby_call_with_gvl(PyRubyObject *, PyObject *, PyObject *);
13
+ static PyObject * PyRuby_getattro_with_gvl(PyRubyObject *, PyObject *);
12
14
 
13
15
  PyTypeObject PyRuby_Type = {
14
16
  PyVarObject_HEAD_INIT(NULL, 0)
15
17
  "PyCall.ruby_object", /* tp_name */
16
18
  sizeof(PyRubyObject), /* tp_basicsize */
17
19
  0, /* tp_itemsize */
18
- (destructor)PyRuby_dealloc, /* tp_dealloc */
20
+ (destructor)PyRuby_dealloc_with_gvl, /* tp_dealloc */
19
21
  0, /* tp_print */
20
22
  0, /* tp_getattr */
21
23
  0, /* tp_setattr */
22
24
  0, /* tp_reserved */
23
- (reprfunc)PyRuby_repr, /* tp_repr */
25
+ (reprfunc)PyRuby_repr_with_gvl, /* tp_repr */
24
26
  0, /* tp_as_number */
25
27
  0, /* tp_as_sequence */
26
28
  0, /* tp_as_mapping */
27
29
  {0}, /* tp_hash */
28
- (ternaryfunc)PyRuby_call, /* tp_call */
30
+ (ternaryfunc)PyRuby_call_with_gvl, /* tp_call */
29
31
  0, /* tp_str */
30
- (getattrofunc)PyRuby_getattro, /* tp_getattro */
32
+ (getattrofunc)PyRuby_getattro_with_gvl, /* tp_getattro */
31
33
  0, /* tp_setattro */
32
34
  0, /* tp_as_buffer */
33
35
  Py_TPFLAGS_BASETYPE, /* tp_flags */
@@ -42,8 +44,8 @@ PyTypeObject PyRuby_Type = {
42
44
  PyRuby_members, /*tp_members*/
43
45
  };
44
46
 
45
- PyObject *
46
- PyRuby_New(VALUE ruby_object)
47
+ static PyObject *
48
+ PyRuby_New_impl(VALUE ruby_object)
47
49
  {
48
50
  PyRubyObject *op;
49
51
 
@@ -53,59 +55,17 @@ PyRuby_New(VALUE ruby_object)
53
55
  return (PyObject *)op;
54
56
  }
55
57
 
56
- static VALUE
57
- funcall_id2ref(VALUE object_id)
58
- {
59
- VALUE rb_mObjSpace;
60
- object_id = rb_check_to_integer(object_id, "to_int");
61
- rb_mObjSpace = rb_const_get(rb_cObject, rb_intern("ObjectSpace"));
62
- return rb_funcall(rb_mObjSpace, rb_intern("_id2ref"), 1, object_id);
63
- }
64
-
65
- static VALUE
66
- protect_id2ref(VALUE object_id)
67
- {
68
- VALUE obj;
69
- int state;
70
-
71
- obj = rb_protect((VALUE (*)(VALUE))funcall_id2ref, object_id, &state);
72
- if (state)
73
- return Qundef;
74
-
75
- return obj;
76
- }
77
-
78
- static VALUE
79
- protect_id2ref_and_set_pyerr(VALUE object_id)
58
+ PyObject *
59
+ PyRuby_New(VALUE ruby_object)
80
60
  {
81
- VALUE obj = protect_id2ref(object_id);
82
- if (obj != Qundef)
83
- return obj;
84
-
85
- obj = rb_errinfo();
86
- if (RTEST(rb_obj_is_kind_of(obj, rb_eRangeError))) {
87
- Py_API(PyErr_SetString)(Py_API(PyExc_RuntimeError), "[BUG] referenced object was garbage-collected");
61
+ if (!ruby_thread_has_gvl_p()) {
62
+ CALL_WITH_GVL(PyRuby_New_impl, ruby_object);
88
63
  }
89
- else {
90
- VALUE emesg = rb_check_funcall(obj, rb_intern("message"), 0, 0);
91
- Py_API(PyErr_Format)(Py_API(PyExc_RuntimeError),
92
- "[BUG] Unable to obtain ruby object from ID: %s (%s)",
93
- StringValueCStr(emesg), rb_class2name(CLASS_OF(obj)));
94
- }
95
- return Qundef;
96
- }
97
64
 
98
- static VALUE
99
- PyRuby_get_ruby_object_and_set_pyerr(PyObject *pyobj)
100
- {
101
- VALUE obj_id;
102
- if (!PyRuby_Check(pyobj))
103
- return Qundef;
104
- obj_id = rb_obj_id(PyRuby_get_ruby_object(pyobj));
105
- return protect_id2ref_and_set_pyerr(obj_id);
65
+ return PyRuby_New_impl(ruby_object);
106
66
  }
107
67
 
108
- static void
68
+ static void *
109
69
  PyRuby_dealloc(PyRubyObject *pyro)
110
70
  {
111
71
  VALUE obj;
@@ -117,9 +77,20 @@ PyRuby_dealloc(PyRubyObject *pyro)
117
77
  #endif /* PYCALL_DEBUG_DUMP_REFCNT */
118
78
 
119
79
  if (obj == Qundef)
120
- return;
80
+ return NULL;
121
81
 
122
82
  pycall_gcguard_unregister_pyrubyobj((PyObject *)pyro);
83
+
84
+ return NULL;
85
+ }
86
+
87
+ static void
88
+ PyRuby_dealloc_with_gvl(PyRubyObject *pyro)
89
+ {
90
+ if (!ruby_thread_has_gvl_p()) {
91
+ CALL_WITH_GVL(PyRuby_dealloc, pyro);
92
+ }
93
+ PyRuby_dealloc(pyro);
123
94
  }
124
95
 
125
96
  static PyObject *
@@ -137,6 +108,15 @@ PyRuby_repr(PyRubyObject *pyro)
137
108
  return res;
138
109
  }
139
110
 
111
+ static PyObject *
112
+ PyRuby_repr_with_gvl(PyRubyObject *pyro)
113
+ {
114
+ if (!ruby_thread_has_gvl_p()) {
115
+ return CALL_WITH_GVL(PyRuby_repr, pyro);
116
+ }
117
+ return PyRuby_repr(pyro);
118
+ }
119
+
140
120
  #if SIZEOF_SSIZE_T < 8
141
121
  /* int64to32hash from src/support/hashing.c in Julia */
142
122
  static inline uint32_t
@@ -152,7 +132,7 @@ int64to32hash(uint64_t key)
152
132
  }
153
133
  #endif
154
134
 
155
- static long
135
+ static void *
156
136
  PyRuby_hash_long(PyRubyObject *pyro)
157
137
  {
158
138
  VALUE obj, rbhash;
@@ -160,15 +140,24 @@ PyRuby_hash_long(PyRubyObject *pyro)
160
140
 
161
141
  obj = PyRuby_get_ruby_object_and_set_pyerr((PyObject *)pyro);
162
142
  if (obj == Qundef)
163
- return -1;
143
+ return (void *)-1;
164
144
 
165
145
  rbhash = rb_hash(obj);
166
146
  h = FIX2LONG(rbhash); /* Ruby's hash value is a Fixnum */
167
147
 
168
- return h == -1 ? pycall_hash_salt : h;
148
+ return (void *)(h == -1 ? pycall_hash_salt : h);
169
149
  }
170
150
 
171
- static Py_hash_t
151
+ static long
152
+ PyRuby_hash_long_with_gvl(PyRubyObject *pyro)
153
+ {
154
+ if (!ruby_thread_has_gvl_p()) {
155
+ return (long)CALL_WITH_GVL(PyRuby_hash_long, pyro);
156
+ }
157
+ return (long)PyRuby_hash_long(pyro);
158
+ }
159
+
160
+ static void *
172
161
  PyRuby_hash_hash_t(PyRubyObject *pyro)
173
162
  {
174
163
  VALUE obj, rbhash;
@@ -176,20 +165,29 @@ PyRuby_hash_hash_t(PyRubyObject *pyro)
176
165
 
177
166
  obj = PyRuby_get_ruby_object_and_set_pyerr((PyObject *)pyro);
178
167
  if (obj == Qundef)
179
- return -1;
168
+ return (void *)-1;
180
169
 
181
170
  rbhash = rb_hash(obj);
182
171
  #if SIZEOF_PY_HASH_T == SIZEOF_LONG
183
172
  /* In this case, we can assume sizeof(Py_hash_t) == sizeof(long) */
184
173
  h = NUM2SSIZET(rbhash);
185
- return h == -1 ? (Py_hash_t)pycall_hash_salt : h;
174
+ return (void *)(h == -1 ? (Py_hash_t)pycall_hash_salt : h);
186
175
  #else
187
176
  /* In this case, we can assume sizeof(long) == 4 and sizeof(Py_hash_t) == 8 */
188
177
  h = (pycall_hash_salt_32 << 32) | FIX2LONG(rbhash);
189
- return h == -1 ? ((pycall_hash_salt << 32) | pycall_hash_salt) : h;
178
+ return (void *)(h == -1 ? ((pycall_hash_salt << 32) | pycall_hash_salt) : h);
190
179
  #endif
191
180
  }
192
181
 
182
+ static Py_hash_t
183
+ PyRuby_hash_hash_t_with_gvl(PyRubyObject *pyro)
184
+ {
185
+ if (!ruby_thread_has_gvl_p()) {
186
+ return (Py_hash_t)CALL_WITH_GVL(PyRuby_hash_hash_t, pyro);
187
+ }
188
+ return (Py_hash_t)PyRuby_hash_hash_t(pyro);
189
+ }
190
+
193
191
  struct call_rb_funcallv_params {
194
192
  VALUE recv;
195
193
  ID meth;
@@ -221,15 +219,21 @@ rb_protect_funcallv(VALUE recv, ID meth, int argc, VALUE *argv, int *pstate)
221
219
  return res;
222
220
  }
223
221
 
222
+ struct PyRuby_call_params {
223
+ PyRubyObject *pyro;
224
+ PyObject *pyobj_args;
225
+ PyObject *pyobj_kwargs;
226
+ };
227
+
224
228
  static PyObject *
225
- PyRuby_call(PyRubyObject *pyro, PyObject *pyobj_args, PyObject *pyobj_kwargs)
229
+ PyRuby_call(struct PyRuby_call_params *params)
226
230
  {
227
231
  ID id_call;
228
232
  VALUE obj, args, kwargs, res;
229
233
  PyObject *pyobj_res;
230
234
  int state;
231
235
 
232
- obj = PyRuby_get_ruby_object_and_set_pyerr((PyObject *)pyro);
236
+ obj = PyRuby_get_ruby_object_and_set_pyerr((PyObject *)params->pyro);
233
237
  if (obj == Qundef)
234
238
  return NULL;
235
239
 
@@ -239,9 +243,9 @@ PyRuby_call(PyRubyObject *pyro, PyObject *pyobj_args, PyObject *pyobj_kwargs)
239
243
  return NULL;
240
244
  }
241
245
 
242
- args = pycall_pyobject_to_a(pyobj_args);
243
- if (pyobj_kwargs) {
244
- kwargs = pycall_pyobject_to_ruby(pyobj_kwargs);
246
+ args = pycall_pyobject_to_a(params->pyobj_args);
247
+ if (params->pyobj_kwargs) {
248
+ kwargs = pycall_pyobject_to_ruby(params->pyobj_kwargs);
245
249
  rb_ary_push(args, kwargs);
246
250
  }
247
251
 
@@ -255,18 +259,38 @@ PyRuby_call(PyRubyObject *pyro, PyObject *pyobj_args, PyObject *pyobj_kwargs)
255
259
  }
256
260
 
257
261
  static PyObject *
258
- PyRuby_getattro(PyRubyObject *pyro, PyObject *pyobj_name)
262
+ PyRuby_call_with_gvl(PyRubyObject *pyro, PyObject *pyobj_args, PyObject *pyobj_kwargs)
263
+ {
264
+ struct PyRuby_call_params params;
265
+ params.pyro = pyro;
266
+ params.pyobj_args = pyobj_args;
267
+ params.pyobj_kwargs = pyobj_kwargs;
268
+
269
+ if (!ruby_thread_has_gvl_p()) {
270
+ return CALL_WITH_GVL(PyRuby_call, &params);
271
+ }
272
+
273
+ return PyRuby_call(&params);
274
+ }
275
+
276
+ struct PyRuby_getattro_params {
277
+ PyRubyObject *pyro;
278
+ PyObject *pyobj_name;
279
+ };
280
+
281
+ static PyObject *
282
+ PyRuby_getattro(struct PyRuby_getattro_params *params)
259
283
  {
260
284
  VALUE obj, name, res;
261
285
  char const *name_cstr;
262
286
  ID name_id;
263
287
  PyObject *pyobj_res;
264
288
 
265
- obj = PyRuby_get_ruby_object_and_set_pyerr((PyObject *)pyro);
289
+ obj = PyRuby_get_ruby_object_and_set_pyerr((PyObject *)params->pyro);
266
290
  if (obj == Qundef)
267
291
  return NULL;
268
292
 
269
- name = pycall_pyobject_to_ruby(pyobj_name);
293
+ name = pycall_pyobject_to_ruby(params->pyobj_name);
270
294
  name_cstr = StringValueCStr(name);
271
295
  name_id = rb_intern(name_cstr);
272
296
 
@@ -299,21 +323,35 @@ PyRuby_getattro(PyRubyObject *pyro, PyObject *pyobj_name)
299
323
  else if (name_cstr[0] == '_' && name_cstr[1] == '_') {
300
324
  /* name.start_with? "__" */
301
325
  /* TODO: handle `__code__` and `func_code` */
302
- return Py_API(PyObject_GenericGetAttr)((PyObject *)pyro, pyobj_name);
326
+ return Py_API(PyObject_GenericGetAttr)((PyObject *)params->pyro, params->pyobj_name);
303
327
  }
304
328
  else {
305
329
  /* TODO: handle `__code__` and `func_code` */
306
330
  if (rb_respond_to(obj, name_id)) {
307
331
  VALUE method = rb_obj_method(obj, name);
308
- return PyRuby_New(method);
332
+ return PyRuby_New_impl(method);
309
333
  }
310
- return Py_API(PyObject_GenericGetAttr)((PyObject *)pyro, pyobj_name);
334
+ return Py_API(PyObject_GenericGetAttr)((PyObject *)params->pyro, params->pyobj_name);
311
335
  }
312
336
 
313
337
  pyobj_res = pycall_pyobject_from_ruby(res);
314
338
  return pyobj_res;
315
339
  }
316
340
 
341
+ static PyObject *
342
+ PyRuby_getattro_with_gvl(PyRubyObject *pyro, PyObject *pyobj_name)
343
+ {
344
+ struct PyRuby_getattro_params params;
345
+ params.pyro = pyro;
346
+ params.pyobj_name = pyobj_name;
347
+
348
+ if (!ruby_thread_has_gvl_p()) {
349
+ return CALL_WITH_GVL(PyRuby_getattro, &params);
350
+ }
351
+
352
+ return PyRuby_getattro(&params);
353
+ }
354
+
317
355
  /* ==== PyCall::PyRubyPtr ==== */
318
356
 
319
357
  VALUE cPyRubyPtr;
@@ -411,9 +449,9 @@ pycall_init_ruby_wrapper(void)
411
449
  PyRuby_Type.tp_flags |= pycall_default_tp_flags();
412
450
  PyRuby_Type.tp_new = Py_API(PyType_GenericNew);
413
451
  if (pycall_python_long_hash)
414
- PyRuby_Type.tp_hash._long = (hashfunc_long)PyRuby_hash_long;
452
+ PyRuby_Type.tp_hash._long = (hashfunc_long)PyRuby_hash_long_with_gvl;
415
453
  else
416
- PyRuby_Type.tp_hash._hash_t = (hashfunc_hash_t)PyRuby_hash_hash_t;
454
+ PyRuby_Type.tp_hash._hash_t = (hashfunc_hash_t)PyRuby_hash_hash_t_with_gvl;
417
455
 
418
456
  if (Py_API(PyType_Ready)(&PyRuby_Type) < 0) {
419
457
  pycall_pyerror_fetch_and_raise("PyType_Ready in pycall_init_ruby_wrapper");
@@ -430,3 +468,57 @@ pycall_init_ruby_wrapper(void)
430
468
 
431
469
  rb_define_module_function(mPyCall, "wrap_ruby_object", pycall_m_wrap_ruby_object, 1);
432
470
  }
471
+
472
+ /* --- File internal utilities --- */
473
+
474
+ static VALUE
475
+ funcall_id2ref(VALUE object_id)
476
+ {
477
+ VALUE rb_mObjSpace;
478
+ object_id = rb_check_to_integer(object_id, "to_int");
479
+ rb_mObjSpace = rb_const_get(rb_cObject, rb_intern("ObjectSpace"));
480
+ return rb_funcall(rb_mObjSpace, rb_intern("_id2ref"), 1, object_id);
481
+ }
482
+
483
+ static VALUE
484
+ protect_id2ref(VALUE object_id)
485
+ {
486
+ VALUE obj;
487
+ int state;
488
+
489
+ obj = rb_protect((VALUE (*)(VALUE))funcall_id2ref, object_id, &state);
490
+ if (state)
491
+ return Qundef;
492
+
493
+ return obj;
494
+ }
495
+
496
+ static VALUE
497
+ protect_id2ref_and_set_pyerr(VALUE object_id)
498
+ {
499
+ VALUE obj = protect_id2ref(object_id);
500
+ if (obj != Qundef)
501
+ return obj;
502
+
503
+ obj = rb_errinfo();
504
+ if (RTEST(rb_obj_is_kind_of(obj, rb_eRangeError))) {
505
+ Py_API(PyErr_SetString)(Py_API(PyExc_RuntimeError), "[BUG] referenced object was garbage-collected");
506
+ }
507
+ else {
508
+ VALUE emesg = rb_check_funcall(obj, rb_intern("message"), 0, 0);
509
+ Py_API(PyErr_Format)(Py_API(PyExc_RuntimeError),
510
+ "[BUG] Unable to obtain ruby object from ID: %s (%s)",
511
+ StringValueCStr(emesg), rb_class2name(CLASS_OF(obj)));
512
+ }
513
+ return Qundef;
514
+ }
515
+
516
+ static VALUE
517
+ PyRuby_get_ruby_object_and_set_pyerr(PyObject *pyobj)
518
+ {
519
+ VALUE obj_id;
520
+ if (!PyRuby_Check(pyobj))
521
+ return Qundef;
522
+ obj_id = rb_obj_id(PyRuby_get_ruby_object(pyobj));
523
+ return protect_id2ref_and_set_pyerr(obj_id);
524
+ }