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.
@@ -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))
@@ -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
@@ -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
@@ -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
@@ -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