pycall 1.0.1-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +41 -0
  5. data/CHANGES.md +39 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +91 -0
  9. data/Rakefile +29 -0
  10. data/appveyor.yml +138 -0
  11. data/bin/console +10 -0
  12. data/bin/guard +17 -0
  13. data/bin/rspec +17 -0
  14. data/bin/runner +6 -0
  15. data/bin/setup +8 -0
  16. data/config/Guardfile +30 -0
  17. data/docker/Dockerfile +191 -0
  18. data/docker/Gemfile +12 -0
  19. data/docker/README.md +22 -0
  20. data/examples/classifier_comparison.rb +135 -0
  21. data/examples/datascience_rb_20170519.ipynb +4836 -0
  22. data/examples/hist.rb +32 -0
  23. data/examples/notebooks/classifier_comparison.ipynb +226 -0
  24. data/examples/notebooks/forest_importances.ipynb +238 -0
  25. data/examples/notebooks/iruby_integration.ipynb +183 -0
  26. data/examples/notebooks/lorenz_attractor.ipynb +214 -0
  27. data/examples/notebooks/polar_axes.ipynb +209 -0
  28. data/examples/notebooks/sum_benchmarking.ipynb +374 -0
  29. data/examples/notebooks/xkcd_style.ipynb +149 -0
  30. data/examples/plot_forest_importances_faces.rb +46 -0
  31. data/examples/sum_benchmarking.rb +49 -0
  32. data/ext/pycall/extconf.rb +3 -0
  33. data/ext/pycall/gc.c +74 -0
  34. data/ext/pycall/libpython.c +217 -0
  35. data/ext/pycall/pycall.c +2184 -0
  36. data/ext/pycall/pycall_internal.h +700 -0
  37. data/ext/pycall/range.c +69 -0
  38. data/ext/pycall/ruby_wrapper.c +432 -0
  39. data/lib/2.1/pycall.so +0 -0
  40. data/lib/2.2/pycall.so +0 -0
  41. data/lib/2.3/pycall.so +0 -0
  42. data/lib/2.4/pycall.so +0 -0
  43. data/lib/pycall/conversion.rb +173 -0
  44. data/lib/pycall/dict.rb +48 -0
  45. data/lib/pycall/error.rb +10 -0
  46. data/lib/pycall/gc_guard.rb +84 -0
  47. data/lib/pycall/import.rb +120 -0
  48. data/lib/pycall/init.rb +55 -0
  49. data/lib/pycall/iruby_helper.rb +40 -0
  50. data/lib/pycall/libpython/finder.rb +170 -0
  51. data/lib/pycall/libpython/pyobject_struct.rb +30 -0
  52. data/lib/pycall/libpython/pytypeobject_struct.rb +273 -0
  53. data/lib/pycall/libpython.rb +12 -0
  54. data/lib/pycall/list.rb +45 -0
  55. data/lib/pycall/pretty_print.rb +9 -0
  56. data/lib/pycall/pyerror.rb +30 -0
  57. data/lib/pycall/pyobject_wrapper.rb +212 -0
  58. data/lib/pycall/python/PyCall/__init__.py +1 -0
  59. data/lib/pycall/python/PyCall/six.py +23 -0
  60. data/lib/pycall/python/investigator.py +7 -0
  61. data/lib/pycall/pytypeobject_wrapper.rb +90 -0
  62. data/lib/pycall/set.rb +19 -0
  63. data/lib/pycall/slice.rb +8 -0
  64. data/lib/pycall/tuple.rb +46 -0
  65. data/lib/pycall/version.rb +3 -0
  66. data/lib/pycall/wrapper_object_cache.rb +61 -0
  67. data/lib/pycall.rb +91 -0
  68. data/pycall.gemspec +40 -0
  69. data/tasks/docker.rake +21 -0
  70. data/tasks/pycall.rake +7 -0
  71. metadata +228 -0
@@ -0,0 +1,69 @@
1
+ #include "pycall_internal.h"
2
+
3
+ struct enumerator_head {
4
+ VALUE obj;
5
+ ID meth;
6
+ VALUE args;
7
+ };
8
+
9
+ int
10
+ pycall_obj_is_step_range(VALUE obj)
11
+ {
12
+ struct enumerator_head *eh;
13
+
14
+ if (!RB_TYPE_P(obj, T_DATA)) {
15
+ return 0;
16
+ }
17
+
18
+ if (!rb_obj_is_kind_of(obj, rb_cEnumerator)) {
19
+ return 0;
20
+ }
21
+
22
+ eh = (struct enumerator_head *)DATA_PTR(obj);
23
+
24
+ if (!rb_obj_is_kind_of(eh->obj, rb_cRange)) {
25
+ return 0;
26
+ }
27
+ if (eh->meth == rb_intern("step")) {
28
+ if (!RB_TYPE_P(eh->args, T_ARRAY)) {
29
+ return 0;
30
+ }
31
+ return (RARRAY_LEN(eh->args) == 1);
32
+ }
33
+
34
+ return 0;
35
+ }
36
+
37
+ int
38
+ pycall_extract_range(VALUE obj, VALUE *pbegin, VALUE *pend, int *pexclude_end, VALUE *pstep)
39
+ {
40
+ ID id_begin, id_end, id_exclude_end;
41
+ VALUE begin, end, exclude_end, step = Qnil;
42
+
43
+ CONST_ID(id_begin, "begin");
44
+ CONST_ID(id_end, "end");
45
+ CONST_ID(id_exclude_end, "exclude_end?");
46
+
47
+ if (rb_obj_is_kind_of(obj, rb_cRange)) {
48
+ begin = rb_funcallv(obj, id_begin, 0, 0);
49
+ end = rb_funcallv(obj, id_end, 0, 0);
50
+ exclude_end = rb_funcallv(obj, id_exclude_end, 0, 0);
51
+ }
52
+ else if (pycall_obj_is_step_range(obj)) {
53
+ struct enumerator_head *eh = (struct enumerator_head *)DATA_PTR(obj);
54
+ begin = rb_funcallv(eh->obj, id_begin, 0, 0);
55
+ end = rb_funcallv(eh->obj, id_end, 0, 0);
56
+ exclude_end = rb_funcallv(eh->obj, id_exclude_end, 0, 0);
57
+ step = RARRAY_AREF(eh->args, 0);
58
+ }
59
+ else {
60
+ return 0;
61
+ }
62
+
63
+ if (pbegin) *pbegin = begin;
64
+ if (pend) *pend = end;
65
+ if (pexclude_end) *pexclude_end = RTEST(exclude_end);
66
+ if (pstep) *pstep = step;
67
+
68
+ return 1;
69
+ }
@@ -0,0 +1,432 @@
1
+ #include "pycall_internal.h"
2
+
3
+ static PyMemberDef PyRuby_members[] = {
4
+ {"ruby_object_ptr", Py_T_PYSSIZET, offsetof(PyRubyObject, ruby_object), Py_READONLY},
5
+ {NULL} /* sentinel */
6
+ };
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 *);
12
+
13
+ PyTypeObject PyRuby_Type = {
14
+ PyVarObject_HEAD_INIT(NULL, 0)
15
+ "PyCall.ruby_object", /* tp_name */
16
+ sizeof(PyRubyObject), /* tp_basicsize */
17
+ 0, /* tp_itemsize */
18
+ (destructor)PyRuby_dealloc, /* tp_dealloc */
19
+ 0, /* tp_print */
20
+ 0, /* tp_getattr */
21
+ 0, /* tp_setattr */
22
+ 0, /* tp_reserved */
23
+ (reprfunc)PyRuby_repr, /* tp_repr */
24
+ 0, /* tp_as_number */
25
+ 0, /* tp_as_sequence */
26
+ 0, /* tp_as_mapping */
27
+ {0}, /* tp_hash */
28
+ (ternaryfunc)PyRuby_call, /* tp_call */
29
+ 0, /* tp_str */
30
+ (getattrofunc)PyRuby_getattro, /* tp_getattro */
31
+ 0, /* tp_setattro */
32
+ 0, /* tp_as_buffer */
33
+ Py_TPFLAGS_BASETYPE, /* tp_flags */
34
+ "ruby object wrapper", /* tp_doc */
35
+ 0, /*tp_traverse*/
36
+ 0, /*tp_clear*/
37
+ 0, /*tp_richcompare*/
38
+ 0, /*tp_weaklistoffset*/
39
+ 0, /*tp_iter*/
40
+ 0, /*tp_iternext*/
41
+ 0, /*tp_methods*/
42
+ PyRuby_members, /*tp_members*/
43
+ };
44
+
45
+ PyObject *
46
+ PyRuby_New(VALUE ruby_object)
47
+ {
48
+ PyRubyObject *op;
49
+
50
+ op = (PyRubyObject *)Py_API(_PyObject_New)(&PyRuby_Type);
51
+ op->ruby_object = ruby_object;
52
+ pycall_gcguard_register_pyrubyobj((PyObject *)op);
53
+ return (PyObject *)op;
54
+ }
55
+
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)
80
+ {
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");
88
+ }
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
+
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);
106
+ }
107
+
108
+ static void
109
+ PyRuby_dealloc(PyRubyObject *pyro)
110
+ {
111
+ VALUE obj;
112
+
113
+ obj = PyRuby_get_ruby_object((PyObject *)pyro);
114
+
115
+ #ifdef PYCALL_DEBUG_DUMP_REFCNT
116
+ fprintf(stderr, "PyRuby_dealloc(%p), ruby_object=%"PRI_LL_PREFIX"d\n", pyro, NUM2LL(rb_obj_id(obj)));
117
+ #endif /* PYCALL_DEBUG_DUMP_REFCNT */
118
+
119
+ if (obj == Qundef)
120
+ return;
121
+
122
+ pycall_gcguard_unregister_pyrubyobj((PyObject *)pyro);
123
+ }
124
+
125
+ static PyObject *
126
+ PyRuby_repr(PyRubyObject *pyro)
127
+ {
128
+ VALUE obj, str;
129
+ PyObject *res;
130
+
131
+ obj = PyRuby_get_ruby_object_and_set_pyerr((PyObject *)pyro);
132
+ if (obj == Qundef)
133
+ return NULL;
134
+
135
+ str = rb_inspect(obj);
136
+ res = pycall_pystring_from_format("<PyCall.ruby_object %s>", StringValueCStr(str));
137
+ return res;
138
+ }
139
+
140
+ #if SIZEOF_SSIZE_T < 8
141
+ /* int64to32hash from src/support/hashing.c in Julia */
142
+ static inline uint32_t
143
+ int64to32hash(uint64_t key)
144
+ {
145
+ key = (~key) + (key << 18); // key = (key << 18) - key - 1;
146
+ key = key ^ (key >> 31);
147
+ key = key * 21; // key = (key + (key << 2)) + (key << 4);
148
+ key = key ^ (key >> 11);
149
+ key = key + (key << 6);
150
+ key = key ^ (key >> 22);
151
+ return (uint32_t)key;
152
+ }
153
+ #endif
154
+
155
+ static long
156
+ PyRuby_hash_long(PyRubyObject *pyro)
157
+ {
158
+ VALUE obj, rbhash;
159
+ long h;
160
+
161
+ obj = PyRuby_get_ruby_object_and_set_pyerr((PyObject *)pyro);
162
+ if (obj == Qundef)
163
+ return -1;
164
+
165
+ rbhash = rb_hash(obj);
166
+ h = FIX2LONG(rbhash); /* Ruby's hash value is a Fixnum */
167
+
168
+ return h == -1 ? pycall_hash_salt : h;
169
+ }
170
+
171
+ static Py_hash_t
172
+ PyRuby_hash_hash_t(PyRubyObject *pyro)
173
+ {
174
+ VALUE obj, rbhash;
175
+ Py_hash_t h;
176
+
177
+ obj = PyRuby_get_ruby_object_and_set_pyerr((PyObject *)pyro);
178
+ if (obj == Qundef)
179
+ return -1;
180
+
181
+ rbhash = rb_hash(obj);
182
+ #if SIZEOF_PY_HASH_T == SIZEOF_LONG
183
+ /* In this case, we can assume sizeof(Py_hash_t) == sizeof(long) */
184
+ h = NUM2SSIZET(rbhash);
185
+ return h == -1 ? (Py_hash_t)pycall_hash_salt : h;
186
+ #else
187
+ /* In this case, we can assume sizeof(long) == 4 and sizeof(Py_hash_t) == 8 */
188
+ h = (pycall_hash_salt_32 << 32) | FIX2LONG(rbhash);
189
+ return h == -1 ? ((pycall_hash_salt << 32) | pycall_hash_salt) : h;
190
+ #endif
191
+ }
192
+
193
+ struct call_rb_funcallv_params {
194
+ VALUE recv;
195
+ ID meth;
196
+ int argc;
197
+ VALUE *argv;
198
+ };
199
+
200
+ static VALUE
201
+ call_rb_funcallv(struct call_rb_funcallv_params *params)
202
+ {
203
+ return rb_funcallv(params->recv, params->meth, params->argc, params->argv);
204
+ }
205
+
206
+ static VALUE
207
+ rb_protect_funcallv(VALUE recv, ID meth, int argc, VALUE *argv, int *pstate)
208
+ {
209
+ struct call_rb_funcallv_params params;
210
+ VALUE res;
211
+ int state;
212
+
213
+ params.recv = recv;
214
+ params.meth = meth;
215
+ params.argc = argc;
216
+ params.argv = argv;
217
+
218
+ res = rb_protect((VALUE (*)(VALUE))call_rb_funcallv, (VALUE)&params, &state);
219
+ if (pstate) *pstate = state;
220
+ if (state) return Qnil;
221
+ return res;
222
+ }
223
+
224
+ static PyObject *
225
+ PyRuby_call(PyRubyObject *pyro, PyObject *pyobj_args, PyObject *pyobj_kwargs)
226
+ {
227
+ ID id_call;
228
+ VALUE obj, args, kwargs, res;
229
+ PyObject *pyobj_res;
230
+ int state;
231
+
232
+ obj = PyRuby_get_ruby_object_and_set_pyerr((PyObject *)pyro);
233
+ if (obj == Qundef)
234
+ return NULL;
235
+
236
+ id_call = rb_intern("call");
237
+ if (!rb_respond_to(obj, id_call)) {
238
+ Py_API(PyErr_SetString)(Py_API(PyExc_TypeError), "non-callable ruby object");
239
+ return NULL;
240
+ }
241
+
242
+ args = pycall_pyobject_to_a(pyobj_args);
243
+ if (pyobj_kwargs) {
244
+ kwargs = pycall_pyobject_to_ruby(pyobj_kwargs);
245
+ rb_ary_push(args, kwargs);
246
+ }
247
+
248
+ res = rb_protect_funcallv(obj, id_call, (int)RARRAY_LEN(args), RARRAY_PTR(args), &state);
249
+ if (state) {
250
+ /* TODO: pyerr set */
251
+ }
252
+
253
+ pyobj_res = pycall_pyobject_from_ruby(res);
254
+ return pyobj_res;
255
+ }
256
+
257
+ static PyObject *
258
+ PyRuby_getattro(PyRubyObject *pyro, PyObject *pyobj_name)
259
+ {
260
+ VALUE obj, name, res;
261
+ char const *name_cstr;
262
+ ID name_id;
263
+ PyObject *pyobj_res;
264
+
265
+ obj = PyRuby_get_ruby_object_and_set_pyerr((PyObject *)pyro);
266
+ if (obj == Qundef)
267
+ return NULL;
268
+
269
+ name = pycall_pyobject_to_ruby(pyobj_name);
270
+ name_cstr = StringValueCStr(name);
271
+ name_id = rb_intern(name_cstr);
272
+
273
+ /* TODO: should handle exception */
274
+ if (strncmp(name_cstr, "__name__", 8) == 0 ||
275
+ strncmp(name_cstr, "func_name", 9) == 0) {
276
+ if (rb_respond_to(obj, rb_intern("name"))) {
277
+ res = rb_funcall(obj, rb_intern("name"), 0);
278
+ }
279
+ else {
280
+ res = rb_any_to_s(obj);
281
+ }
282
+ }
283
+ else if (strncmp(name_cstr, "__doc__", 7) == 0 ||
284
+ strncmp(name_cstr, "func_doc", 8) == 0) {
285
+ /* TODO: support docstring */
286
+ res = Qnil;
287
+ }
288
+ else if (strncmp(name_cstr, "__module__", 10) == 0) {
289
+ res = Qnil;
290
+ }
291
+ else if (strncmp(name_cstr, "__defaults__", 12) == 0 ||
292
+ strncmp(name_cstr, "func_defaults", 13) == 0) {
293
+ res = Qnil;
294
+ }
295
+ else if (strncmp(name_cstr, "__closure__", 11) == 0 ||
296
+ strncmp(name_cstr, "func_closure", 12) == 0) {
297
+ res = Qnil;
298
+ }
299
+ else if (name_cstr[0] == '_' && name_cstr[1] == '_') {
300
+ /* name.start_with? "__" */
301
+ /* TODO: handle `__code__` and `func_code` */
302
+ return Py_API(PyObject_GenericGetAttr)((PyObject *)pyro, pyobj_name);
303
+ }
304
+ else {
305
+ /* TODO: handle `__code__` and `func_code` */
306
+ if (rb_respond_to(obj, name_id)) {
307
+ VALUE method = rb_obj_method(obj, name);
308
+ return PyRuby_New(method);
309
+ }
310
+ return Py_API(PyObject_GenericGetAttr)((PyObject *)pyro, pyobj_name);
311
+ }
312
+
313
+ pyobj_res = pycall_pyobject_from_ruby(res);
314
+ return pyobj_res;
315
+ }
316
+
317
+ /* ==== PyCall::PyRubyPtr ==== */
318
+
319
+ VALUE cPyRubyPtr;
320
+
321
+ const rb_data_type_t pycall_pyrubyptr_data_type = {
322
+ "PyCall::PyRubyPtr",
323
+ { 0, pycall_pyptr_free, pycall_pyptr_memsize, },
324
+ #ifdef RUBY_TYPED_FREE_IMMEDIATELY
325
+ &pycall_pyptr_data_type, 0, RUBY_TYPED_FREE_IMMEDIATELY
326
+ #endif
327
+ };
328
+
329
+ static inline int
330
+ is_pycall_pyrubyptr(VALUE obj)
331
+ {
332
+ return rb_typeddata_is_kind_of(obj, &pycall_pyrubyptr_data_type);
333
+ }
334
+
335
+ static inline PyRubyObject*
336
+ get_pyrubyobj_ptr(VALUE obj)
337
+ {
338
+ PyRubyObject *pyruby;
339
+ TypedData_Get_Struct(obj, PyRubyObject, &pycall_pyrubyptr_data_type, pyruby);
340
+ return pyruby;
341
+ }
342
+
343
+ static inline PyRubyObject*
344
+ try_get_pyrubyobj_ptr(VALUE obj)
345
+ {
346
+ if (is_pycall_pyrubyptr(obj)) return NULL;
347
+ return (PyRubyObject*)DATA_PTR(obj);
348
+ }
349
+
350
+ static inline PyRubyObject *
351
+ check_get_pyrubyobj_ptr(VALUE obj)
352
+ {
353
+ PyRubyObject *pyrubyobj;
354
+ if (!is_pycall_pyrubyptr(obj))
355
+ rb_raise(rb_eTypeError, "unexpected type %s (expected PyCall::PyRubyPtr)", rb_class2name(CLASS_OF(obj)));
356
+
357
+ pyrubyobj = get_pyrubyobj_ptr(obj);
358
+ if (!PyRuby_Check(pyrubyobj))
359
+ rb_raise(rb_eTypeError, "unexpected Python type %s (expected ruby object)", Py_TYPE(pyrubyobj)->tp_name);
360
+
361
+ return pyrubyobj;
362
+ }
363
+
364
+ static VALUE
365
+ pycall_pyruby_allocate(VALUE klass)
366
+ {
367
+ return TypedData_Wrap_Struct(klass, &pycall_pyrubyptr_data_type, NULL);
368
+ }
369
+
370
+ VALUE
371
+ pycall_pyrubyptr_new(PyObject *pyrubyobj)
372
+ {
373
+ VALUE obj;
374
+
375
+ if (!PyRuby_Check(pyrubyobj)) {
376
+ rb_raise(rb_eTypeError, "wrong type of python object %s (expect PyRubyObject)", Py_TYPE(pyrubyobj)->tp_name);
377
+ }
378
+
379
+ obj = pycall_pyruby_allocate(cPyRubyPtr);
380
+ DATA_PTR(obj) = pyrubyobj;
381
+ return obj;
382
+ }
383
+
384
+ static VALUE
385
+ pycall_pyruby_get_ruby_object_id(VALUE obj)
386
+ {
387
+ PyRubyObject *pyrubyobj;
388
+
389
+ pyrubyobj = check_get_pyrubyobj_ptr(obj);
390
+ return rb_obj_id(pyrubyobj->ruby_object);
391
+ }
392
+
393
+ VALUE
394
+ pycall_wrap_ruby_object(VALUE obj)
395
+ {
396
+ PyObject *pyobj;
397
+ pyobj = PyRuby_New(obj);
398
+ return pycall_pyrubyptr_new(pyobj);
399
+ }
400
+
401
+ static VALUE
402
+ pycall_m_wrap_ruby_object(VALUE mod, VALUE obj)
403
+ {
404
+ return pycall_wrap_ruby_object(obj);
405
+ }
406
+
407
+ void
408
+ pycall_init_ruby_wrapper(void)
409
+ {
410
+ PyRuby_Type.ob_base.ob_type = Py_API(PyType_Type);
411
+ PyRuby_Type.tp_flags |= pycall_default_tp_flags();
412
+ PyRuby_Type.tp_new = Py_API(PyType_GenericNew);
413
+ if (pycall_python_long_hash)
414
+ PyRuby_Type.tp_hash._long = (hashfunc_long)PyRuby_hash_long;
415
+ else
416
+ PyRuby_Type.tp_hash._hash_t = (hashfunc_hash_t)PyRuby_hash_hash_t;
417
+
418
+ if (Py_API(PyType_Ready)(&PyRuby_Type) < 0) {
419
+ pycall_pyerror_fetch_and_raise("PyType_Ready in pycall_init_ruby_wrapper");
420
+ }
421
+ Py_API(Py_IncRef)((PyObject *)&PyRuby_Type);
422
+
423
+ /* TODO */
424
+
425
+ /* PyCall::PyRubyPtr */
426
+
427
+ cPyRubyPtr = rb_define_class_under(mPyCall, "PyRubyPtr", cPyPtr);
428
+ rb_define_alloc_func(cPyRubyPtr, pycall_pyruby_allocate);
429
+ rb_define_method(cPyRubyPtr, "__ruby_object_id__", pycall_pyruby_get_ruby_object_id, 0);
430
+
431
+ rb_define_module_function(mPyCall, "wrap_ruby_object", pycall_m_wrap_ruby_object, 1);
432
+ }
data/lib/2.1/pycall.so ADDED
Binary file
data/lib/2.2/pycall.so ADDED
Binary file
data/lib/2.3/pycall.so ADDED
Binary file
data/lib/2.4/pycall.so ADDED
Binary file
@@ -0,0 +1,173 @@
1
+ module PyCall
2
+ module Conversions
3
+ @python_type_map = []
4
+
5
+ class TypePair < Struct.new(:pytype, :rbtype)
6
+ def to_a
7
+ [pytype, rbtype]
8
+ end
9
+ end
10
+
11
+ def self.each_type_pair
12
+ i, n = 1, @python_type_map.length
13
+ while i <= n
14
+ yield @python_type_map[n - i]
15
+ i += 1
16
+ end
17
+ self
18
+ end
19
+
20
+ def self.python_type_mapping(pytype, rbtype)
21
+ each_type_pair do |type_pair|
22
+ next unless pytype == type_pair.pytype
23
+ type_pair.rbtype = rbtype
24
+ return
25
+ end
26
+ @python_type_map << TypePair.new(pytype, rbtype)
27
+ end
28
+
29
+ # Convert a PyCall::PyObjectStruct object to a Ruby object
30
+ #
31
+ # @param [PyCall::PyObjectStruct] pyptr a PyObjectStruct object.
32
+ #
33
+ # @return a Ruby object converted from `pyptr`.
34
+ def self.to_ruby(pyptr)
35
+ return nil if pyptr.null? || PyCall.none?(pyptr)
36
+
37
+ case
38
+ when PyCall::Types.pyisinstance(pyptr, LibPython.PyType_Type)
39
+ return TypeObject.new(pyptr)
40
+
41
+ when PyCall::Types.pyisinstance(pyptr, LibPython.PyBool_Type)
42
+ return Conversions.convert_to_boolean(pyptr)
43
+
44
+ when PyCall::Types.pyisinstance(pyptr, LibPython.PyInt_Type)
45
+ return Conversions.convert_to_integer(pyptr)
46
+
47
+ when PyCall::Types.pyisinstance(pyptr, LibPython.PyLong_Type)
48
+ # TODO: should make Bignum
49
+
50
+ when PyCall::Types.pyisinstance(pyptr, LibPython.PyFloat_Type)
51
+ return Conversions.convert_to_float(pyptr)
52
+
53
+ when PyCall::Types.pyisinstance(pyptr, LibPython.PyComplex_Type)
54
+ return Conversions.convert_to_complex(pyptr)
55
+
56
+ when PyCall::Types.pyisinstance(pyptr, LibPython.PyString_Type)
57
+ return Conversions.convert_to_string(pyptr)
58
+
59
+ when PyCall::Types.pyisinstance(pyptr, LibPython.PyUnicode_Type)
60
+ py_str_ptr = LibPython.PyUnicode_AsUTF8String(pyptr)
61
+ return Conversions.convert_to_string(py_str_ptr).force_encoding(Encoding::UTF_8)
62
+
63
+ when PyCall::Types.pyisinstance(pyptr, LibPython.PyList_Type)
64
+ return PyCall::List.new(pyptr)
65
+
66
+ when PyCall::Types.pyisinstance(pyptr, LibPython.PyTuple_Type)
67
+ return Conversions.convert_to_tuple(pyptr)
68
+
69
+ when PyCall::Types.pyisinstance(pyptr, LibPython.PyDict_Type)
70
+ return PyCall::Dict.new(pyptr)
71
+
72
+ when PyCall::Types.pyisinstance(pyptr, LibPython.PySet_Type)
73
+ return PyCall::Set.new(pyptr)
74
+ end
75
+
76
+ pyobj = PyObject.new(pyptr)
77
+ each_type_pair do |tp|
78
+ pytype, rbtype = tp.to_a
79
+ next unless pyobj.kind_of?(pytype)
80
+ case
81
+ when rbtype.kind_of?(Proc)
82
+ return rbtype.(pyobj)
83
+ when rbtype.respond_to?(:from_python)
84
+ return rbtype.from_python(pyobj)
85
+ else
86
+ return rbtype.new(pyobj)
87
+ end
88
+ end
89
+ pyobj
90
+ end
91
+
92
+ def self.from_ruby(obj)
93
+ case obj
94
+ when LibPython::PyObjectStruct
95
+ obj
96
+ when PyObject, PyObjectWrapper
97
+ obj.__pyobj__
98
+ when TrueClass, FalseClass
99
+ LibPython.PyBool_FromLong(obj ? 1 : 0)
100
+ when Integer
101
+ LibPython.PyInt_FromSsize_t(obj)
102
+ when Float
103
+ LibPython.PyFloat_FromDouble(obj)
104
+ when String
105
+ if obj.encoding != Encoding::BINARY && (PyCall.unicode_literals? || !obj.ascii_only?)
106
+ obj = obj.encode(Encoding::UTF_8) if obj.encoding != Encoding::UTF_8
107
+ return LibPython.PyUnicode_DecodeUTF8(obj, obj.bytesize, nil)
108
+ end
109
+ LibPython.PyString_FromStringAndSize(obj, obj.bytesize)
110
+ when Symbol
111
+ from_ruby(obj.to_s)
112
+ when Array
113
+ PyCall::List.new(obj).__pyobj__
114
+ when Hash
115
+ PyCall::Dict.new(obj).__pyobj__
116
+ when Proc
117
+ PyCall.wrap_ruby_callable(obj)
118
+ else
119
+ PyCall.None
120
+ end
121
+ end
122
+
123
+ def self.convert_to_boolean(py_obj)
124
+ 0 != LibPython.PyInt_AsSsize_t(py_obj)
125
+ end
126
+
127
+ def self.convert_to_integer(py_obj)
128
+ LibPython.PyInt_AsSsize_t(py_obj)
129
+ end
130
+
131
+ def self.convert_to_float(py_obj)
132
+ LibPython.PyFloat_AsDouble(py_obj)
133
+ end
134
+
135
+ def self.convert_to_complex(py_obj)
136
+ real = LibPython.PyComplex_RealAsDouble(py_obj)
137
+ imag = LibPython.PyComplex_ImagAsDouble(py_obj)
138
+ Complex(real, imag)
139
+ end
140
+
141
+ def self.convert_to_string(py_obj)
142
+ FFI::MemoryPointer.new(:string) do |str_ptr|
143
+ FFI::MemoryPointer.new(:int) do |len_ptr|
144
+ res = LibPython.PyString_AsStringAndSize(py_obj, str_ptr, len_ptr)
145
+ return nil if res == -1 # FIXME: error
146
+
147
+ len = len_ptr.get(:int, 0)
148
+ return str_ptr.get_pointer(0).read_string(len)
149
+ end
150
+ end
151
+ end
152
+
153
+ def self.convert_to_array(py_obj, force_list: true, array_class: Array)
154
+ case
155
+ when force_list || py_obj.kind_of?(LibPython.PyList_Type)
156
+ len = LibPython.PySequence_Size(py_obj)
157
+ array_class.new(len) do |i|
158
+ LibPython.PySequence_GetItem(py_obj, i).to_ruby
159
+ end
160
+ end
161
+ end
162
+
163
+ def self.convert_to_tuple(py_obj)
164
+ PyCall::Tuple.new(py_obj)
165
+ end
166
+ end
167
+
168
+ class LibPython::PyObjectStruct
169
+ def to_ruby
170
+ Conversions.to_ruby(self)
171
+ end
172
+ end
173
+ end