pycall 0.1.0.alpha.20170711 → 1.0.0
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 +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
|