pycall 1.1.0.rc1 → 1.2.0.beta1

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
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