pycall 1.0.3 → 1.1.0.rc1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 254de6af22a7da5928ad1f5233727a7ac378c84c
4
- data.tar.gz: fc897d3873076f610b527fb23841c4044793e317
2
+ SHA256:
3
+ metadata.gz: 78588bb0a23c7fa9b5c522113598027e8c98aea9fb9b72e96b64a1db4e9cff41
4
+ data.tar.gz: '08d504ae9fdaa7ea8f532ca10848df3119264d956b259b40b3dfb356f5ea5834'
5
5
  SHA512:
6
- metadata.gz: 32b60a4408b80fd5df671b8c1c9177d5973ff9fff11603ea0f4dd9407dbb27e39532f7d269cd7d5805e0e275ab2e5e9e956a247100160ecc091028b9757828df
7
- data.tar.gz: 79d9c857888a3577f76428247bc73e999df73cbbac3269d39c86765d2b6656f32ca916caf54fe955921842a9119141e60b53d1a16973de63cf1a154b08715f27
6
+ metadata.gz: d3d9c586c37ec66a8a6a7bd1093b5fd0848aad2602ab6784fe73a50c08a05402289601d1e87d7bfe13f51b54f2f8002790e7c95ac4899de339ad8af7dedccae3
7
+ data.tar.gz: b3c3d61680274ff6e612dabd28818522637f479225f43a9a8025d2a036127b3fddc62d2342456673cf96cdc280ce8e388fa503e904b5d5860b44c0b0fa8c3a29
@@ -7,9 +7,10 @@ sudo: required
7
7
 
8
8
  rvm:
9
9
  - ruby-head
10
- - 2.4.2
10
+ - 2.5.0
11
+ - 2.4.3
11
12
  - 2.3.5
12
- - 2.2.8
13
+ - 2.2.9
13
14
  - 2.1.10
14
15
 
15
16
  env:
@@ -39,8 +40,10 @@ matrix:
39
40
  compiler: clang
40
41
  rvm: 2.4.1
41
42
  env: PYENV_VERSION=miniconda3-4.3.11
42
- allow_failure:
43
+ allow_failures:
43
44
  - os: osx
45
+ - rvm: 2.2.9
46
+ - rvm: 2.1.10
44
47
 
45
48
  before_install:
46
49
  - gem update --system
data/CHANGES.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # The change history of PyCall
2
2
 
3
+ ## master
4
+
5
+ * Add `PyCall::Tuple#to_ary`
6
+
7
+ *Naoto Takai*
8
+
9
+ * Release GVL while the Python interpreter is running
10
+
11
+ * Drop support Ruby 2.2.x and 2.1.x
12
+
13
+ * Release GVL while the Python interpreter is running [Fix #45]
14
+
15
+ * Add public header file
16
+
17
+ * Use PyPtr.none? instead of removed PyCall.none?
18
+
19
+ *Kouhei Sutou*
20
+
21
+ * Export PyObject convert functions
22
+
23
+ *Kouhei Sutou*
24
+
3
25
  ## 1.0.3
4
26
 
5
27
  * Fix anaconda support to define the environment variable `PYTHONHOME`.
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # PyCall: Calling Python functions from the Ruby language
2
2
 
3
- [![Build Status](https://travis-ci.org/mrkn/pycall.rb.svg?branch=1.0)](https://travis-ci.org/mrkn/pycall.rb)
4
- [![Build status](https://ci.appveyor.com/api/projects/status/naw56ldru71w7qsw/branch/1.0?svg=true)](https://ci.appveyor.com/project/mrkn/pycall-rb/branch/1.0)
3
+ [![Build Status](https://travis-ci.org/mrkn/pycall.rb.svg?branch=master)](https://travis-ci.org/mrkn/pycall.rb)
4
+ [![Build status](https://ci.appveyor.com/api/projects/status/071is0f4iu0vy8lp/branch/master?svg=true)](https://ci.appveyor.com/project/mrkn/pycall.rb/branch/master)
5
5
 
6
6
  This library provides the features to directly call and partially interoperate
7
7
  with Python from the Ruby language. You can import arbitrary Python modules
data/Rakefile CHANGED
@@ -17,6 +17,8 @@ Rake::ExtensionTask.new('pycall', gem_spec) do |ext|
17
17
  end
18
18
  end
19
19
 
20
+ Rake::ExtensionTask.new('pycall/spec_helper')
21
+
20
22
  desc "Compile binaries for mingw platform using rake-compiler-dock"
21
23
  task 'build:mingw' do
22
24
  require 'rake_compiler_dock'
@@ -1,71 +1,37 @@
1
1
  ---
2
2
  environment:
3
3
  matrix:
4
- # Ruby 2.1 (32bit)
5
- - ruby_version: "21"
4
+ # Ruby 2.4 (32bit)
5
+ - ruby_version: "24"
6
6
  PYTHONDIR: "C:\\Python27"
7
7
  PYTHON: "C:\\Python27\\python.exe"
8
8
 
9
- - ruby_version: "21"
9
+ - ruby_version: "24"
10
10
  PYTHONDIR: "C:\\Python34"
11
11
  PYTHON: "C:\\Python34\\python.exe"
12
12
 
13
- - ruby_version: "21"
13
+ - ruby_version: "24"
14
14
  PYTHONDIR: "C:\\Python35"
15
15
  PYTHON: "C:\\Python35\\python.exe"
16
16
 
17
- - ruby_version: "21"
17
+ - ruby_version: "24"
18
18
  PYTHONDIR: "C:\\Python36"
19
19
  PYTHON: "C:\\Python36\\python.exe"
20
20
 
21
- # Ruby 2.1 (64bit)
22
- - ruby_version: "21-x64"
21
+ # Ruby 2.4 (64bit)
22
+ - ruby_version: "24-x64"
23
23
  PYTHONDIR: "C:\\Python27-x64"
24
24
  PYTHON: "C:\\Python27-x64\\python.exe"
25
25
 
26
- - ruby_version: "21-x64"
26
+ - ruby_version: "24-x64"
27
27
  PYTHONDIR: "C:\\Python34-x64"
28
28
  PYTHON: "C:\\Python34-x64\\python.exe"
29
29
 
30
- - ruby_version: "21-x64"
30
+ - ruby_version: "24-x64"
31
31
  PYTHONDIR: "C:\\Python35-x64"
32
32
  PYTHON: "C:\\Python35-x64\\python.exe"
33
33
 
34
- - ruby_version: "21-x64"
35
- PYTHONDIR: "C:\\Python36-x64"
36
- PYTHON: "C:\\Python36-x64\\python.exe"
37
-
38
- # Ruby 2.2 (32bit)
39
- - ruby_version: "22"
40
- PYTHONDIR: "C:\\Python27"
41
- PYTHON: "C:\\Python27\\python.exe"
42
-
43
- - ruby_version: "22"
44
- PYTHONDIR: "C:\\Python34"
45
- PYTHON: "C:\\Python34\\python.exe"
46
-
47
- - ruby_version: "22"
48
- PYTHONDIR: "C:\\Python35"
49
- PYTHON: "C:\\Python35\\python.exe"
50
-
51
- - ruby_version: "22"
52
- PYTHONDIR: "C:\\Python36"
53
- PYTHON: "C:\\Python36\\python.exe"
54
-
55
- # Ruby 2.2 (64bit)
56
- - ruby_version: "22-x64"
57
- PYTHONDIR: "C:\\Python27-x64"
58
- PYTHON: "C:\\Python27-x64\\python.exe"
59
-
60
- - ruby_version: "22-x64"
61
- PYTHONDIR: "C:\\Python34-x64"
62
- PYTHON: "C:\\Python34-x64\\python.exe"
63
-
64
- - ruby_version: "22-x64"
65
- PYTHONDIR: "C:\\Python35-x64"
66
- PYTHON: "C:\\Python35-x64\\python.exe"
67
-
68
- - ruby_version: "22-x64"
34
+ - ruby_version: "24-x64"
69
35
  PYTHONDIR: "C:\\Python36-x64"
70
36
  PYTHON: "C:\\Python36-x64\\python.exe"
71
37
 
@@ -154,12 +154,16 @@ pycall_init_libpython_api_table(VALUE libpython_handle)
154
154
 
155
155
  INIT_API_TABLE_ENTRY(PyIter_Next, required);
156
156
 
157
+ INIT_API_TABLE_ENTRY(PyEval_ThreadsInitialized, required);
158
+ INIT_API_TABLE_ENTRY(PyEval_InitThreads, required);
159
+
157
160
  INIT_API_TABLE_ENTRY(PyErr_Occurred, required);
158
161
  INIT_API_TABLE_ENTRY(PyErr_Fetch, required);
159
162
  INIT_API_TABLE_ENTRY(PyErr_Restore, required);
160
163
  INIT_API_TABLE_ENTRY(PyErr_Clear, required);
161
164
  INIT_API_TABLE_ENTRY(PyErr_SetString, required);
162
165
  INIT_API_TABLE_ENTRY(PyErr_Format, required);
166
+ INIT_API_TABLE_ENTRY(PyErr_SetInterrupt, required);
163
167
 
164
168
  INIT_API_TABLE_ENTRY(PyImport_ImportModule, required);
165
169
  INIT_API_TABLE_ENTRY(PyImport_ImportModuleLevel, required);
@@ -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>
@@ -143,6 +144,12 @@ get_pyobj_ptr(VALUE obj)
143
144
  return pyobj;
144
145
  }
145
146
 
147
+ PyObject *
148
+ pycall_pyptr_get_pyobj_ptr(VALUE pyptr)
149
+ {
150
+ return get_pyobj_ptr(pyptr);
151
+ }
152
+
146
153
  static inline PyObject*
147
154
  try_get_pyobj_ptr(VALUE obj)
148
155
  {
@@ -727,7 +734,6 @@ pycall_libpython_helpers_m_compare(VALUE mod, VALUE op, VALUE pyptr_a, VALUE pyp
727
734
  }
728
735
 
729
736
  static int is_pyobject_wrapper(VALUE obj);
730
- static PyObject * pycall_pyobject_wrapper_get_pyobj_ptr(VALUE obj);
731
737
 
732
738
  VALUE
733
739
  pycall_getattr_default(VALUE obj, char const *name, VALUE default_value)
@@ -858,6 +864,42 @@ pycall_extract_kwargs_from_ruby_hash(VALUE key, VALUE value, VALUE arg)
858
864
  return ST_CONTINUE;
859
865
  }
860
866
 
867
+ static void
868
+ pycall_interrupt_python_thread(void *ptr)
869
+ {
870
+ Py_API(PyErr_SetInterrupt)();
871
+ }
872
+
873
+ struct call_pyobject_call_params {
874
+ PyObject *pycallable;
875
+ PyObject *args;
876
+ PyObject *kwargs;
877
+ };
878
+
879
+ PyObject *
880
+ call_pyobject_call(struct call_pyobject_call_params *params)
881
+ {
882
+ PyObject *res;
883
+ res = Py_API(PyObject_Call)(params->pycallable, params->args, params->kwargs); /* New reference */
884
+ return res;
885
+ }
886
+
887
+ PyObject *
888
+ pyobject_call_without_gvl(PyObject *pycallable, PyObject *args, PyObject *kwargs)
889
+ {
890
+ PyObject *res;
891
+ struct call_pyobject_call_params params;
892
+ params.pycallable = pycallable;
893
+ params.args = args;
894
+ params.kwargs = kwargs;
895
+
896
+ res = (PyObject *)rb_thread_call_without_gvl(
897
+ (void * (*)(void *))call_pyobject_call, (void *)&params,
898
+ (rb_unblock_function_t *)pycall_interrupt_python_thread, NULL);
899
+
900
+ return res;
901
+ }
902
+
861
903
  static VALUE
862
904
  pycall_call_python_callable(PyObject *pycallable, int argc, VALUE *argv)
863
905
  {
@@ -905,7 +947,7 @@ pycall_call_python_callable(PyObject *pycallable, int argc, VALUE *argv)
905
947
  }
906
948
  }
907
949
 
908
- res = Py_API(PyObject_Call)(pycallable, args, kwargs); /* New reference */
950
+ res = pyobject_call_without_gvl(pycallable, args, kwargs); /* New reference */
909
951
  if (!res) {
910
952
  pycall_pyerror_fetch_and_raise("PyObject_Call in pycall_call_python_callable");
911
953
  }
@@ -1213,7 +1255,7 @@ pycall_pyobject_wrapper_get_pyptr(VALUE obj)
1213
1255
  return rb_funcall(obj, rb_intern("__pyptr__"), 0);
1214
1256
  }
1215
1257
 
1216
- static PyObject *
1258
+ PyObject *
1217
1259
  pycall_pyobject_wrapper_get_pyobj_ptr(VALUE obj)
1218
1260
  {
1219
1261
  VALUE pyptr = pycall_pyobject_wrapper_get_pyptr(obj);
@@ -1947,6 +1989,10 @@ init_python(void)
1947
1989
  Py_API(Py_InitializeEx)(0);
1948
1990
  Py_API(PySys_SetArgvEx)(0, (char **)argv, 0);
1949
1991
 
1992
+ if (!Py_API(PyEval_ThreadsInitialized)()) {
1993
+ Py_API(PyEval_InitThreads)();
1994
+ }
1995
+
1950
1996
  /* check the availability of stackless extension */
1951
1997
  python_has_stackless_extension = (Py_API(PyImport_ImportModule)("stackless") != NULL);
1952
1998
  if (!python_has_stackless_extension) {
@@ -2078,6 +2124,7 @@ init_tuple(void)
2078
2124
  rb_define_singleton_method(cTuple, "new", pycall_tuple_s_new, -1);
2079
2125
  rb_define_method(cTuple, "length", pycall_tuple_length, 0);
2080
2126
  rb_define_method(cTuple, "to_a", pycall_tuple_to_a, 0);
2127
+ rb_define_alias(cTuple, "to_ary", "to_a");
2081
2128
  }
2082
2129
 
2083
2130
  void
@@ -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,6 +10,7 @@ extern "C" {
10
10
 
11
11
  #include <ruby.h>
12
12
  #include <ruby/encoding.h>
13
+ #include <ruby/thread.h>
13
14
  #include <assert.h>
14
15
  #include <inttypes.h>
15
16
  #include <limits.h>
@@ -34,6 +35,9 @@ pycall_integer_type_p(VALUE obj)
34
35
  #endif
35
36
 
36
37
  void ruby_debug_breakpoint();
38
+ int ruby_thread_has_gvl_p(void);
39
+
40
+ #define CALL_WITH_GVL(func, data) rb_thread_call_with_gvl((void * (*)(void *))(func), (void *)(data))
37
41
 
38
42
  /* ==== python ==== */
39
43
 
@@ -581,12 +585,16 @@ typedef struct {
581
585
 
582
586
  PyObject * (* PyIter_Next)(PyObject *);
583
587
 
588
+ int (* PyEval_ThreadsInitialized)(void);
589
+ void (* PyEval_InitThreads)(void);
590
+
584
591
  PyObject * (* PyErr_Occurred)(void);
585
592
  void (* PyErr_Fetch)(PyObject **, PyObject **, PyObject **);
586
593
  void (* PyErr_Restore)(PyObject *, PyObject *, PyObject *);
587
594
  void (* PyErr_Clear)(void);
588
595
  void (* PyErr_SetString)(PyObject *, const char *); /* decoded from utf-8 */
589
596
  void (* PyErr_Format)(PyObject *, const char *, ...); /* ASCII-encoded string */
597
+ void (* PyErr_SetInterrupt)(void);
590
598
 
591
599
  PyObject * (* PyImport_ImportModule)(char const*);
592
600
  PyObject * (* PyImport_ImportModuleLevel)(char const*, PyObject *, PyObject *, PyObject *, int);
@@ -637,7 +645,6 @@ VALUE pycall_import_module_level(char const *name, VALUE globals, VALUE locals,
637
645
  VALUE pycall_getattr_default(VALUE pyobj, char const *name, VALUE default_value);
638
646
  VALUE pycall_getattr(VALUE pyobj, char const *name);
639
647
 
640
- VALUE pycall_pyobject_to_ruby(PyObject *);
641
648
  VALUE pycall_pytype_to_ruby(PyObject *);
642
649
  VALUE pycall_pymodule_to_ruby(PyObject *);
643
650
  VALUE pycall_pybool_to_ruby(PyObject *);
@@ -5,29 +5,30 @@ static PyMemberDef PyRuby_members[] = {
5
5
  {NULL} /* sentinel */
6
6
  };
7
7
 
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 *);
8
+ static VALUE PyRuby_get_ruby_object_and_set_pyerr(PyObject *pyobj);
9
+ static void PyRuby_dealloc_with_gvl(PyRubyObject *);
10
+ static PyObject * PyRuby_repr_with_gvl(PyRubyObject *);
11
+ static PyObject * PyRuby_call_with_gvl(PyRubyObject *, PyObject *, PyObject *);
12
+ static PyObject * PyRuby_getattro_with_gvl(PyRubyObject *, PyObject *);
12
13
 
13
14
  PyTypeObject PyRuby_Type = {
14
15
  PyVarObject_HEAD_INIT(NULL, 0)
15
16
  "PyCall.ruby_object", /* tp_name */
16
17
  sizeof(PyRubyObject), /* tp_basicsize */
17
18
  0, /* tp_itemsize */
18
- (destructor)PyRuby_dealloc, /* tp_dealloc */
19
+ (destructor)PyRuby_dealloc_with_gvl, /* tp_dealloc */
19
20
  0, /* tp_print */
20
21
  0, /* tp_getattr */
21
22
  0, /* tp_setattr */
22
23
  0, /* tp_reserved */
23
- (reprfunc)PyRuby_repr, /* tp_repr */
24
+ (reprfunc)PyRuby_repr_with_gvl, /* tp_repr */
24
25
  0, /* tp_as_number */
25
26
  0, /* tp_as_sequence */
26
27
  0, /* tp_as_mapping */
27
28
  {0}, /* tp_hash */
28
- (ternaryfunc)PyRuby_call, /* tp_call */
29
+ (ternaryfunc)PyRuby_call_with_gvl, /* tp_call */
29
30
  0, /* tp_str */
30
- (getattrofunc)PyRuby_getattro, /* tp_getattro */
31
+ (getattrofunc)PyRuby_getattro_with_gvl, /* tp_getattro */
31
32
  0, /* tp_setattro */
32
33
  0, /* tp_as_buffer */
33
34
  Py_TPFLAGS_BASETYPE, /* tp_flags */
@@ -42,8 +43,8 @@ PyTypeObject PyRuby_Type = {
42
43
  PyRuby_members, /*tp_members*/
43
44
  };
44
45
 
45
- PyObject *
46
- PyRuby_New(VALUE ruby_object)
46
+ static PyObject *
47
+ PyRuby_New_impl(VALUE ruby_object)
47
48
  {
48
49
  PyRubyObject *op;
49
50
 
@@ -53,59 +54,17 @@ PyRuby_New(VALUE ruby_object)
53
54
  return (PyObject *)op;
54
55
  }
55
56
 
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)
57
+ PyObject *
58
+ PyRuby_New(VALUE ruby_object)
80
59
  {
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");
60
+ if (!ruby_thread_has_gvl_p()) {
61
+ CALL_WITH_GVL(PyRuby_New_impl, ruby_object);
88
62
  }
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
63
 
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);
64
+ return PyRuby_New_impl(ruby_object);
106
65
  }
107
66
 
108
- static void
67
+ static void *
109
68
  PyRuby_dealloc(PyRubyObject *pyro)
110
69
  {
111
70
  VALUE obj;
@@ -117,9 +76,20 @@ PyRuby_dealloc(PyRubyObject *pyro)
117
76
  #endif /* PYCALL_DEBUG_DUMP_REFCNT */
118
77
 
119
78
  if (obj == Qundef)
120
- return;
79
+ return NULL;
121
80
 
122
81
  pycall_gcguard_unregister_pyrubyobj((PyObject *)pyro);
82
+
83
+ return NULL;
84
+ }
85
+
86
+ static void
87
+ PyRuby_dealloc_with_gvl(PyRubyObject *pyro)
88
+ {
89
+ if (!ruby_thread_has_gvl_p()) {
90
+ CALL_WITH_GVL(PyRuby_dealloc, pyro);
91
+ }
92
+ PyRuby_dealloc(pyro);
123
93
  }
124
94
 
125
95
  static PyObject *
@@ -137,6 +107,15 @@ PyRuby_repr(PyRubyObject *pyro)
137
107
  return res;
138
108
  }
139
109
 
110
+ static PyObject *
111
+ PyRuby_repr_with_gvl(PyRubyObject *pyro)
112
+ {
113
+ if (!ruby_thread_has_gvl_p()) {
114
+ return CALL_WITH_GVL(PyRuby_repr, pyro);
115
+ }
116
+ return PyRuby_repr(pyro);
117
+ }
118
+
140
119
  #if SIZEOF_SSIZE_T < 8
141
120
  /* int64to32hash from src/support/hashing.c in Julia */
142
121
  static inline uint32_t
@@ -152,7 +131,7 @@ int64to32hash(uint64_t key)
152
131
  }
153
132
  #endif
154
133
 
155
- static long
134
+ static void *
156
135
  PyRuby_hash_long(PyRubyObject *pyro)
157
136
  {
158
137
  VALUE obj, rbhash;
@@ -160,15 +139,24 @@ PyRuby_hash_long(PyRubyObject *pyro)
160
139
 
161
140
  obj = PyRuby_get_ruby_object_and_set_pyerr((PyObject *)pyro);
162
141
  if (obj == Qundef)
163
- return -1;
142
+ return (void *)-1;
164
143
 
165
144
  rbhash = rb_hash(obj);
166
145
  h = FIX2LONG(rbhash); /* Ruby's hash value is a Fixnum */
167
146
 
168
- return h == -1 ? pycall_hash_salt : h;
147
+ return (void *)(h == -1 ? pycall_hash_salt : h);
169
148
  }
170
149
 
171
- static Py_hash_t
150
+ static long
151
+ PyRuby_hash_long_with_gvl(PyRubyObject *pyro)
152
+ {
153
+ if (!ruby_thread_has_gvl_p()) {
154
+ return (long)CALL_WITH_GVL(PyRuby_hash_long, pyro);
155
+ }
156
+ return (long)PyRuby_hash_long(pyro);
157
+ }
158
+
159
+ static void *
172
160
  PyRuby_hash_hash_t(PyRubyObject *pyro)
173
161
  {
174
162
  VALUE obj, rbhash;
@@ -176,20 +164,29 @@ PyRuby_hash_hash_t(PyRubyObject *pyro)
176
164
 
177
165
  obj = PyRuby_get_ruby_object_and_set_pyerr((PyObject *)pyro);
178
166
  if (obj == Qundef)
179
- return -1;
167
+ return (void *)-1;
180
168
 
181
169
  rbhash = rb_hash(obj);
182
170
  #if SIZEOF_PY_HASH_T == SIZEOF_LONG
183
171
  /* In this case, we can assume sizeof(Py_hash_t) == sizeof(long) */
184
172
  h = NUM2SSIZET(rbhash);
185
- return h == -1 ? (Py_hash_t)pycall_hash_salt : h;
173
+ return (void *)(h == -1 ? (Py_hash_t)pycall_hash_salt : h);
186
174
  #else
187
175
  /* In this case, we can assume sizeof(long) == 4 and sizeof(Py_hash_t) == 8 */
188
176
  h = (pycall_hash_salt_32 << 32) | FIX2LONG(rbhash);
189
- return h == -1 ? ((pycall_hash_salt << 32) | pycall_hash_salt) : h;
177
+ return (void *)(h == -1 ? ((pycall_hash_salt << 32) | pycall_hash_salt) : h);
190
178
  #endif
191
179
  }
192
180
 
181
+ static Py_hash_t
182
+ PyRuby_hash_hash_t_with_gvl(PyRubyObject *pyro)
183
+ {
184
+ if (!ruby_thread_has_gvl_p()) {
185
+ return (Py_hash_t)CALL_WITH_GVL(PyRuby_hash_hash_t, pyro);
186
+ }
187
+ return (Py_hash_t)PyRuby_hash_hash_t(pyro);
188
+ }
189
+
193
190
  struct call_rb_funcallv_params {
194
191
  VALUE recv;
195
192
  ID meth;
@@ -221,15 +218,21 @@ rb_protect_funcallv(VALUE recv, ID meth, int argc, VALUE *argv, int *pstate)
221
218
  return res;
222
219
  }
223
220
 
221
+ struct PyRuby_call_params {
222
+ PyRubyObject *pyro;
223
+ PyObject *pyobj_args;
224
+ PyObject *pyobj_kwargs;
225
+ };
226
+
224
227
  static PyObject *
225
- PyRuby_call(PyRubyObject *pyro, PyObject *pyobj_args, PyObject *pyobj_kwargs)
228
+ PyRuby_call(struct PyRuby_call_params *params)
226
229
  {
227
230
  ID id_call;
228
231
  VALUE obj, args, kwargs, res;
229
232
  PyObject *pyobj_res;
230
233
  int state;
231
234
 
232
- obj = PyRuby_get_ruby_object_and_set_pyerr((PyObject *)pyro);
235
+ obj = PyRuby_get_ruby_object_and_set_pyerr((PyObject *)params->pyro);
233
236
  if (obj == Qundef)
234
237
  return NULL;
235
238
 
@@ -239,9 +242,9 @@ PyRuby_call(PyRubyObject *pyro, PyObject *pyobj_args, PyObject *pyobj_kwargs)
239
242
  return NULL;
240
243
  }
241
244
 
242
- args = pycall_pyobject_to_a(pyobj_args);
243
- if (pyobj_kwargs) {
244
- kwargs = pycall_pyobject_to_ruby(pyobj_kwargs);
245
+ args = pycall_pyobject_to_a(params->pyobj_args);
246
+ if (params->pyobj_kwargs) {
247
+ kwargs = pycall_pyobject_to_ruby(params->pyobj_kwargs);
245
248
  rb_ary_push(args, kwargs);
246
249
  }
247
250
 
@@ -255,18 +258,38 @@ PyRuby_call(PyRubyObject *pyro, PyObject *pyobj_args, PyObject *pyobj_kwargs)
255
258
  }
256
259
 
257
260
  static PyObject *
258
- PyRuby_getattro(PyRubyObject *pyro, PyObject *pyobj_name)
261
+ PyRuby_call_with_gvl(PyRubyObject *pyro, PyObject *pyobj_args, PyObject *pyobj_kwargs)
262
+ {
263
+ struct PyRuby_call_params params;
264
+ params.pyro = pyro;
265
+ params.pyobj_args = pyobj_args;
266
+ params.pyobj_kwargs = pyobj_kwargs;
267
+
268
+ if (!ruby_thread_has_gvl_p()) {
269
+ return CALL_WITH_GVL(PyRuby_call, &params);
270
+ }
271
+
272
+ return PyRuby_call(&params);
273
+ }
274
+
275
+ struct PyRuby_getattro_params {
276
+ PyRubyObject *pyro;
277
+ PyObject *pyobj_name;
278
+ };
279
+
280
+ static PyObject *
281
+ PyRuby_getattro(struct PyRuby_getattro_params *params)
259
282
  {
260
283
  VALUE obj, name, res;
261
284
  char const *name_cstr;
262
285
  ID name_id;
263
286
  PyObject *pyobj_res;
264
287
 
265
- obj = PyRuby_get_ruby_object_and_set_pyerr((PyObject *)pyro);
288
+ obj = PyRuby_get_ruby_object_and_set_pyerr((PyObject *)params->pyro);
266
289
  if (obj == Qundef)
267
290
  return NULL;
268
291
 
269
- name = pycall_pyobject_to_ruby(pyobj_name);
292
+ name = pycall_pyobject_to_ruby(params->pyobj_name);
270
293
  name_cstr = StringValueCStr(name);
271
294
  name_id = rb_intern(name_cstr);
272
295
 
@@ -299,21 +322,35 @@ PyRuby_getattro(PyRubyObject *pyro, PyObject *pyobj_name)
299
322
  else if (name_cstr[0] == '_' && name_cstr[1] == '_') {
300
323
  /* name.start_with? "__" */
301
324
  /* TODO: handle `__code__` and `func_code` */
302
- return Py_API(PyObject_GenericGetAttr)((PyObject *)pyro, pyobj_name);
325
+ return Py_API(PyObject_GenericGetAttr)((PyObject *)params->pyro, params->pyobj_name);
303
326
  }
304
327
  else {
305
328
  /* TODO: handle `__code__` and `func_code` */
306
329
  if (rb_respond_to(obj, name_id)) {
307
330
  VALUE method = rb_obj_method(obj, name);
308
- return PyRuby_New(method);
331
+ return PyRuby_New_impl(method);
309
332
  }
310
- return Py_API(PyObject_GenericGetAttr)((PyObject *)pyro, pyobj_name);
333
+ return Py_API(PyObject_GenericGetAttr)((PyObject *)params->pyro, params->pyobj_name);
311
334
  }
312
335
 
313
336
  pyobj_res = pycall_pyobject_from_ruby(res);
314
337
  return pyobj_res;
315
338
  }
316
339
 
340
+ static PyObject *
341
+ PyRuby_getattro_with_gvl(PyRubyObject *pyro, PyObject *pyobj_name)
342
+ {
343
+ struct PyRuby_getattro_params params;
344
+ params.pyro = pyro;
345
+ params.pyobj_name = pyobj_name;
346
+
347
+ if (!ruby_thread_has_gvl_p()) {
348
+ return CALL_WITH_GVL(PyRuby_getattro, &params);
349
+ }
350
+
351
+ return PyRuby_getattro(&params);
352
+ }
353
+
317
354
  /* ==== PyCall::PyRubyPtr ==== */
318
355
 
319
356
  VALUE cPyRubyPtr;
@@ -411,9 +448,9 @@ pycall_init_ruby_wrapper(void)
411
448
  PyRuby_Type.tp_flags |= pycall_default_tp_flags();
412
449
  PyRuby_Type.tp_new = Py_API(PyType_GenericNew);
413
450
  if (pycall_python_long_hash)
414
- PyRuby_Type.tp_hash._long = (hashfunc_long)PyRuby_hash_long;
451
+ PyRuby_Type.tp_hash._long = (hashfunc_long)PyRuby_hash_long_with_gvl;
415
452
  else
416
- PyRuby_Type.tp_hash._hash_t = (hashfunc_hash_t)PyRuby_hash_hash_t;
453
+ PyRuby_Type.tp_hash._hash_t = (hashfunc_hash_t)PyRuby_hash_hash_t_with_gvl;
417
454
 
418
455
  if (Py_API(PyType_Ready)(&PyRuby_Type) < 0) {
419
456
  pycall_pyerror_fetch_and_raise("PyType_Ready in pycall_init_ruby_wrapper");
@@ -430,3 +467,57 @@ pycall_init_ruby_wrapper(void)
430
467
 
431
468
  rb_define_module_function(mPyCall, "wrap_ruby_object", pycall_m_wrap_ruby_object, 1);
432
469
  }
470
+
471
+ /* --- File internal utilities --- */
472
+
473
+ static VALUE
474
+ funcall_id2ref(VALUE object_id)
475
+ {
476
+ VALUE rb_mObjSpace;
477
+ object_id = rb_check_to_integer(object_id, "to_int");
478
+ rb_mObjSpace = rb_const_get(rb_cObject, rb_intern("ObjectSpace"));
479
+ return rb_funcall(rb_mObjSpace, rb_intern("_id2ref"), 1, object_id);
480
+ }
481
+
482
+ static VALUE
483
+ protect_id2ref(VALUE object_id)
484
+ {
485
+ VALUE obj;
486
+ int state;
487
+
488
+ obj = rb_protect((VALUE (*)(VALUE))funcall_id2ref, object_id, &state);
489
+ if (state)
490
+ return Qundef;
491
+
492
+ return obj;
493
+ }
494
+
495
+ static VALUE
496
+ protect_id2ref_and_set_pyerr(VALUE object_id)
497
+ {
498
+ VALUE obj = protect_id2ref(object_id);
499
+ if (obj != Qundef)
500
+ return obj;
501
+
502
+ obj = rb_errinfo();
503
+ if (RTEST(rb_obj_is_kind_of(obj, rb_eRangeError))) {
504
+ Py_API(PyErr_SetString)(Py_API(PyExc_RuntimeError), "[BUG] referenced object was garbage-collected");
505
+ }
506
+ else {
507
+ VALUE emesg = rb_check_funcall(obj, rb_intern("message"), 0, 0);
508
+ Py_API(PyErr_Format)(Py_API(PyExc_RuntimeError),
509
+ "[BUG] Unable to obtain ruby object from ID: %s (%s)",
510
+ StringValueCStr(emesg), rb_class2name(CLASS_OF(obj)));
511
+ }
512
+ return Qundef;
513
+ }
514
+
515
+ static VALUE
516
+ PyRuby_get_ruby_object_and_set_pyerr(PyObject *pyobj)
517
+ {
518
+ VALUE obj_id;
519
+ if (!PyRuby_Check(pyobj))
520
+ return Qundef;
521
+ obj_id = rb_obj_id(PyRuby_get_ruby_object(pyobj));
522
+ return protect_id2ref_and_set_pyerr(obj_id);
523
+ }
@@ -32,7 +32,7 @@ module PyCall
32
32
  #
33
33
  # @return a Ruby object converted from `pyptr`.
34
34
  def self.to_ruby(pyptr)
35
- return nil if pyptr.null? || PyCall.none?(pyptr)
35
+ return nil if pyptr.null? || pyptr.none?
36
36
 
37
37
  case
38
38
  when PyCall::Types.pyisinstance(pyptr, LibPython.PyType_Type)
@@ -1,3 +1,3 @@
1
1
  module PyCall
2
- VERSION = "1.0.3"
2
+ VERSION = "1.1.0.rc1"
3
3
  end
@@ -17,6 +17,7 @@ Gem::Specification.new do |spec|
17
17
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
18
  case f
19
19
  when %r{^Guardfile}, # NOTE: Skip symlink for Windows
20
+ %r{^ext/pycall/spec_helper/},
20
21
  %r{^(test|spec|features)/}
21
22
  true
22
23
  else
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pycall
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.1.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kenta Murata
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-09-22 00:00:00.000000000 Z
11
+ date: 2018-03-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -167,6 +167,7 @@ files:
167
167
  - ext/pycall/gc.c
168
168
  - ext/pycall/libpython.c
169
169
  - ext/pycall/pycall.c
170
+ - ext/pycall/pycall.h
170
171
  - ext/pycall/pycall_internal.h
171
172
  - ext/pycall/range.c
172
173
  - ext/pycall/ruby_wrapper.c
@@ -213,12 +214,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
213
214
  version: '0'
214
215
  required_rubygems_version: !ruby/object:Gem::Requirement
215
216
  requirements:
216
- - - ">="
217
+ - - ">"
217
218
  - !ruby/object:Gem::Version
218
- version: '0'
219
+ version: 1.3.1
219
220
  requirements: []
220
221
  rubyforge_project:
221
- rubygems_version: 2.6.13
222
+ rubygems_version: 2.7.4
222
223
  signing_key:
223
224
  specification_version: 4
224
225
  summary: pycall