pycall 0.1.0.alpha.20170502 → 0.1.0.alpha.20170711

Sign up to get free protection for your applications and to get access to all the features.
data/lib/pycall/init.rb CHANGED
@@ -11,10 +11,20 @@ module PyCall
11
11
  end
12
12
 
13
13
  @builtin = LibPython.PyImport_ImportModule(PYTHON_VERSION < '3.0.0' ? '__builtin__' : 'builtins').to_ruby
14
+
15
+ begin
16
+ import_module('stackless')
17
+ @has_stackless_extension = true
18
+ rescue PyError
19
+ @has_stackless_extension = false
20
+ end
21
+
22
+ __initialize_ruby_wrapper__
14
23
  end
15
24
 
16
25
  class << self
17
26
  attr_reader :builtin
27
+ attr_reader :has_stackless_extension
18
28
  end
19
29
 
20
30
  __initialize_pycall__
@@ -0,0 +1,30 @@
1
+ require 'ffi'
2
+
3
+ module PyCall
4
+ module LibPython
5
+ class PyObjectStruct < FFI::Struct
6
+ end
7
+
8
+ class PyTypeObjectStruct < PyObjectStruct
9
+ end
10
+
11
+ class PyObjectStruct < FFI::Struct
12
+ layout ob_refcnt: :ssize_t,
13
+ ob_type: PyTypeObjectStruct.by_ref
14
+
15
+ def self.null
16
+ new(FFI::Pointer::NULL)
17
+ end
18
+
19
+ def py_none?
20
+ PyCall.none?(self)
21
+ end
22
+
23
+ def kind_of?(klass)
24
+ klass = klass.__pyobj__ if klass.kind_of? PyObjectWrapper
25
+ return super unless klass.kind_of? PyObjectStruct
26
+ PyCall::Types.pyisinstance(self, klass)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,273 @@
1
+ require 'pycall/libpython/pyobject_struct'
2
+
3
+ module PyCall
4
+ module LibPython
5
+ # types:
6
+ T_SHORT = 0
7
+ T_INT = 1
8
+ T_LONG = 2
9
+ T_FLOAT = 3
10
+ T_DOUBLE = 4
11
+ T_STRING = 5
12
+ T_OBJECT = 6
13
+ T_CHAR = 7
14
+ T_BYTE = 8
15
+ T_UBYTE = 9
16
+ T_USHORT = 10
17
+ T_UINT = 11
18
+ T_ULONG = 12
19
+ T_STRING_INPLACE = 13
20
+ T_BOOL = 14
21
+ T_OBJECT_EX = 16
22
+ T_LONGLONG = 17 # added in Python 2.5
23
+ T_ULONGLONG = 18 # added in Python 2.5
24
+ T_PYSSIZET = 19 # added in Python 2.6
25
+ T_NONE = 20 # added in Python 3.0
26
+
27
+ # flags:
28
+ READONLY = 1
29
+ READ_RESTRICTED = 2
30
+ PY_WRITE_RESTRICTED = 4
31
+ RESTRICTED = (READ_RESTRICTED | PY_WRITE_RESTRICTED)
32
+
33
+ # Python 2.7
34
+ Py_TPFLAGS_HAVE_GETCHARBUFFER = 0x00000001<<0
35
+ Py_TPFLAGS_HAVE_SEQUENCE_IN = 0x00000001<<1
36
+ Py_TPFLAGS_GC = 0 # was sometimes (0x00000001<<2) in Python <= 2.1
37
+ Py_TPFLAGS_HAVE_INPLACEOPS = 0x00000001<<3
38
+ Py_TPFLAGS_CHECKTYPES = 0x00000001<<4
39
+ Py_TPFLAGS_HAVE_RICHCOMPARE = 0x00000001<<5
40
+ Py_TPFLAGS_HAVE_WEAKREFS = 0x00000001<<6
41
+ Py_TPFLAGS_HAVE_ITER = 0x00000001<<7
42
+ Py_TPFLAGS_HAVE_CLASS = 0x00000001<<8
43
+ Py_TPFLAGS_HAVE_INDEX = 0x00000001<<17
44
+ Py_TPFLAGS_HAVE_NEWBUFFER = 0x00000001<<21
45
+ Py_TPFLAGS_STRING_SUBCLASS = 0x00000001<<27
46
+
47
+ # Python 3.0+ has only these:
48
+ Py_TPFLAGS_HEAPTYPE = 0x00000001<<9
49
+ Py_TPFLAGS_BASETYPE = 0x00000001<<10
50
+ Py_TPFLAGS_READY = 0x00000001<<12
51
+ Py_TPFLAGS_READYING = 0x00000001<<13
52
+ Py_TPFLAGS_HAVE_GC = 0x00000001<<14
53
+ Py_TPFLAGS_HAVE_VERSION_TAG = 0x00000001<<18
54
+ Py_TPFLAGS_VALID_VERSION_TAG = 0x00000001<<19
55
+ Py_TPFLAGS_IS_ABSTRACT = 0x00000001<<20
56
+ Py_TPFLAGS_INT_SUBCLASS = 0x00000001<<23
57
+ Py_TPFLAGS_LONG_SUBCLASS = 0x00000001<<24
58
+ Py_TPFLAGS_LIST_SUBCLASS = 0x00000001<<25
59
+ Py_TPFLAGS_TUPLE_SUBCLASS = 0x00000001<<26
60
+ Py_TPFLAGS_BYTES_SUBCLASS = 0x00000001<<27
61
+ Py_TPFLAGS_UNICODE_SUBCLASS = 0x00000001<<28
62
+ Py_TPFLAGS_DICT_SUBCLASS = 0x00000001<<29
63
+ Py_TPFLAGS_BASE_EXC_SUBCLASS = 0x00000001<<30
64
+ Py_TPFLAGS_TYPE_SUBCLASS = 0x00000001<<31
65
+
66
+ # only use this if we have the stackless extension
67
+ Py_TPFLAGS_HAVE_STACKLESS_EXTENSION_ = 0x00000003<<15
68
+
69
+ class PyMethodDef < FFI::Struct
70
+ layout ml_name: :string,
71
+ ml_meth: :pointer,
72
+ ml_flags: :int,
73
+ ml_doc: :string # may be NULL
74
+
75
+ def initialize(*args)
76
+ case args.length
77
+ when 3, 4
78
+ name, meth, flags, doc = *args
79
+ super()
80
+ self.ml_name = name
81
+ self[:ml_meth] = meth
82
+ self[:ml_flags] = flags
83
+ self.ml_doc = doc
84
+ else
85
+ super
86
+ end
87
+ end
88
+
89
+ def ml_name=(str)
90
+ @saved_name = FFI::MemoryPointer.from_string(str || '')
91
+ self.pointer.put_pointer(offset_of(:ml_name), @saved_name)
92
+ end
93
+
94
+ def ml_doc=(str)
95
+ @saved_doc = FFI::MemoryPointer.from_string(str || '')
96
+ self.pointer.put_pointer(offset_of(:ml_name), @saved_doc)
97
+ end
98
+ end
99
+
100
+ # ml_flags should be one of:
101
+ METH_VARARGS = 0x0001 # args are a tuple of arguments
102
+ METH_KEYWORDS = 0x0002 # two arguments: the varargs and the kwargs
103
+ METH_NOARGS = 0x0004 # no arguments (NULL argument pointer)
104
+ METH_O = 0x0008 # single argument (not wrapped in tuple)
105
+
106
+ # not sure when these are needed:
107
+ METH_CLASS = 0x0010 # for class methods
108
+ METH_STATIC = 0x0020 # for static methods
109
+
110
+ class PyGetSetDef < FFI::Struct
111
+ layout name: :string,
112
+ get: :pointer,
113
+ set: :pointer, # may be NULL for read-only members
114
+ doc: :string,
115
+ closure: :pointer
116
+ end
117
+
118
+ class PyMemberDef < FFI::Struct
119
+ layout name: :string,
120
+ type: :int,
121
+ offset: :ssize_t,
122
+ flags: :int,
123
+ doc: :string
124
+
125
+ [:name, :doc].each do |field|
126
+ define_method(:"#{field}=") do |str|
127
+ saved_str = FFI::MemoryPointer.from_string(str)
128
+ instance_variable_set(:"@saved_#{field}", saved_str)
129
+ self.pointer.put_pointer(offset_of(field), saved_str)
130
+ end
131
+ end
132
+ end
133
+
134
+ class PyTypeObjectStruct < PyObjectStruct
135
+ layout ob_refcnt: :ssize_t,
136
+ ob_type: PyTypeObjectStruct.by_ref,
137
+ ob_size: :ssize_t,
138
+
139
+ tp_name: :string, # For printing, in format "<module>.<name>"
140
+
141
+ # For allocation
142
+ tp_basicsize: :ssize_t,
143
+ tp_itemsize: :ssize_t,
144
+
145
+ # Methods to implement standard operations
146
+
147
+ tp_dealloc: :pointer,
148
+ tp_print: :pointer,
149
+ tp_getattr: :pointer,
150
+ tp_setattr: :pointer,
151
+ tp_as_async: :pointer, # formerly known as tp_compare (Python 2) or tp_reserved (Python 3)
152
+ tp_repr: :pointer,
153
+
154
+ # Method suites for standard classes
155
+
156
+ tp_as_number: :pointer,
157
+ tp_as_sequence: :pointer,
158
+ tp_as_mapping: :pointer,
159
+
160
+ # More standard operations (here for binary compatibility)
161
+
162
+ tp_hash: :pointer,
163
+ tp_call: :pointer,
164
+ tp_str: :pointer,
165
+ tp_getattro: :pointer,
166
+ tp_setattro: :pointer,
167
+
168
+ # Functions to access object as input/output buffer
169
+ tp_as_buffer: :pointer,
170
+
171
+ # Flags to define presence of optional/expanded features
172
+ tp_flags: :ulong,
173
+
174
+ tp_doc: :string, # Documentation string
175
+
176
+ # Assigned meaning in release 2.0
177
+ # call function for all accessible objects
178
+ tp_traverse: :pointer,
179
+
180
+ # delete references to contained objects
181
+ tp_clear: :pointer,
182
+
183
+ # Assigned meaning in release 2.1
184
+ # rich comparisons
185
+ tp_richcompare: :pointer,
186
+
187
+ # weak reference enabler
188
+ tp_weaklistoffset: :ssize_t,
189
+
190
+ # Iterators
191
+ tp_iter: :pointer,
192
+ tp_iternext: :pointer,
193
+
194
+ # Attribute descriptor and subclassing stuff
195
+ tp_methods: PyMethodDef.by_ref,
196
+ tp_members: PyMemberDef.by_ref,
197
+ tp_getset: PyGetSetDef.by_ref,
198
+ tp_base: :pointer,
199
+ tp_dict: PyObjectStruct.by_ref,
200
+ tp_descr_get: :pointer,
201
+ tp_descr_set: :pointer,
202
+ tp_dictoffset: :ssize_t,
203
+ tp_init: :pointer,
204
+ tp_alloc: :pointer,
205
+ tp_new: :pointer,
206
+ tp_free: :pointer, # Low-level free-memory routine
207
+ tp_is_gc: :pointer, # For PyObject_IS_GC
208
+ tp_bases: PyObjectStruct.by_ref,
209
+ tp_mro: PyObjectStruct.by_ref, # method resolution order
210
+ tp_cache: PyObjectStruct.by_ref,
211
+ tp_subclasses: PyObjectStruct.by_ref,
212
+ tp_weaklist: PyObjectStruct.by_ref,
213
+ tp_del: :pointer,
214
+
215
+ # Type attribute cache version tag. Added in version 2.6
216
+ tp_version_tag: :uint,
217
+
218
+ tp_finalize: :pointer,
219
+
220
+ # The following members are only used for COUNT_ALLOCS builds of Python
221
+ tp_allocs: :ssize_t,
222
+ tp_frees: :ssize_t,
223
+ tp_maxalloc: :ssize_t,
224
+ tp_prev: :pointer,
225
+ tp_next: :pointer
226
+
227
+ def self.new(*args)
228
+ case args.length
229
+ when 0, 1
230
+ super
231
+ else
232
+ name, basic_size = *args
233
+ new.tap do |t|
234
+ # NOTE: Disable autorelease for avoiding SEGV occurrance in Python's GC collect function
235
+ # at which the __new__ method object of this type object is freed.
236
+ t.pointer.autorelease = false
237
+
238
+ # PyVarObject_HEAD_INIT(&PyType_Type, 0)
239
+ t[:ob_refcnt] = 1
240
+ t[:ob_type] = LibPython.PyType_Type
241
+ t[:ob_size] = 0
242
+
243
+ t[:tp_basicsize] = basic_size
244
+ stackless_extension_flag = PyCall.has_stackless_extension ? Py_TPFLAGS_HAVE_STACKLESS_EXTENSION_ : 0
245
+ t[:tp_flags] = if PYTHON_VERSION >= '3'
246
+ stackless_extension_flag | Py_TPFLAGS_HAVE_VERSION_TAG
247
+ else
248
+ Py_TPFLAGS_HAVE_GETCHARBUFFER |
249
+ Py_TPFLAGS_HAVE_SEQUENCE_IN |
250
+ Py_TPFLAGS_HAVE_INPLACEOPS |
251
+ Py_TPFLAGS_HAVE_RICHCOMPARE |
252
+ Py_TPFLAGS_HAVE_WEAKREFS |
253
+ Py_TPFLAGS_HAVE_ITER |
254
+ Py_TPFLAGS_HAVE_CLASS |
255
+ stackless_extension_flag |
256
+ Py_TPFLAGS_HAVE_INDEX
257
+ end
258
+ t.tp_name = name
259
+ yield t if block_given?
260
+ t[:tp_new] = LibPython.find_symbol(:PyType_GenericNew) if t[:tp_new] == FFI::Pointer::NULL
261
+ raise PyError.fetch if LibPython.PyType_Ready(t) < 0
262
+ LibPython.Py_IncRef(t)
263
+ end
264
+ end
265
+ end
266
+
267
+ def tp_name=(str)
268
+ @saved_name = FFI::MemoryPointer.from_string(str)
269
+ self.pointer.put_pointer(offset_of(:tp_name), @saved_name)
270
+ end
271
+ end
272
+ end
273
+ end
@@ -1,28 +1,11 @@
1
1
  require 'ffi'
2
+ require 'pycall/libpython/pyobject_struct'
3
+ require 'pycall/libpython/pytypeobject_struct'
2
4
 
3
5
  module PyCall
4
6
  module LibPython
5
7
  extend FFI::Library
6
8
 
7
- class PyObjectStruct < FFI::Struct
8
- layout ob_refcnt: :ssize_t,
9
- ob_type: PyObjectStruct.by_ref
10
-
11
- def self.null
12
- new(FFI::Pointer::NULL)
13
- end
14
-
15
- def py_none?
16
- PyCall.none?(self)
17
- end
18
-
19
- def kind_of?(klass)
20
- klass = klass.__pyobj__ if klass.kind_of? PyObjectWrapper
21
- return super unless klass.kind_of? PyObjectStruct
22
- PyCall::Types.pyisinstance(self, klass)
23
- end
24
- end
25
-
26
9
  private_class_method
27
10
 
28
11
  def self.find_libpython(python = nil)
@@ -151,6 +134,8 @@ module PyCall
151
134
  ffi_lib_flags :lazy, :global
152
135
  libpython = find_libpython ENV['PYTHON']
153
136
 
137
+ define_singleton_method(:find_symbol) {|name| libpython.find_symbol(name.to_s) }
138
+
154
139
  attach_function :Py_GetVersion, [], :string
155
140
  PYTHON_DESCRIPTION = LibPython.Py_GetVersion().freeze
156
141
  PYTHON_VERSION = PYTHON_DESCRIPTION.split(' ', 2)[0].freeze
@@ -171,37 +156,42 @@ module PyCall
171
156
  _Py_NoneStruct
172
157
  end
173
158
 
174
- attach_variable :PyType_Type, PyObjectStruct
159
+ attach_variable :PyType_Type, PyTypeObjectStruct
175
160
 
176
161
  if libpython.find_variable('PyInt_Type')
177
162
  has_PyInt_Type = true
178
- attach_variable :PyInt_Type, PyObjectStruct
163
+ attach_variable :PyInt_Type, PyTypeObjectStruct
179
164
  else
180
165
  has_PyInt_Type = false
181
- attach_variable :PyInt_Type, :PyLong_Type, PyObjectStruct
166
+ attach_variable :PyInt_Type, :PyLong_Type, PyTypeObjectStruct
182
167
  end
183
168
 
184
- attach_variable :PyLong_Type, PyObjectStruct
185
- attach_variable :PyBool_Type, PyObjectStruct
186
- attach_variable :PyFloat_Type, PyObjectStruct
187
- attach_variable :PyComplex_Type, PyObjectStruct
188
- attach_variable :PyUnicode_Type, PyObjectStruct
169
+ attach_variable :PyLong_Type, PyTypeObjectStruct
170
+ attach_variable :PyBool_Type, PyTypeObjectStruct
171
+ attach_variable :PyFloat_Type, PyTypeObjectStruct
172
+ attach_variable :PyComplex_Type, PyTypeObjectStruct
173
+ attach_variable :PyUnicode_Type, PyTypeObjectStruct
189
174
 
190
175
  if libpython.find_symbol('PyString_FromStringAndSize')
191
176
  string_as_bytes = false
192
- attach_variable :PyString_Type, PyObjectStruct
177
+ attach_variable :PyString_Type, PyTypeObjectStruct
193
178
  else
194
179
  string_as_bytes = true
195
- attach_variable :PyString_Type, :PyBytes_Type, PyObjectStruct
180
+ attach_variable :PyString_Type, :PyBytes_Type, PyTypeObjectStruct
196
181
  end
197
182
 
198
- attach_variable :PyList_Type, PyObjectStruct
199
- attach_variable :PyTuple_Type, PyObjectStruct
200
- attach_variable :PyDict_Type, PyObjectStruct
201
- attach_variable :PySet_Type, PyObjectStruct
183
+ attach_variable :PyList_Type, PyTypeObjectStruct
184
+ attach_variable :PyTuple_Type, PyTypeObjectStruct
185
+ attach_variable :PyDict_Type, PyTypeObjectStruct
186
+ attach_variable :PySet_Type, PyTypeObjectStruct
202
187
 
203
- attach_variable :PyFunction_Type, PyObjectStruct
204
- attach_variable :PyMethod_Type, PyObjectStruct
188
+ attach_variable :PyFunction_Type, PyTypeObjectStruct
189
+ attach_variable :PyMethod_Type, PyTypeObjectStruct
190
+
191
+ # --- exceptions ---
192
+
193
+ attach_variable :PyExc_RuntimeError, PyObjectStruct.ptr
194
+ attach_variable :PyExc_TypeError, PyObjectStruct.ptr
205
195
 
206
196
  # --- functions ---
207
197
 
@@ -216,6 +206,7 @@ module PyCall
216
206
 
217
207
  # Object
218
208
 
209
+ attach_function :_PyObject_New, [PyTypeObjectStruct.ptr], PyObjectStruct.ptr
219
210
  attach_function :PyObject_RichCompare, [PyObjectStruct.by_ref, PyObjectStruct.by_ref, :int], PyObjectStruct.by_ref
220
211
  attach_function :PyObject_GetAttrString, [PyObjectStruct.by_ref, :string], PyObjectStruct.by_ref
221
212
  attach_function :PyObject_SetAttrString, [PyObjectStruct.by_ref, :string, PyObjectStruct.by_ref], :int
@@ -231,6 +222,10 @@ module PyCall
231
222
  attach_function :PyObject_Type, [PyObjectStruct.by_ref], PyObjectStruct.by_ref
232
223
  attach_function :PyCallable_Check, [PyObjectStruct.by_ref], :int
233
224
 
225
+ # Type
226
+
227
+ attach_function :PyType_Ready, [PyTypeObjectStruct.ptr], :int
228
+
234
229
  # Bool
235
230
 
236
231
  attach_function :PyBool_FromLong, [:long], PyObjectStruct.by_ref
@@ -339,13 +334,23 @@ module PyCall
339
334
  attach_function :PySet_Size, [PyObjectStruct.by_ref], :ssize_t
340
335
  attach_function :PySet_Contains, [PyObjectStruct.by_ref, PyObjectStruct.by_ref], :int
341
336
 
337
+ # Method
338
+
339
+ attach_function :PyCFunction_NewEx, [PyMethodDef.ptr, :pointer, :pointer], PyObjectStruct.ptr
340
+
341
+ # Weakref
342
+
343
+ attach_function :PyWeakref_NewRef, [PyObjectStruct.ptr, PyObjectStruct.ptr], PyObjectStruct.ptr
344
+
342
345
  # Module
343
346
 
344
347
  attach_function :PyModule_GetDict, [PyObjectStruct.by_ref], PyObjectStruct.by_ref
345
348
 
346
349
  # Import
347
350
 
351
+ attach_function :PyImport_GetModuleDict, [], PyObjectStruct.by_ref
348
352
  attach_function :PyImport_ImportModule, [:string], PyObjectStruct.by_ref
353
+ attach_function :PyImport_ImportModuleLevel, [:string, PyObjectStruct.by_ref, PyObjectStruct.by_ref, PyObjectStruct.by_ref, :int], PyObjectStruct.by_ref
349
354
 
350
355
  # Operators
351
356
 
@@ -367,6 +372,7 @@ module PyCall
367
372
  attach_function :PyErr_Occurred, [], PyObjectStruct.by_ref
368
373
  attach_function :PyErr_Fetch, [:pointer, :pointer, :pointer], :void
369
374
  attach_function :PyErr_NormalizeException, [:pointer, :pointer, :pointer], :void
375
+ attach_function :PyErr_SetString, [PyObjectStruct.ptr, :string], :void
370
376
 
371
377
  public_class_method
372
378
  end
data/lib/pycall/list.rb CHANGED
@@ -32,6 +32,8 @@ module PyCall
32
32
  LibPython.PyList_Size(__pyobj__)
33
33
  end
34
34
 
35
+ alias length size
36
+
35
37
  def include?(value)
36
38
  value = Conversions.from_ruby(value)
37
39
  value = LibPython.PySequence_Contains(__pyobj__, value)
@@ -1,4 +1,6 @@
1
1
  module PyCall
2
+ HASH_SALT = "PyCall::PyObject".hash
3
+
2
4
  Py_LT = 0
3
5
  Py_LE = 1
4
6
  Py_EQ = 2
@@ -0,0 +1,137 @@
1
+ require 'pycall'
2
+
3
+ module PyCall
4
+ class RubyWrapStruct < LibPython::PyObjectStruct
5
+ layout ob_refcnt: :ssize_t,
6
+ ob_type: LibPython::PyTypeObjectStruct.by_ref,
7
+ rb_object_id: :ssize_t
8
+ end
9
+
10
+ # This will be called from __initialize_pycall__ defined in pycall/init.rb
11
+ private_class_method def self.__initialize_ruby_wrapper__()
12
+ @ruby_wrapper_members = FFI::MemoryPointer.new(LibPython::PyMemberDef.size, 2)
13
+ LibPython::PyMemberDef.new(@ruby_wrapper_members).tap do |member|
14
+ member.name = 'rb_object_id'
15
+ member[:type] = LibPython::T_PYSSIZET
16
+ member[:offset] = LibPython::PyObjectStruct.size
17
+ member[:flags] = RubyWrapStruct.offset_of(:rb_object_id)
18
+ member.doc = "Ruby object ID"
19
+ end
20
+ @ruby_wrapper_dealloc = FFI::Function.new(:void, [LibPython::PyObjectStruct.ptr]) do |pyptr|
21
+ GCGuard.unregister(pyptr)
22
+ nil
23
+ end
24
+ @ruby_wrapper_repr = FFI::Function.new(LibPython::PyObjectStruct.ptr, [LibPython::PyObjectStruct.ptr]) do |pyptr|
25
+ str = if pyptr.null?
26
+ '<PyCall.ruby_wrapper NULL>'
27
+ else
28
+ obj = ObjectSpace._id2ref(RubyWrapStruct.new(pyptr.pointer)[:rb_object_id])
29
+ "<PyCall.ruby_wrapper #{obj.inspect}>"
30
+ end
31
+ Conversions.from_ruby(str)
32
+ end
33
+ @ruby_wrapper_hash = FFI::Function.new(:uint64, [LibPython::PyObjectStruct.ptr]) do |pyptr|
34
+ h = ObjectSpace._id2ref(RubyWrapStruct.new(pyptr.pointer)[:rb_object_id]).hash
35
+ h == -1 ? PyCall::HASH_SALT : h
36
+ end
37
+ pysalt32 = 0xb592cd9b # This value comes from PyCall.jl
38
+ @ruby_wrapper_hash32 = FFI::Function.new(:uint32, [LibPython::PyObjectStruct.ptr]) do |pyptr|
39
+ # int64to32hash from src/support/hashing.c in julia
40
+ key = ObjectSpace._id2ref(RubyWrapStruct.new(pyptr.pointer)[:rb_object_id]).hash
41
+ key = (~key) + (key << 18)
42
+ key = key ^ (key >> 31)
43
+ key = key * 21
44
+ key = key ^ (key >> 11)
45
+ key = key + (key << 6)
46
+ key = key ^ (key >> 22)
47
+ h = 0xFFFFFFFF & key
48
+ h == -1 ? pysalt32 : h
49
+ end
50
+ @ruby_callable_call = FFI::Function.new(
51
+ LibPython::PyObjectStruct.ptr,
52
+ [LibPython::PyObjectStruct.ptr, LibPython::PyObjectStruct.ptr, LibPython::PyObjectStruct.ptr]
53
+ ) do |self_, args_, kwargs_|
54
+ obj = ObjectSpace._id2ref(RubyWrapStruct.new(self_.pointer)[:rb_object_id])
55
+ begin
56
+ args = Conversions.to_ruby(args_).to_ary
57
+ if kwargs_.null?
58
+ ret = obj.(*args)
59
+ else
60
+ kwargs = PyCall::Dict.new(kwargs_).to_hash
61
+ ret = obj.(*args, **kwargs)
62
+ end
63
+ Conversions.from_ruby(ret)
64
+ rescue Exception => err
65
+ PyCall.raise_python_exception(err)
66
+ LibPython::PyObjectStruct.null
67
+ end
68
+ end
69
+ @ruby_callable_getattr = FFI::Function.new(
70
+ LibPython::PyObjectStruct.ptr,
71
+ [LibPython::PyObjectStruct.ptr, LibPython::PyObjectStruct.ptr]
72
+ ) do |self_, attr_|
73
+ obj = ObjectSpace._id2ref(RubyWrapStruct.new(self_.pointer)[:rb_object_id])
74
+ attr = Conversions.to_ruby(attr_)
75
+ begin
76
+ case attr
77
+ when '__name__', 'func_name'
78
+ if obj.respond_to? :name
79
+ Conversions.from_ruby(obj.name)
80
+ else
81
+ Conversions.from_ruby(obj.to_s)
82
+ end
83
+ when '__doc__', 'func_doc'
84
+ # TODO: support docstring
85
+ PyCall.none
86
+ when '__module__', '__defaults__', 'func_defaults', '__closure__', 'func_closure'
87
+ PyCall.none
88
+ else
89
+ # TODO: handle __code__ and func_code
90
+ LibPython.PyObject_GenericGetAttr(self_, attr_)
91
+ end
92
+ rescue Exception => err
93
+ PyCall.raise_python_exception(err)
94
+ LibPython::PyObjectStruct.null
95
+ end
96
+ end
97
+ @ruby_wrapper = LibPython::PyTypeObjectStruct.new("PyCall.ruby_wrapper", RubyWrapStruct.size) do |t|
98
+ t[:tp_flags] |= LibPython::Py_TPFLAGS_BASETYPE
99
+ t[:tp_members] = LibPython::PyMemberDef.new(@ruby_wrapper_members)
100
+ t[:tp_dealloc] = @ruby_wrapper_dealloc
101
+ t[:tp_repr] = @ruby_wrapper_repr
102
+ if FFI.type_size(LibPython.find_type(:Py_hash_t)) < FFI.type_size(:uint64)
103
+ t[:tp_hash] = @ruby_wrapper_hash32
104
+ else
105
+ t[:tp_hash] = @ruby_wrapper_hash
106
+ end
107
+ end
108
+ @ruby_callable = PyCall.ruby_wrapper_subclass_new("PyCall.ruby_callable") do |t|
109
+ t[:tp_call] = @ruby_callable_call
110
+ t[:tp_getattro] = @ruby_callable_getattr
111
+ end
112
+ end
113
+
114
+ def self.ruby_wrapper_subclass_new(name)
115
+ LibPython::PyTypeObjectStruct.new(name, RubyWrapStruct.size + FFI.type_size(:pointer)) do |t|
116
+ t[:tp_base] = @ruby_wrapper.pointer
117
+ LibPython.Py_IncRef(LibPython::PyObjectStruct.new(@ruby_wrapper.pointer))
118
+ yield(t)
119
+ end
120
+ end
121
+
122
+ def self.ruby_wrapper_new(type, obj)
123
+ pyobj = LibPython._PyObject_New(type)
124
+ RubyWrapStruct.new(pyobj.pointer).tap do |rw|
125
+ rw[:rb_object_id] = obj.object_id
126
+ GCGuard.register(rw, obj)
127
+ end
128
+ end
129
+
130
+ def self.wrap_ruby_object(obj)
131
+ ruby_wrapper_new(@ruby_wrapper, obj)
132
+ end
133
+
134
+ def self.wrap_ruby_callable(obj)
135
+ ruby_wrapper_new(@ruby_callable, obj)
136
+ end
137
+ end
data/lib/pycall/set.rb CHANGED
@@ -6,10 +6,12 @@ module PyCall
6
6
  super(pyobj)
7
7
  end
8
8
 
9
- def length
9
+ def size
10
10
  LibPython.PySet_Size(__pyobj__)
11
11
  end
12
12
 
13
+ alias length size
14
+
13
15
  def include?(obj)
14
16
  1 == LibPython.PySet_Contains(__pyobj__, Conversions.from_ruby(obj))
15
17
  end
data/lib/pycall/tuple.rb CHANGED
@@ -22,10 +22,12 @@ module PyCall
22
22
  new(ary)
23
23
  end
24
24
 
25
- def length
25
+ def size
26
26
  LibPython.PyTuple_Size(__pyobj__)
27
27
  end
28
28
 
29
+ alias length size
30
+
29
31
  def [](index)
30
32
  LibPython.PyTuple_GetItem(__pyobj__, index).to_ruby
31
33
  end
@@ -1,3 +1,3 @@
1
1
  module PyCall
2
- VERSION = "0.1.0.alpha.20170502"
2
+ VERSION = "0.1.0.alpha.20170711"
3
3
  end
data/lib/pycall.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  require "pycall/version"
2
2
  require "pycall/libpython"
3
+ require "pycall/gc_guard"
4
+ require "pycall/exception"
3
5
  require "pycall/pyobject_wrapper"
4
6
  require "pycall/pyobject"
5
7
  require "pycall/pyerror"
@@ -13,4 +15,5 @@ require "pycall/dict"
13
15
  require "pycall/set"
14
16
  require "pycall/slice"
15
17
  require "pycall/utils"
18
+ require "pycall/ruby_wrapper"
16
19
  require "pycall/init"
data/pycall.gemspec CHANGED
@@ -34,4 +34,5 @@ Gem::Specification.new do |spec|
34
34
  spec.add_development_dependency "rake", "~> 10.0"
35
35
  spec.add_development_dependency "rspec", "~> 3.0"
36
36
  spec.add_development_dependency "launchy"
37
+ spec.add_development_dependency "pry"
37
38
  end
data/tasks/docker.rake CHANGED
@@ -5,6 +5,7 @@ namespace :docker do
5
5
  end
6
6
  end
7
7
 
8
+ desc 'Run docker container [port=8888] [attach_local=$(pwd)]'
8
9
  task :run do
9
10
  require 'securerandom'
10
11
  require 'launchy'