pycall 1.1.0.rc1 → 1.2.0.beta1

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
2
  SHA256:
3
- metadata.gz: 78588bb0a23c7fa9b5c522113598027e8c98aea9fb9b72e96b64a1db4e9cff41
4
- data.tar.gz: '08d504ae9fdaa7ea8f532ca10848df3119264d956b259b40b3dfb356f5ea5834'
3
+ metadata.gz: 4ee819add9bcf3e4c3a3fb4f4fdc7b94abe6ccf1a969a8aabe9bd7edf9aa6701
4
+ data.tar.gz: '03839cbba19832eca0cf90ee7e85cf4cd953f2f5bba2dd12bcc9005097393482'
5
5
  SHA512:
6
- metadata.gz: d3d9c586c37ec66a8a6a7bd1093b5fd0848aad2602ab6784fe73a50c08a05402289601d1e87d7bfe13f51b54f2f8002790e7c95ac4899de339ad8af7dedccae3
7
- data.tar.gz: b3c3d61680274ff6e612dabd28818522637f479225f43a9a8025d2a036127b3fddc62d2342456673cf96cdc280ce8e388fa503e904b5d5860b44c0b0fa8c3a29
6
+ metadata.gz: d22a8905b5d7c9f9f2793fd8bb0ed003fa418ab35f8048faced61145d577502571ab0d0bbfb187bb689ee59f8e99539b3de9789d4905584340041e801e951c0f
7
+ data.tar.gz: d382691fcdd3995347d126ff70fa0dfc1e877b9136bfce4ca6ac6d783aa7802da544250b03601ae9d335ae4fd9c72af15ab412450e14c2d20a05a29b16b58763
@@ -10,8 +10,6 @@ rvm:
10
10
  - 2.5.0
11
11
  - 2.4.3
12
12
  - 2.3.5
13
- - 2.2.9
14
- - 2.1.10
15
13
 
16
14
  env:
17
15
  global:
@@ -42,8 +40,6 @@ matrix:
42
40
  env: PYENV_VERSION=miniconda3-4.3.11
43
41
  allow_failures:
44
42
  - os: osx
45
- - rvm: 2.2.9
46
- - rvm: 2.1.10
47
43
 
48
44
  before_install:
49
45
  - gem update --system
data/CHANGES.md CHANGED
@@ -22,6 +22,16 @@
22
22
 
23
23
  *Kouhei Sutou*
24
24
 
25
+ * Support multiple candidates of Python command in `PyCall.init`
26
+
27
+ * Now, `PyCall.init` tries `python3` command before `python` in default
28
+
29
+ * Drop Ruby 2.2 and 2.1 supports
30
+
31
+ * Add `PyCall::PyTypeObjectWrapper#<` as `Class#<`
32
+
33
+ * Support class inheritance in python type mapping
34
+
25
35
  ## 1.0.3
26
36
 
27
37
  * Fix anaconda support to define the environment variable `PYTHONHOME`.
data/README.md CHANGED
@@ -1,3 +1,8 @@
1
+ <a name="logo"/>
2
+ <div align="center">
3
+ <img src="./images/pycallrb_logo_200.png" alt="pycall.rb logo" width="200" height="200"></img>
4
+ </div>
5
+
1
6
  # PyCall: Calling Python functions from the Ruby language
2
7
 
3
8
  [![Build Status](https://travis-ci.org/mrkn/pycall.rb.svg?branch=master)](https://travis-ci.org/mrkn/pycall.rb)
@@ -8,6 +13,16 @@ with Python from the Ruby language. You can import arbitrary Python modules
8
13
  into Ruby modules, call Python functions with automatic type conversion from
9
14
  Ruby to Python.
10
15
 
16
+ ## Supported Ruby versions
17
+
18
+ pycall.rb supports Ruby version 2.3 or higher.
19
+
20
+ ## Supported Python versions
21
+
22
+ pycall.rb supports Python version 2.7 or higher.
23
+
24
+ Note that in Python 2.7 old-style class, that is defined without a super class, is not fully supported in pycall.rb.
25
+
11
26
  ## Installation
12
27
 
13
28
  Add this line to your application's Gemfile:
@@ -94,6 +94,7 @@ pycall_init_libpython_api_table(VALUE libpython_handle)
94
94
  INIT_API_TABLE_ENTRY(_PyObject_New, required);
95
95
  INIT_API_TABLE_ENTRY(PyCallable_Check, required);
96
96
  INIT_API_TABLE_ENTRY(PyObject_IsInstance, required);
97
+ INIT_API_TABLE_ENTRY(PyObject_IsSubclass, required);
97
98
  INIT_API_TABLE_ENTRY2(PyObject_Hash._hash_t, PyObject_Hash, required);
98
99
  INIT_API_TABLE_ENTRY(PyObject_RichCompare, required);
99
100
  INIT_API_TABLE_ENTRY(PyObject_Call, required);
@@ -452,7 +452,7 @@ get_pytypeobj_ptr(VALUE obj)
452
452
  static inline PyTypeObject*
453
453
  try_get_pytypeobj_ptr(VALUE obj)
454
454
  {
455
- if (is_pycall_pytypeptr(obj)) return NULL;
455
+ if (!is_pycall_pytypeptr(obj)) return NULL;
456
456
  return (PyTypeObject*)DATA_PTR(obj);
457
457
  }
458
458
 
@@ -541,6 +541,20 @@ pycall_pytypeptr_eqq(VALUE obj, VALUE other)
541
541
  return Qfalse;
542
542
  }
543
543
 
544
+ static VALUE
545
+ pycall_pytypeptr_subclass_p(VALUE obj, VALUE other)
546
+ {
547
+ PyTypeObject* pytype = get_pytypeobj_ptr(obj);
548
+ if (is_pycall_pyptr(other)) {
549
+ PyTypeObject* pytype_other = try_get_pytypeobj_ptr(other);
550
+ if (pytype_other) {
551
+ int res = Py_API(PyObject_IsSubclass)((PyObject *)pytype, (PyObject *)pytype_other);
552
+ return res ? Qtrue : Qfalse;
553
+ }
554
+ }
555
+ return Qfalse;
556
+ }
557
+
544
558
  /* ==== PyCall::LibPython::API ==== */
545
559
 
546
560
  static VALUE
@@ -1277,11 +1291,39 @@ pycall_pyobject_wrapper_check_get_pyobj_ptr(VALUE obj, PyTypeObject *pytypeobj)
1277
1291
 
1278
1292
  /* ==== PyCall::Conversion ==== */
1279
1293
 
1294
+ static int
1295
+ get_mapped_ancestor_class_iter(VALUE key, VALUE value, VALUE arg)
1296
+ {
1297
+ VALUE *args = (VALUE *)arg;
1298
+ if (RTEST(pycall_pytypeptr_subclass_p(args[0], key))) {
1299
+ args[1] = value;
1300
+ return ST_STOP;
1301
+ }
1302
+ return ST_CONTINUE;
1303
+ }
1304
+
1305
+ static VALUE
1306
+ pycall_python_type_mapping_get_mapped_ancestor_class(VALUE pytypeptr)
1307
+ {
1308
+ VALUE args[2];
1309
+ args[0] = pytypeptr;
1310
+ args[1] = Qnil;
1311
+
1312
+ rb_hash_foreach(python_type_mapping, get_mapped_ancestor_class_iter, (VALUE)args);
1313
+
1314
+ return args[1];
1315
+ }
1316
+
1280
1317
  static VALUE
1281
1318
  pycall_python_type_mapping_get_mapped_class(VALUE pytypeptr)
1282
1319
  {
1320
+ VALUE mapped;
1283
1321
  (void)check_get_pytypeobj_ptr(pytypeptr);
1284
- return rb_hash_lookup(python_type_mapping, pytypeptr);
1322
+ mapped = rb_hash_lookup(python_type_mapping, pytypeptr);
1323
+ if (NIL_P(mapped)) {
1324
+ mapped = pycall_python_type_mapping_get_mapped_ancestor_class(pytypeptr);
1325
+ }
1326
+ return mapped;
1285
1327
  }
1286
1328
 
1287
1329
  static int
@@ -2179,6 +2221,7 @@ Init_pycall(void)
2179
2221
  rb_define_method(cPyTypePtr, "__tp_basicsize__", pycall_pytypeptr_get_tp_basicsize, 0);
2180
2222
  rb_define_method(cPyTypePtr, "__tp_flags__", pycall_pytypeptr_get_tp_flags, 0);
2181
2223
  rb_define_method(cPyTypePtr, "===", pycall_pytypeptr_eqq, 1);
2224
+ rb_define_method(cPyTypePtr, "<", pycall_pytypeptr_subclass_p, 1);
2182
2225
 
2183
2226
  /* PyCall::LibPython::API */
2184
2227
 
@@ -529,6 +529,7 @@ typedef struct {
529
529
  PyObject * (* _PyObject_New)(PyTypeObject *);
530
530
  int (* PyCallable_Check)(PyObject *);
531
531
  int (* PyObject_IsInstance)(PyObject *, PyObject *);
532
+ int (* PyObject_IsSubclass)(PyObject *, PyObject *);
532
533
  union {
533
534
  long (* _long)(PyObject *);
534
535
  Py_hash_t (* _hash_t)(PyObject *);
@@ -1,4 +1,5 @@
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},
Binary file
@@ -19,25 +19,26 @@ module PyCall
19
19
  LIBSUFFIX = libsuffix || 'so'
20
20
 
21
21
  class << self
22
+ DEFAULT_PYTHON = [
23
+ -'python3',
24
+ -'python',
25
+ ].freeze
26
+
27
+ def find_python_config(python = nil)
28
+ python ||= DEFAULT_PYTHON
29
+ Array(python).each do |python_cmd|
30
+ python_config = investigate_python_config(python_cmd)
31
+ return [python_cmd, python_config] unless python_config.empty?
32
+ end
33
+ rescue
34
+ raise ::PyCall::PythonNotFound
35
+ else
36
+ raise ::PyCall::PythonNotFound
37
+ end
38
+
22
39
  def find_libpython(python = nil)
23
40
  debug_report("find_libpython(#{python.inspect})")
24
- if python
25
- begin
26
- python_config = investigate_python_config(python)
27
- rescue
28
- raise ::PyCall::PythonNotFound
29
- end
30
- else
31
- %w[python python3].each do |python_cmd|
32
- begin
33
- python_config = investigate_python_config(python_cmd)
34
- python = python_cmd
35
- break
36
- rescue
37
- raise ::PyCall::PythonNotFound
38
- end
39
- end
40
- end
41
+ python, python_config = find_python_config(python)
41
42
 
42
43
  set_PYTHONHOME(python_config)
43
44
  libs = make_libs(python_config)
@@ -101,6 +102,7 @@ module PyCall
101
102
  IO.popen(python_env, [python, python_investigator_py], 'r') do |io|
102
103
  {}.tap do |config|
103
104
  io.each_line do |line|
105
+ next unless line =~ /: /
104
106
  key, value = line.chomp.split(': ', 2)
105
107
  case value
106
108
  when 'True', 'true', 'False', 'false'
@@ -49,6 +49,15 @@ module PyCall
49
49
  end
50
50
  end
51
51
 
52
+ def <(other)
53
+ case other
54
+ when PyTypeObjectWrapper
55
+ __pyptr__ < other.__pyptr__
56
+ else
57
+ raise TypeError, "compared with non class/module"
58
+ end
59
+ end
60
+
52
61
  private
53
62
 
54
63
  def register_python_type_mapping
@@ -1,3 +1,3 @@
1
1
  module PyCall
2
- VERSION = "1.1.0.rc1"
2
+ VERSION = "1.2.0.beta1"
3
3
  end
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.1.0.rc1
4
+ version: 1.2.0.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kenta Murata
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-03-07 00:00:00.000000000 Z
11
+ date: 2018-08-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -171,8 +171,9 @@ files:
171
171
  - ext/pycall/pycall_internal.h
172
172
  - ext/pycall/range.c
173
173
  - ext/pycall/ruby_wrapper.c
174
+ - images/pycallrb_logo.png
175
+ - images/pycallrb_logo_200.png
174
176
  - lib/pycall.rb
175
- - lib/pycall/conversion.rb
176
177
  - lib/pycall/dict.rb
177
178
  - lib/pycall/error.rb
178
179
  - lib/pycall/gc_guard.rb
@@ -193,7 +194,6 @@ files:
193
194
  - lib/pycall/pytypeobject_wrapper.rb
194
195
  - lib/pycall/set.rb
195
196
  - lib/pycall/slice.rb
196
- - lib/pycall/tuple.rb
197
197
  - lib/pycall/version.rb
198
198
  - lib/pycall/wrapper_object_cache.rb
199
199
  - pycall.gemspec
@@ -219,7 +219,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
219
219
  version: 1.3.1
220
220
  requirements: []
221
221
  rubyforge_project:
222
- rubygems_version: 2.7.4
222
+ rubygems_version: 2.7.6
223
223
  signing_key:
224
224
  specification_version: 4
225
225
  summary: pycall
@@ -1,173 +0,0 @@
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? || pyptr.none?
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
@@ -1,46 +0,0 @@
1
- module PyCall
2
- class Tuple
3
- include PyObjectWrapper
4
-
5
- def self.new(init)
6
- case init
7
- when Integer
8
- super(LibPython.PyTuple_New(init))
9
- when Array
10
- tuple = new(init.length)
11
- init.each_with_index do |obj, index|
12
- tuple[index] = obj
13
- end
14
- tuple
15
- when LibPython::PyObjectStruct
16
- super(init)
17
- end
18
- end
19
-
20
- # Make tuple from array
21
- def self.[](*ary)
22
- new(ary)
23
- end
24
-
25
- def size
26
- LibPython.PyTuple_Size(__pyobj__)
27
- end
28
-
29
- alias length size
30
-
31
- def [](index)
32
- LibPython.PyTuple_GetItem(__pyobj__, index).to_ruby
33
- end
34
-
35
- def []=(index, value)
36
- value = Conversions.from_ruby(value)
37
- LibPython.PyTuple_SetItem(__pyobj__, index, value)
38
- end
39
-
40
- def to_a
41
- Array.new(length) {|i| self[i] }
42
- end
43
-
44
- alias to_ary to_a
45
- end
46
- end