pycall 0.1.0.alpha.20170711 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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/exception.rb
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
require 'pycall'
|
2
|
-
|
3
|
-
module PyCall
|
4
|
-
@exceptions = {
|
5
|
-
Exception => LibPython.PyExc_RuntimeError,
|
6
|
-
TypeError => LibPython.PyExc_TypeError,
|
7
|
-
}.freeze
|
8
|
-
|
9
|
-
def self.raise_python_exception(exception)
|
10
|
-
pyexc = @exceptions[exception.class] || @exceptions[Exception]
|
11
|
-
LibPython.PyErr_SetString(pyexc, "Ruby exception: #{exception.class}: #{exception.message}")
|
12
|
-
end
|
13
|
-
end
|
data/lib/pycall/pyobject.rb
DELETED
@@ -1,58 +0,0 @@
|
|
1
|
-
module PyCall
|
2
|
-
class PyObject
|
3
|
-
include PyObjectWrapper
|
4
|
-
|
5
|
-
def self.null
|
6
|
-
new(LibPython::PyObjectStruct.new(FFI::Pointer::NULL))
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
def self.getattr(pyobj, name, default=nil)
|
11
|
-
name = check_attr_name(name)
|
12
|
-
pyobj = pyobj.__pyobj__ unless pyobj.kind_of? LibPython::PyObjectStruct
|
13
|
-
value = LibPython.PyObject_GetAttrString(pyobj, name)
|
14
|
-
if value.null?
|
15
|
-
return default if default
|
16
|
-
raise PyError.fetch
|
17
|
-
end
|
18
|
-
value.to_ruby
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.setattr(pyobj, name, value)
|
22
|
-
name = check_attr_name(name)
|
23
|
-
value = Conversions.from_ruby(value)
|
24
|
-
value = value.__pyobj__ unless pyobj.kind_of? LibPython::PyObjectStruct
|
25
|
-
pyobj = pyobj.__pyobj__ unless pyobj.kind_of? LibPython::PyObjectStruct
|
26
|
-
return self unless LibPython.PyObject_SetAttrString(pyobj, name, value) == -1
|
27
|
-
raise PyError.fetch
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.hasattr?(pyobj, name)
|
31
|
-
name = check_attr_name(name)
|
32
|
-
pyobj = pyobj.__pyobj__ unless pyobj.kind_of? LibPython::PyObjectStruct
|
33
|
-
1 == LibPython.PyObject_HasAttrString(pyobj, name)
|
34
|
-
end
|
35
|
-
|
36
|
-
def self.check_attr_name(name)
|
37
|
-
return name.to_str if name.respond_to? :to_str
|
38
|
-
return name.to_s if name.kind_of? Symbol
|
39
|
-
raise TypeError, "attribute name must be a String or a Symbol: #{name.inspect}"
|
40
|
-
end
|
41
|
-
private_class_method :check_attr_name
|
42
|
-
|
43
|
-
def self.getitem(pyobj, key)
|
44
|
-
pyobj = pyobj.__pyobj__ unless pyobj.kind_of? LibPython::PyObjectStruct
|
45
|
-
pykey = Conversions.from_ruby(key)
|
46
|
-
value = LibPython.PyObject_GetItem(pyobj, pykey)
|
47
|
-
return value.to_ruby unless value.null?
|
48
|
-
raise PyError.fetch
|
49
|
-
end
|
50
|
-
|
51
|
-
def self.setitem(pyobj, key, value)
|
52
|
-
pyobj = pyobj.__pyobj__ unless pyobj.kind_of? LibPython::PyObjectStruct
|
53
|
-
pykey = Conversions.from_ruby(key)
|
54
|
-
value = Conversions.from_ruby(value)
|
55
|
-
return self unless LibPython.PyObject_SetItem(pyobj, pykey, value) == -1
|
56
|
-
raise PyError.fetch
|
57
|
-
end
|
58
|
-
end
|
data/lib/pycall/ruby_wrapper.rb
DELETED
@@ -1,137 +0,0 @@
|
|
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/type_object.rb
DELETED
data/lib/pycall/types.rb
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
module PyCall
|
2
|
-
module Types
|
3
|
-
def self.pyisinstance(pyobj, pytype)
|
4
|
-
pyobj = LibPython::PyObjectStruct.new(pyobj) if pyobj.kind_of? FFI::Pointer
|
5
|
-
pyobj = pyobj.__pyobj__ unless pyobj.kind_of? LibPython::PyObjectStruct
|
6
|
-
|
7
|
-
pytype = LibPython::PyObjectStruct.new(pytype) if pytype.kind_of? FFI::Pointer
|
8
|
-
pytype = ptype.__pyobj__ unless pytype.kind_of? LibPython::PyObjectStruct
|
9
|
-
|
10
|
-
LibPython.PyObject_IsInstance(pyobj, pytype) == 1
|
11
|
-
end
|
12
|
-
|
13
|
-
class << self
|
14
|
-
private def check_pyobject(pyobj)
|
15
|
-
# TODO: Check whether pyobj is PyObject
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
data/lib/pycall/utils.rb
DELETED
@@ -1,106 +0,0 @@
|
|
1
|
-
module PyCall
|
2
|
-
module Utils
|
3
|
-
def append_sys_path(path_str)
|
4
|
-
pyobj = LibPython.PyUnicode_DecodeUTF8(path_str, path_str.bytesize, nil)
|
5
|
-
sys.path << pyobj
|
6
|
-
end
|
7
|
-
|
8
|
-
def callable?(pyobj)
|
9
|
-
unless pyobj.kind_of? LibPython::PyObjectStruct
|
10
|
-
raise TypeError, "the argument must be a Python object" unless pyobj.respond_to? :__pyobj__
|
11
|
-
pyobj = pyobj.__pyobj__
|
12
|
-
end
|
13
|
-
1 == LibPython.PyCallable_Check(pyobj)
|
14
|
-
end
|
15
|
-
|
16
|
-
def dir(pyobj)
|
17
|
-
pyobj = pyobj.__pyobj__ unless pyobj.kind_of? LibPython::PyObjectStruct
|
18
|
-
value = LibPython.PyObject_Dir(pyobj)
|
19
|
-
return value.to_ruby unless value.null?
|
20
|
-
raise PyError.fetch
|
21
|
-
end
|
22
|
-
|
23
|
-
def incref(pyobj)
|
24
|
-
LibPython.Py_IncRef(pyobj)
|
25
|
-
pyobj
|
26
|
-
end
|
27
|
-
|
28
|
-
def decref(pyobj)
|
29
|
-
LibPython.Py_DecRef(pyobj)
|
30
|
-
pyobj.send :pointer=, FFI::Pointer::NULL
|
31
|
-
pyobj
|
32
|
-
end
|
33
|
-
|
34
|
-
def int(pyobj)
|
35
|
-
@int ||= PyCall.eval('int')
|
36
|
-
@int.(pyobj)
|
37
|
-
end
|
38
|
-
|
39
|
-
def len(pyobj)
|
40
|
-
@len ||= PyCall.eval('len')
|
41
|
-
@len.(pyobj)
|
42
|
-
end
|
43
|
-
|
44
|
-
def None
|
45
|
-
LibPython.Py_None
|
46
|
-
end
|
47
|
-
|
48
|
-
def none?(pyobj)
|
49
|
-
case pyobj
|
50
|
-
when FFI::Pointer
|
51
|
-
ptr = pyobj
|
52
|
-
when LibPython::PyObjectStruct
|
53
|
-
ptr = pyobj.to_ptr
|
54
|
-
else
|
55
|
-
pyobj = pyobj.__pyobj__.to_ptr
|
56
|
-
end
|
57
|
-
ptr == self.None.to_ptr
|
58
|
-
end
|
59
|
-
|
60
|
-
def slice(*args)
|
61
|
-
Slice.new(*args)
|
62
|
-
end
|
63
|
-
|
64
|
-
def str(pyobj)
|
65
|
-
@str ||= PyCall.eval('str')
|
66
|
-
@str.(pyobj)
|
67
|
-
end
|
68
|
-
|
69
|
-
def sys
|
70
|
-
@sys ||= PyCall.import_module('sys')
|
71
|
-
end
|
72
|
-
|
73
|
-
def tuple(*args)
|
74
|
-
PyCall::Tuple[*args]
|
75
|
-
end
|
76
|
-
|
77
|
-
def type(pyobj)
|
78
|
-
@type ||= PyCall.eval('type')
|
79
|
-
@type.(pyobj)
|
80
|
-
end
|
81
|
-
|
82
|
-
def with(ctx)
|
83
|
-
__exit__ = PyCall.getattr(ctx, :__exit__)
|
84
|
-
begin
|
85
|
-
yield PyCall.getattr(ctx,:__enter__).()
|
86
|
-
rescue Exception => err
|
87
|
-
if err.kind_of? PyError
|
88
|
-
exit_value = __exit__.(err.type, err.value, err.traceback)
|
89
|
-
else
|
90
|
-
# TODO: support telling what exception has been catched
|
91
|
-
exit_value = __exit__.(PyCall.None, PyCall.None, PyCall.None)
|
92
|
-
end
|
93
|
-
raise err unless exit_value.equal? true
|
94
|
-
else
|
95
|
-
__exit__.(PyCall.None, PyCall.None, PyCall.None)
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
def format_traceback(pyobj)
|
100
|
-
@format_tb ||= import_module('traceback').format_tb
|
101
|
-
@format_tb.(pyobj)
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
extend Utils
|
106
|
-
end
|