pycall 0.1.0.alpha → 0.1.0.alpha.20170224
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/.travis.yml +15 -1
- data/Gemfile +5 -0
- data/Guardfile +1 -0
- data/README.md +4 -2
- data/bin/guard +17 -0
- data/bin/rspec +17 -0
- data/config/Guardfile +30 -0
- data/examples/classifier_comparison.rb +130 -0
- data/examples/hist.rb +27 -0
- data/examples/plot_forest_importances_faces.rb +41 -0
- data/examples/sum_benchmarking.rb +48 -0
- data/lib/pycall.rb +14 -4
- data/lib/pycall/conversion.rb +120 -0
- data/lib/pycall/dict.rb +90 -0
- data/lib/pycall/eval.rb +39 -0
- data/lib/pycall/import.rb +74 -0
- data/lib/pycall/init.rb +16 -0
- data/lib/pycall/libpython.rb +331 -0
- data/lib/pycall/list.rb +65 -0
- data/lib/pycall/pyerror.rb +25 -0
- data/lib/pycall/pyobject.rb +185 -0
- data/lib/pycall/pyobject_wrapper.rb +48 -0
- data/lib/pycall/python/investigator.py +6 -0
- data/lib/pycall/set.rb +17 -0
- data/lib/pycall/slice.rb +27 -0
- data/lib/pycall/tuple.rb +54 -0
- data/lib/pycall/types.rb +15 -0
- data/lib/pycall/utils.rb +33 -0
- data/lib/pycall/version.rb +2 -2
- data/pycall.gemspec +3 -1
- metadata +41 -3
data/lib/pycall/list.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
module PyCall
|
2
|
+
class List
|
3
|
+
include PyObjectWrapper
|
4
|
+
|
5
|
+
def self.new(init=nil)
|
6
|
+
case init
|
7
|
+
when PyObject
|
8
|
+
super
|
9
|
+
when nil
|
10
|
+
new(0)
|
11
|
+
when Integer
|
12
|
+
new(LibPython.PyList_New(init))
|
13
|
+
when Array
|
14
|
+
new.tap do |list|
|
15
|
+
init.each do |item|
|
16
|
+
list << item
|
17
|
+
end
|
18
|
+
end
|
19
|
+
else
|
20
|
+
new(obj.to_ary)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(pyobj)
|
25
|
+
super(pyobj, LibPython.PyList_Type)
|
26
|
+
end
|
27
|
+
|
28
|
+
def [](index)
|
29
|
+
LibPython.PyList_GetItem(__pyobj__, index).to_ruby
|
30
|
+
end
|
31
|
+
|
32
|
+
def []=(index, value)
|
33
|
+
value = Conversions.from_ruby(value)
|
34
|
+
LibPython.PyList_SetItem(__pyobj__, index, value)
|
35
|
+
value
|
36
|
+
end
|
37
|
+
|
38
|
+
def <<(value)
|
39
|
+
value = Conversions.from_ruby(value)
|
40
|
+
LibPython.PyList_Append(__pyobj__, value)
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
def size
|
45
|
+
LibPython.PyList_Size(__pyobj__)
|
46
|
+
end
|
47
|
+
|
48
|
+
def include?(value)
|
49
|
+
value = Conversions.from_ruby(value)
|
50
|
+
LibPython.PyList_Contains(__pyobj__, value).to_ruby
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_a
|
54
|
+
[].tap do |a|
|
55
|
+
i, n = 0, size
|
56
|
+
while i < n
|
57
|
+
a << self[i]
|
58
|
+
i += 1
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
alias to_ary to_a
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module PyCall
|
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 = PyTypeObject.new(ptype.read(:pointer))
|
11
|
+
value = PyObject.new(pvalue.read(:pointer))
|
12
|
+
traceback = PyObject.new(ptraceback.read(:pointer))
|
13
|
+
new(type, value, traceback)
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(type, value, traceback)
|
17
|
+
@type = type
|
18
|
+
@value = value
|
19
|
+
@traceback = traceback
|
20
|
+
super("#{@type.inspect}: #{PyCall.eval('str').(@value)}")
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :type, :value, :traceback
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
module PyCall
|
2
|
+
Py_LT = 0
|
3
|
+
Py_LE = 1
|
4
|
+
Py_EQ = 2
|
5
|
+
Py_NE = 3
|
6
|
+
Py_GT = 4
|
7
|
+
Py_GE = 5
|
8
|
+
|
9
|
+
RICH_COMPARISON_OPCODES = {
|
10
|
+
:< => Py_LT,
|
11
|
+
:<= => Py_LE,
|
12
|
+
:== => Py_EQ,
|
13
|
+
:!= => Py_NE,
|
14
|
+
:> => Py_GT,
|
15
|
+
:>= => Py_GE
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
module PyObjectMethods
|
19
|
+
def rich_compare(other, op)
|
20
|
+
opcode = RICH_COMPARISON_OPCODES[op]
|
21
|
+
raise ArgumentError, "Unknown comparison op: #{op}" unless opcode
|
22
|
+
|
23
|
+
other = Conversions.from_ruby(other) unless other.kind_of?(PyObject)
|
24
|
+
return other.null? if self.null?
|
25
|
+
return false if other.null?
|
26
|
+
|
27
|
+
value = LibPython.PyObject_RichCompare(self, other, opcode)
|
28
|
+
raise "Unable to compare: #{self} #{op} #{other}" if value.null?
|
29
|
+
value.to_ruby
|
30
|
+
end
|
31
|
+
|
32
|
+
RICH_COMPARISON_OPCODES.keys.each do |op|
|
33
|
+
define_method(op) {|other| rich_compare(other, op) }
|
34
|
+
end
|
35
|
+
|
36
|
+
def py_none?
|
37
|
+
to_ptr == LibPython.Py_None.to_ptr
|
38
|
+
end
|
39
|
+
|
40
|
+
def kind_of?(klass)
|
41
|
+
case klass
|
42
|
+
when PyTypeObject
|
43
|
+
Types.pyisinstance(self, klass)
|
44
|
+
else
|
45
|
+
super
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class PyObject < FFI::Struct
|
51
|
+
include PyObjectMethods
|
52
|
+
|
53
|
+
def self.null
|
54
|
+
new(FFI::Pointer::NULL)
|
55
|
+
end
|
56
|
+
|
57
|
+
alias __aref__ []
|
58
|
+
alias __aset__ []=
|
59
|
+
|
60
|
+
def [](*indices)
|
61
|
+
if indices.length == 1
|
62
|
+
indices = indices[0]
|
63
|
+
else
|
64
|
+
indices = PyCall.tuple(*indices)
|
65
|
+
end
|
66
|
+
PyCall.getitem(self, indices)
|
67
|
+
end
|
68
|
+
|
69
|
+
def []=(*indices_and_value)
|
70
|
+
value = indices_and_value.pop
|
71
|
+
indices = indices_and_value
|
72
|
+
if indices.length == 1
|
73
|
+
indices = indices[0]
|
74
|
+
else
|
75
|
+
indices = PyCall.tuple(*indices)
|
76
|
+
end
|
77
|
+
PyCall.setitem(self, indices, value)
|
78
|
+
end
|
79
|
+
|
80
|
+
def +(other)
|
81
|
+
value = LibPython.PyNumber_Add(self, other)
|
82
|
+
return value.to_ruby unless value.null?
|
83
|
+
raise PyError.fetch
|
84
|
+
end
|
85
|
+
|
86
|
+
def -(other)
|
87
|
+
value = LibPython.PyNumber_Subtract(self, other)
|
88
|
+
return value.to_ruby unless value.null?
|
89
|
+
raise PyError.fetch
|
90
|
+
end
|
91
|
+
|
92
|
+
def *(other)
|
93
|
+
value = LibPython.PyNumber_Multiply(self, other)
|
94
|
+
return value.to_ruby unless value.null?
|
95
|
+
raise PyError.fetch
|
96
|
+
end
|
97
|
+
|
98
|
+
def /(other)
|
99
|
+
value = LibPython.PyNumber_TrueDivide(self, other)
|
100
|
+
return value.to_ruby unless value.null?
|
101
|
+
raise PyError.fetch
|
102
|
+
end
|
103
|
+
|
104
|
+
def coerce(other)
|
105
|
+
[Conversions.from_ruby(other), self]
|
106
|
+
end
|
107
|
+
|
108
|
+
def call(*args, **kwargs)
|
109
|
+
args = PyCall::Tuple[*args]
|
110
|
+
kwargs = kwargs.empty? ? PyObject.null : PyCall::Dict.new(kwargs).__pyobj__
|
111
|
+
res = LibPython.PyObject_Call(self, args.__pyobj__, kwargs)
|
112
|
+
return res.to_ruby if LibPython.PyErr_Occurred().null?
|
113
|
+
raise PyError.fetch
|
114
|
+
end
|
115
|
+
|
116
|
+
def method_missing(name, *args, **kwargs)
|
117
|
+
if PyCall.hasattr?(self, name)
|
118
|
+
PyCall.getattr(self, name)
|
119
|
+
else
|
120
|
+
super
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def to_s
|
125
|
+
PyCall.str(self)
|
126
|
+
end
|
127
|
+
|
128
|
+
alias inspect to_s
|
129
|
+
end
|
130
|
+
|
131
|
+
class PyTypeObject < FFI::Struct
|
132
|
+
include PyObjectMethods
|
133
|
+
|
134
|
+
def ===(obj)
|
135
|
+
obj.kind_of? self
|
136
|
+
end
|
137
|
+
|
138
|
+
def inspect
|
139
|
+
"pytype(#{self[:tp_name]})"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.getattr(pyobj, name, default=nil)
|
144
|
+
name = check_attr_name(name)
|
145
|
+
value = LibPython.PyObject_GetAttrString(pyobj, name)
|
146
|
+
if value.null?
|
147
|
+
return default if default
|
148
|
+
raise PyError.fetch
|
149
|
+
end
|
150
|
+
value.to_ruby
|
151
|
+
end
|
152
|
+
|
153
|
+
def self.setattr(pyobj, name, value)
|
154
|
+
name = check_attr_name(name)
|
155
|
+
value = Conversions.from_ruby(value)
|
156
|
+
return self unless LibPython.PyObject_SetAttrString(pyobj, name, value) == -1
|
157
|
+
raise PyError.fetch
|
158
|
+
end
|
159
|
+
|
160
|
+
def self.hasattr?(pyobj, name)
|
161
|
+
name = check_attr_name(name)
|
162
|
+
1 == LibPython.PyObject_HasAttrString(pyobj, name)
|
163
|
+
end
|
164
|
+
|
165
|
+
def self.check_attr_name(name)
|
166
|
+
return name.to_str if name.respond_to? :to_str
|
167
|
+
return name.to_s if name.kind_of? Symbol
|
168
|
+
raise TypeError, "attribute name must be a String or a Symbol: #{name.inspect}"
|
169
|
+
end
|
170
|
+
private_class_method :check_attr_name
|
171
|
+
|
172
|
+
def self.getitem(pyobj, key)
|
173
|
+
pykey = Conversions.from_ruby(key)
|
174
|
+
value = LibPython.PyObject_GetItem(pyobj, pykey)
|
175
|
+
return value.to_ruby unless value.null?
|
176
|
+
raise PyError.fetch
|
177
|
+
end
|
178
|
+
|
179
|
+
def self.setitem(pyobj, key, value)
|
180
|
+
pykey = Conversions.from_ruby(key)
|
181
|
+
value = Conversions.from_ruby(value)
|
182
|
+
return self unless LibPython.PyObject_SetItem(pyobj, pykey, value) == -1
|
183
|
+
raise PyError.fetch
|
184
|
+
end
|
185
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module PyCall
|
2
|
+
module PyObjectWrapper
|
3
|
+
def initialize(pyobj, pytype)
|
4
|
+
check_type pyobj, pytype
|
5
|
+
@__pyobj__ = pyobj
|
6
|
+
end
|
7
|
+
|
8
|
+
attr_reader :__pyobj__
|
9
|
+
|
10
|
+
def ==(other)
|
11
|
+
case other
|
12
|
+
when self.class
|
13
|
+
__pyobj__ == other.__pyobj__
|
14
|
+
when PyObject
|
15
|
+
__pyobj__ == other
|
16
|
+
else
|
17
|
+
super
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def call(*args, **kwargs)
|
22
|
+
__pyobj__.call(*args, **kwargs)
|
23
|
+
end
|
24
|
+
|
25
|
+
def method_missing(name, *args, **kwargs)
|
26
|
+
if PyCall.hasattr?(__pyobj__, name.to_s)
|
27
|
+
PyCall.getattr(__pyobj__, name)
|
28
|
+
else
|
29
|
+
super
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_s
|
34
|
+
__pyobj__.to_s
|
35
|
+
end
|
36
|
+
|
37
|
+
def inspect
|
38
|
+
__pyobj__.inspect
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def check_type(pyobj, pytype)
|
44
|
+
return if pyobj.kind_of?(PyObject) && pyobj.kind_of?(pytype)
|
45
|
+
raise TypeError, "the argument must be a PyObject of #{pytype}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
from distutils.sysconfig import get_config_var
|
2
|
+
import sys
|
3
|
+
for var in ('executable', 'exec_prefix', 'prefix'):
|
4
|
+
print(var + ': ' + getattr(sys, var))
|
5
|
+
for var in ('VERSION', 'LIBRARY', 'LDLIBRARY', 'LIBDIR', 'PYTHONFRAMEWORKPREFIX'):
|
6
|
+
print(var + ': ' + get_config_var(var))
|
data/lib/pycall/set.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module PyCall
|
2
|
+
class Set
|
3
|
+
include PyObjectWrapper
|
4
|
+
|
5
|
+
def initialize(pyobj)
|
6
|
+
super(pyobj, LibPython.PySet_Type)
|
7
|
+
end
|
8
|
+
|
9
|
+
def length
|
10
|
+
LibPython.PySet_Size(__pyobj__)
|
11
|
+
end
|
12
|
+
|
13
|
+
def include?(obj)
|
14
|
+
1 == LibPython.PySet_Contains(__pyobj__, Conversions.from_ruby(obj))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/pycall/slice.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module PyCall
|
2
|
+
class Slice
|
3
|
+
include PyObjectWrapper
|
4
|
+
|
5
|
+
def self.new(*args)
|
6
|
+
start, stop, step = nil
|
7
|
+
case args.length
|
8
|
+
when 1
|
9
|
+
stop = args[0]
|
10
|
+
return super(stop) if stop.kind_of?(PyObject)
|
11
|
+
when 2
|
12
|
+
start, stop = args
|
13
|
+
when 3
|
14
|
+
start, stop, step = args
|
15
|
+
else
|
16
|
+
much_or_few = args.length > 3 ? 'much' : 'few'
|
17
|
+
raise ArgumentError, "too #{much_or_few} arguments (#{args.length} for 1..3)"
|
18
|
+
end
|
19
|
+
start = start ? Conversions.from_ruby(start) : PyObject.null
|
20
|
+
stop = stop ? Conversions.from_ruby(stop) : PyObject.null
|
21
|
+
step = step ? Conversions.from_ruby(step) : PyObject.null
|
22
|
+
pyobj = LibPython.PySlice_New(start, stop, step)
|
23
|
+
return pyobj.to_ruby unless pyobj.null?
|
24
|
+
raise PyError.fetch
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/pycall/tuple.rb
ADDED
@@ -0,0 +1,54 @@
|
|
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 PyObject
|
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 initialize(pyobj)
|
26
|
+
super(pyobj, LibPython.PyTuple_Type)
|
27
|
+
end
|
28
|
+
|
29
|
+
def length
|
30
|
+
LibPython.PyTuple_Size(__pyobj__)
|
31
|
+
end
|
32
|
+
|
33
|
+
def [](index)
|
34
|
+
LibPython.PyTuple_GetItem(__pyobj__, index).to_ruby
|
35
|
+
end
|
36
|
+
|
37
|
+
def []=(index, value)
|
38
|
+
value = Conversions.from_ruby(value)
|
39
|
+
LibPython.PyTuple_SetItem(__pyobj__, index, value)
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_a
|
43
|
+
[].tap do |ary|
|
44
|
+
i, n = 0, length
|
45
|
+
while i < n
|
46
|
+
ary << self[i]
|
47
|
+
i += 1
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
alias to_ary to_a
|
53
|
+
end
|
54
|
+
end
|
data/lib/pycall/types.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
module PyCall
|
2
|
+
module Types
|
3
|
+
def self.pyisinstance(pyobj, pytype)
|
4
|
+
check_pyobject(pyobj)
|
5
|
+
pyobj_ptr = pyobj # TODO: fix after introducing PyObject class
|
6
|
+
LibPython.PyObject_IsInstance(pyobj_ptr, pytype) == 1
|
7
|
+
end
|
8
|
+
|
9
|
+
class << self
|
10
|
+
private def check_pyobject(pyobj)
|
11
|
+
# TODO: Check whether pyobj is PyObject
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|