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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.travis.yml +13 -1
  4. data/CHANGES.md +35 -0
  5. data/Gemfile +0 -5
  6. data/README.md +41 -49
  7. data/Rakefile +22 -1
  8. data/appveyor.yml +9 -26
  9. data/examples/classifier_comparison.rb +52 -52
  10. data/examples/hist.rb +11 -11
  11. data/examples/notebooks/classifier_comparison.ipynb +51 -66
  12. data/examples/notebooks/forest_importances.ipynb +26 -49
  13. data/examples/notebooks/iruby_integration.ipynb +15 -36
  14. data/examples/notebooks/lorenz_attractor.ipynb +16 -47
  15. data/examples/notebooks/polar_axes.ipynb +29 -64
  16. data/examples/notebooks/sum_benchmarking.ipynb +109 -103
  17. data/examples/notebooks/xkcd_style.ipynb +12 -12
  18. data/examples/plot_forest_importances_faces.rb +8 -8
  19. data/examples/sum_benchmarking.rb +15 -19
  20. data/ext/pycall/extconf.rb +3 -0
  21. data/ext/pycall/gc.c +74 -0
  22. data/ext/pycall/libpython.c +217 -0
  23. data/ext/pycall/pycall.c +2184 -0
  24. data/ext/pycall/pycall_internal.h +700 -0
  25. data/ext/pycall/range.c +69 -0
  26. data/ext/pycall/ruby_wrapper.c +432 -0
  27. data/lib/pycall.rb +91 -19
  28. data/lib/pycall/dict.rb +28 -82
  29. data/lib/pycall/error.rb +10 -0
  30. data/lib/pycall/import.rb +45 -40
  31. data/lib/pycall/init.rb +44 -20
  32. data/lib/pycall/libpython.rb +6 -380
  33. data/lib/pycall/libpython/finder.rb +170 -0
  34. data/lib/pycall/list.rb +21 -51
  35. data/lib/pycall/pretty_print.rb +9 -0
  36. data/lib/pycall/pyerror.rb +14 -20
  37. data/lib/pycall/pyobject_wrapper.rb +157 -158
  38. data/lib/pycall/python/PyCall/__init__.py +1 -0
  39. data/lib/pycall/python/PyCall/six.py +23 -0
  40. data/lib/pycall/pytypeobject_wrapper.rb +79 -0
  41. data/lib/pycall/slice.rb +3 -22
  42. data/lib/pycall/tuple.rb +1 -7
  43. data/lib/pycall/version.rb +1 -1
  44. data/lib/pycall/wrapper_object_cache.rb +61 -0
  45. data/pycall.gemspec +4 -2
  46. data/tasks/pycall.rake +7 -0
  47. metadata +65 -27
  48. data/lib/pycall/eval.rb +0 -57
  49. data/lib/pycall/exception.rb +0 -13
  50. data/lib/pycall/pyobject.rb +0 -58
  51. data/lib/pycall/ruby_wrapper.rb +0 -137
  52. data/lib/pycall/type_object.rb +0 -11
  53. data/lib/pycall/types.rb +0 -19
  54. data/lib/pycall/utils.rb +0 -106
@@ -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
@@ -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
@@ -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
@@ -1,11 +0,0 @@
1
- module PyCall
2
- class TypeObject
3
- include PyObjectWrapper
4
-
5
- def to_s
6
- return "pytype(#{self.__name__})"
7
- end
8
-
9
- alias inspect to_s
10
- end
11
- end
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