pycall 1.0.3 → 1.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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