pycall 0.1.0.alpha.20170711 → 1.0.0
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/.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
|