pycall 0.1.0.alpha → 0.1.0.alpha.20170224
Sign up to get free protection for your applications and to get access to all the features.
- 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
|