pycall 0.1.0.alpha.20170711 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.travis.yml +13 -1
- data/CHANGES.md +35 -0
- data/Gemfile +0 -5
- data/README.md +41 -49
- data/Rakefile +22 -1
- data/appveyor.yml +9 -26
- data/examples/classifier_comparison.rb +52 -52
- data/examples/hist.rb +11 -11
- data/examples/notebooks/classifier_comparison.ipynb +51 -66
- data/examples/notebooks/forest_importances.ipynb +26 -49
- data/examples/notebooks/iruby_integration.ipynb +15 -36
- data/examples/notebooks/lorenz_attractor.ipynb +16 -47
- data/examples/notebooks/polar_axes.ipynb +29 -64
- data/examples/notebooks/sum_benchmarking.ipynb +109 -103
- data/examples/notebooks/xkcd_style.ipynb +12 -12
- data/examples/plot_forest_importances_faces.rb +8 -8
- data/examples/sum_benchmarking.rb +15 -19
- data/ext/pycall/extconf.rb +3 -0
- data/ext/pycall/gc.c +74 -0
- data/ext/pycall/libpython.c +217 -0
- data/ext/pycall/pycall.c +2184 -0
- data/ext/pycall/pycall_internal.h +700 -0
- data/ext/pycall/range.c +69 -0
- data/ext/pycall/ruby_wrapper.c +432 -0
- data/lib/pycall.rb +91 -19
- data/lib/pycall/dict.rb +28 -82
- data/lib/pycall/error.rb +10 -0
- data/lib/pycall/import.rb +45 -40
- data/lib/pycall/init.rb +44 -20
- data/lib/pycall/libpython.rb +6 -380
- data/lib/pycall/libpython/finder.rb +170 -0
- data/lib/pycall/list.rb +21 -51
- data/lib/pycall/pretty_print.rb +9 -0
- data/lib/pycall/pyerror.rb +14 -20
- data/lib/pycall/pyobject_wrapper.rb +157 -158
- data/lib/pycall/python/PyCall/__init__.py +1 -0
- data/lib/pycall/python/PyCall/six.py +23 -0
- data/lib/pycall/pytypeobject_wrapper.rb +79 -0
- data/lib/pycall/slice.rb +3 -22
- data/lib/pycall/tuple.rb +1 -7
- data/lib/pycall/version.rb +1 -1
- data/lib/pycall/wrapper_object_cache.rb +61 -0
- data/pycall.gemspec +4 -2
- data/tasks/pycall.rake +7 -0
- metadata +65 -27
- data/lib/pycall/eval.rb +0 -57
- data/lib/pycall/exception.rb +0 -13
- data/lib/pycall/pyobject.rb +0 -58
- data/lib/pycall/ruby_wrapper.rb +0 -137
- data/lib/pycall/type_object.rb +0 -11
- data/lib/pycall/types.rb +0 -19
- data/lib/pycall/utils.rb +0 -106
data/lib/pycall/list.rb
CHANGED
@@ -1,73 +1,43 @@
|
|
1
1
|
module PyCall
|
2
|
+
List = builtins.list
|
2
3
|
class List
|
3
|
-
|
4
|
+
register_python_type_mapping
|
5
|
+
|
4
6
|
include Enumerable
|
5
7
|
|
6
|
-
def
|
7
|
-
|
8
|
-
when LibPython::PyObjectStruct
|
9
|
-
super
|
10
|
-
when nil
|
11
|
-
new(0)
|
12
|
-
when Integer
|
13
|
-
new(LibPython.PyList_New(init))
|
14
|
-
when Array
|
15
|
-
new.tap do |list|
|
16
|
-
init.each do |item|
|
17
|
-
list << item
|
18
|
-
end
|
19
|
-
end
|
20
|
-
else
|
21
|
-
new(obj.to_ary)
|
22
|
-
end
|
8
|
+
def include?(item)
|
9
|
+
LibPython::Helpers.sequence_contains(__pyptr__, item)
|
23
10
|
end
|
24
11
|
|
25
|
-
def
|
26
|
-
|
27
|
-
LibPython.PyList_Append(__pyobj__, value)
|
28
|
-
self
|
12
|
+
def length
|
13
|
+
PyCall.len(self)
|
29
14
|
end
|
30
15
|
|
31
|
-
def
|
32
|
-
|
16
|
+
def each
|
17
|
+
return enum_for unless block_given?
|
18
|
+
LibPython::Helpers.sequence_each(__pyptr__, &proc)
|
19
|
+
self
|
33
20
|
end
|
34
21
|
|
35
|
-
|
22
|
+
def <<(item)
|
23
|
+
append(item)
|
24
|
+
end
|
36
25
|
|
37
|
-
def
|
38
|
-
|
39
|
-
value = LibPython.PySequence_Contains(__pyobj__, value)
|
40
|
-
raise PyError.fetch if value == -1
|
41
|
-
1 == value
|
26
|
+
def push(*items)
|
27
|
+
items.each {|i| append(i) }
|
42
28
|
end
|
43
29
|
|
44
|
-
def
|
45
|
-
|
46
|
-
when Array
|
47
|
-
self.to_a == other
|
48
|
-
else
|
49
|
-
super
|
50
|
-
end
|
30
|
+
def sort
|
31
|
+
dup.sort!
|
51
32
|
end
|
52
33
|
|
53
|
-
def
|
54
|
-
|
55
|
-
i, n = 0, size
|
56
|
-
while i < n
|
57
|
-
yield self[i]
|
58
|
-
i += 1
|
59
|
-
end
|
34
|
+
def sort!
|
35
|
+
LibPython::Helpers.getattr(__pyptr__, :sort).__call__
|
60
36
|
self
|
61
37
|
end
|
62
38
|
|
63
39
|
def to_a
|
64
|
-
|
65
|
-
i, n = 0, size
|
66
|
-
while i < n
|
67
|
-
a << self[i]
|
68
|
-
i += 1
|
69
|
-
end
|
70
|
-
end
|
40
|
+
Array.new(length) {|i| self[i] }
|
71
41
|
end
|
72
42
|
|
73
43
|
alias to_ary to_a
|
data/lib/pycall/pyerror.rb
CHANGED
@@ -1,36 +1,30 @@
|
|
1
|
-
|
2
|
-
class PyError < StandardError
|
3
|
-
def self.fetch
|
4
|
-
ptrs = FFI::MemoryPointer.new(:pointer, 3)
|
5
|
-
ptype = ptrs + 0 * ptrs.type_size
|
6
|
-
pvalue = ptrs + 1 * ptrs.type_size
|
7
|
-
ptraceback = ptrs + 2 * ptrs.type_size
|
8
|
-
LibPython.PyErr_Fetch(ptype, pvalue, ptraceback)
|
9
|
-
LibPython.PyErr_NormalizeException(ptype, pvalue, ptraceback)
|
10
|
-
type = Conversions.to_ruby(ptype.read(:pointer))
|
11
|
-
value = Conversions.to_ruby(pvalue.read(:pointer))
|
12
|
-
traceback = Conversions.to_ruby(ptraceback.read(:pointer))
|
13
|
-
new(type, value, traceback)
|
14
|
-
end
|
1
|
+
require 'pycall/error'
|
15
2
|
|
3
|
+
module PyCall
|
4
|
+
class PyError < Error
|
16
5
|
def initialize(type, value, traceback)
|
17
6
|
@type = type
|
18
7
|
@value = value
|
19
8
|
@traceback = traceback
|
20
|
-
super("
|
9
|
+
super("Exception occurred in Python")
|
21
10
|
end
|
22
11
|
|
23
12
|
attr_reader :type, :value, :traceback
|
24
13
|
|
25
14
|
def to_s
|
26
15
|
"#{type}: #{value}".tap do |msg|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
strs.each {|s| msg << s }
|
31
|
-
end
|
16
|
+
if (strs = format_traceback)
|
17
|
+
msg << "\n"
|
18
|
+
strs.each {|s| msg << s }
|
32
19
|
end
|
33
20
|
end
|
34
21
|
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def format_traceback
|
26
|
+
return nil if traceback.nil?
|
27
|
+
::PyCall.import_module('traceback').format_tb(traceback)
|
28
|
+
end
|
35
29
|
end
|
36
30
|
end
|
@@ -1,213 +1,212 @@
|
|
1
|
-
|
2
|
-
HASH_SALT = "PyCall::PyObject".hash
|
3
|
-
|
4
|
-
Py_LT = 0
|
5
|
-
Py_LE = 1
|
6
|
-
Py_EQ = 2
|
7
|
-
Py_NE = 3
|
8
|
-
Py_GT = 4
|
9
|
-
Py_GE = 5
|
10
|
-
|
11
|
-
RICH_COMPARISON_OPCODES = {
|
12
|
-
:< => Py_LT,
|
13
|
-
:<= => Py_LE,
|
14
|
-
:== => Py_EQ,
|
15
|
-
:!= => Py_NE,
|
16
|
-
:> => Py_GT,
|
17
|
-
:>= => Py_GE
|
18
|
-
}.freeze
|
1
|
+
require 'pycall/wrapper_object_cache'
|
19
2
|
|
3
|
+
module PyCall
|
20
4
|
module PyObjectWrapper
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
def wrap_class(pyclass)
|
25
|
-
pyclass__pyobj__ = pyclass.__pyobj__
|
26
|
-
define_singleton_method(:__pyobj__) { pyclass__pyobj__ }
|
27
|
-
|
28
|
-
PyCall.dir(__pyobj__).each do |name|
|
29
|
-
obj = PyCall.getattr(__pyobj__, name)
|
30
|
-
next unless obj.kind_of?(PyCall::PyObject) || obj.kind_of?(PyCall::PyObjectWrapper)
|
31
|
-
next unless PyCall.callable?(obj)
|
5
|
+
attr_reader :__pyptr__
|
32
6
|
|
33
|
-
|
34
|
-
|
35
|
-
|
7
|
+
def self.extend_object(obj)
|
8
|
+
pyptr = obj.instance_variable_get(:@__pyptr__)
|
9
|
+
unless pyptr.kind_of? PyPtr
|
10
|
+
raise TypeError, "@__pyptr__ should have PyCall::PyPtr object"
|
11
|
+
end
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
OPERATOR_METHOD_NAMES = {
|
16
|
+
:+ => :__add__,
|
17
|
+
:- => :__sub__,
|
18
|
+
:* => :__mul__,
|
19
|
+
:/ => :__truediv__,
|
20
|
+
:% => :__mod__,
|
21
|
+
:** => :__pow__,
|
22
|
+
:<< => :__lshift__,
|
23
|
+
:>> => :__rshift__,
|
24
|
+
:& => :__and__,
|
25
|
+
:^ => :__xor__,
|
26
|
+
:| => :__or__
|
27
|
+
}.freeze
|
28
|
+
|
29
|
+
def method_missing(name, *args)
|
30
|
+
name_str = name.to_s if name.kind_of?(Symbol)
|
31
|
+
name_str.chop! if name_str.end_with?('=')
|
32
|
+
case name
|
33
|
+
when *OPERATOR_METHOD_NAMES.keys
|
34
|
+
op_name = OPERATOR_METHOD_NAMES[name]
|
35
|
+
if LibPython::Helpers.hasattr?(__pyptr__, op_name)
|
36
|
+
LibPython::Helpers.define_wrapper_method(self, op_name)
|
37
|
+
singleton_class.__send__(:alias_method, name, op_name)
|
38
|
+
return self.__send__(name, *args)
|
36
39
|
end
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
PyCall.getattr(__pyobj__, name)
|
42
|
-
end
|
40
|
+
else
|
41
|
+
if LibPython::Helpers.hasattr?(__pyptr__, name_str)
|
42
|
+
LibPython::Helpers.define_wrapper_method(self, name)
|
43
|
+
return self.__send__(name, *args)
|
43
44
|
end
|
44
|
-
|
45
|
-
PyCall::Conversions.python_type_mapping(__pyobj__, self)
|
46
45
|
end
|
46
|
+
super
|
47
47
|
end
|
48
48
|
|
49
|
-
def
|
50
|
-
|
49
|
+
def respond_to_missing?(name, include_private)
|
50
|
+
return true if LibPython::Helpers.hasattr?(__pyptr__, name)
|
51
|
+
super
|
51
52
|
end
|
52
53
|
|
53
|
-
def
|
54
|
-
|
55
|
-
|
56
|
-
|
54
|
+
def kind_of?(cls)
|
55
|
+
case cls
|
56
|
+
when PyTypeObjectWrapper
|
57
|
+
__pyptr__.kind_of?(cls.__pyptr__)
|
58
|
+
else
|
59
|
+
super
|
60
|
+
end
|
57
61
|
end
|
58
62
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
+
[:==, :!=, :<, :<=, :>, :>=].each do |op|
|
64
|
+
class_eval("#{<<-"begin;"}\n#{<<-"end;"}", __FILE__, __LINE__+1)
|
65
|
+
begin;
|
66
|
+
def #{op}(other)
|
67
|
+
case other
|
68
|
+
when PyObjectWrapper
|
69
|
+
LibPython::Helpers.compare(:#{op}, __pyptr__, other.__pyptr__)
|
70
|
+
else
|
71
|
+
other = Conversion.from_ruby(other)
|
72
|
+
LibPython::Helpers.compare(:#{op}, __pyptr__, other)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end;
|
63
76
|
end
|
64
77
|
|
65
|
-
def
|
66
|
-
|
67
|
-
return super if hash_value == -1
|
68
|
-
hash_value
|
78
|
+
def [](*key)
|
79
|
+
LibPython::Helpers.getitem(__pyptr__, key)
|
69
80
|
end
|
70
81
|
|
71
|
-
def
|
72
|
-
LibPython.
|
82
|
+
def []=(*key, value)
|
83
|
+
LibPython::Helpers.setitem(__pyptr__, key, value)
|
73
84
|
end
|
74
85
|
|
75
|
-
def
|
76
|
-
|
86
|
+
def call(*args)
|
87
|
+
LibPython::Helpers.call_object(__pyptr__, *args)
|
77
88
|
end
|
78
89
|
|
79
|
-
|
80
|
-
|
81
|
-
|
90
|
+
class SwappedOperationAdapter
|
91
|
+
def initialize(obj)
|
92
|
+
@obj = obj
|
93
|
+
end
|
82
94
|
|
83
|
-
|
84
|
-
to_ptr == PyCall.None.to_ptr
|
85
|
-
end
|
95
|
+
attr_reader :obj
|
86
96
|
|
87
|
-
|
88
|
-
|
89
|
-
when PyObjectWrapper
|
90
|
-
__pyobj__.kind_of? klass.__pyobj__
|
91
|
-
when LibPython::PyObjectStruct
|
92
|
-
__pyobj__.kind_of? klass
|
93
|
-
else
|
94
|
-
super
|
97
|
+
def +(other)
|
98
|
+
other.__radd__(self.obj)
|
95
99
|
end
|
96
|
-
end
|
97
100
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
+
def -(other)
|
102
|
+
other.__rsub__(self.obj)
|
103
|
+
end
|
101
104
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
+
def *(other)
|
106
|
+
other.__rmul__(self.obj)
|
107
|
+
end
|
105
108
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
end
|
109
|
+
def /(other)
|
110
|
+
other.__rtruediv__(self.obj)
|
111
|
+
end
|
110
112
|
|
111
|
-
|
112
|
-
|
113
|
-
|
113
|
+
def %(other)
|
114
|
+
other.__rmod__(self.obj)
|
115
|
+
end
|
114
116
|
|
115
|
-
|
116
|
-
|
117
|
-
indices = indices[0]
|
118
|
-
else
|
119
|
-
indices = PyCall.tuple(*indices)
|
117
|
+
def **(other)
|
118
|
+
other.__rpow__(self.obj)
|
120
119
|
end
|
121
|
-
PyCall.getitem(self, indices)
|
122
|
-
end
|
123
120
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
121
|
+
def <<(other)
|
122
|
+
other.__rlshift__(self.obj)
|
123
|
+
end
|
124
|
+
|
125
|
+
def >>(other)
|
126
|
+
other.__rrshift__(self.obj)
|
127
|
+
end
|
128
|
+
|
129
|
+
def &(other)
|
130
|
+
other.__rand__(self.obj)
|
131
|
+
end
|
132
|
+
|
133
|
+
def ^(other)
|
134
|
+
other.__rxor__(self.obj)
|
135
|
+
end
|
136
|
+
|
137
|
+
def |(other)
|
138
|
+
other.__ror__(self.obj)
|
131
139
|
end
|
132
|
-
PyCall.setitem(self, indices, value)
|
133
140
|
end
|
134
141
|
|
135
|
-
def
|
136
|
-
|
137
|
-
value = LibPython.PyNumber_Add(__pyobj__, other)
|
138
|
-
return value.to_ruby unless value.null?
|
139
|
-
raise PyError.fetch
|
142
|
+
def coerce(other)
|
143
|
+
[SwappedOperationAdapter.new(other), self]
|
140
144
|
end
|
141
145
|
|
142
|
-
def
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
146
|
+
def dup
|
147
|
+
super.tap do |duped|
|
148
|
+
copied = PyCall.import_module('copy').copy(__pyptr__)
|
149
|
+
copied = copied.__pyptr__ if copied.kind_of? PyObjectWrapper
|
150
|
+
duped.instance_variable_set(:@__pyptr__, copied)
|
151
|
+
end
|
147
152
|
end
|
148
153
|
|
149
|
-
def
|
150
|
-
|
151
|
-
value = LibPython.PyNumber_Multiply(__pyobj__, other)
|
152
|
-
return value.to_ruby unless value.null?
|
153
|
-
raise PyError.fetch
|
154
|
+
def inspect
|
155
|
+
PyCall.builtins.repr(__pyptr__)
|
154
156
|
end
|
155
157
|
|
156
|
-
def
|
157
|
-
|
158
|
-
value = LibPython.PyNumber_TrueDivide(__pyobj__, other)
|
159
|
-
return value.to_ruby unless value.null?
|
160
|
-
raise PyError.fetch
|
158
|
+
def to_s
|
159
|
+
LibPython::Helpers.str(__pyptr__)
|
161
160
|
end
|
162
161
|
|
163
|
-
def
|
164
|
-
|
165
|
-
value = LibPython.PyNumber_Power(__pyobj__, other, PyCall.None)
|
166
|
-
return value.to_ruby unless value.null?
|
167
|
-
raise PyError.fetch
|
162
|
+
def to_i
|
163
|
+
LibPython::Helpers.call_object(PyCall::builtins.int.__pyptr__, __pyptr__)
|
168
164
|
end
|
169
165
|
|
170
|
-
def
|
171
|
-
|
166
|
+
def to_f
|
167
|
+
LibPython::Helpers.call_object(PyCall::builtins.float.__pyptr__, __pyptr__)
|
172
168
|
end
|
169
|
+
end
|
170
|
+
|
171
|
+
module_function
|
173
172
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
res = LibPython.PyObject_Call(__pyobj__, args.__pyobj__, kwargs.__pyobj__)
|
178
|
-
return res.to_ruby if LibPython.PyErr_Occurred().null?
|
179
|
-
raise PyError.fetch
|
173
|
+
class WrapperModuleCache < WrapperObjectCache
|
174
|
+
def initialize
|
175
|
+
super(LibPython::API::PyModule_Type)
|
180
176
|
end
|
181
177
|
|
182
|
-
def
|
183
|
-
|
184
|
-
|
185
|
-
name = name_s[0..-2]
|
186
|
-
if PyCall.hasattr?(__pyobj__, name.to_s)
|
187
|
-
PyCall.setattr(__pyobj__, name, args.first)
|
188
|
-
else
|
189
|
-
raise NameError, "object has no attribute `#{name}'"
|
190
|
-
end
|
191
|
-
elsif PyCall.hasattr?(__pyobj__, name.to_s)
|
192
|
-
PyCall.getattr(__pyobj__, name)
|
193
|
-
else
|
194
|
-
super
|
178
|
+
def check_wrapper_object(wrapper_object)
|
179
|
+
unless wrapper_object.kind_of?(Module) && wrapper_object.kind_of?(PyObjectWrapper)
|
180
|
+
raise TypeError, "unexpected type #{wrapper_object.class} (expected Module extended by PyObjectWrapper)"
|
195
181
|
end
|
196
182
|
end
|
197
183
|
|
198
|
-
def
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
184
|
+
def self.instance
|
185
|
+
@instance ||= self.new
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
private_constant :WrapperModuleCache
|
190
|
+
|
191
|
+
def wrap_module(pymodptr)
|
192
|
+
check_ismodule(pymodptr)
|
193
|
+
WrapperModuleCache.instance.lookup(pymodptr) do
|
194
|
+
Module.new do |mod|
|
195
|
+
mod.instance_variable_set(:@__pyptr__, pymodptr)
|
196
|
+
mod.extend PyObjectWrapper
|
207
197
|
end
|
208
|
-
s.to_ruby
|
209
198
|
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def check_isclass(pyptr)
|
202
|
+
pyptr = pyptr.__pyptr__ if pyptr.kind_of? PyObjectWrapper
|
203
|
+
return if pyptr.kind_of? LibPython::API::PyType_Type
|
204
|
+
return defined?(LibPython::API::PyClass_Type) && pyptr.kind_of?(LibPython::API::PyClass_Type)
|
205
|
+
raise TypeError, "PyType object is required"
|
206
|
+
end
|
210
207
|
|
211
|
-
|
208
|
+
def check_ismodule(pyptr)
|
209
|
+
return if pyptr.kind_of? LibPython::API::PyModule_Type
|
210
|
+
raise TypeError, "PyModule object is required"
|
212
211
|
end
|
213
212
|
end
|