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.rb
CHANGED
@@ -1,19 +1,91 @@
|
|
1
|
-
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
1
|
+
module PyCall
|
2
|
+
require 'pycall/version'
|
3
|
+
require 'pycall/libpython'
|
4
|
+
require 'pycall/pyerror'
|
5
|
+
require 'pycall/pyobject_wrapper'
|
6
|
+
require 'pycall/pytypeobject_wrapper'
|
7
|
+
require 'pycall/init'
|
8
|
+
|
9
|
+
module_function
|
10
|
+
|
11
|
+
def builtins
|
12
|
+
@builtins ||= wrap_module(LibPython::API.builtins_module_ptr)
|
13
|
+
end
|
14
|
+
|
15
|
+
def callable?(obj)
|
16
|
+
case obj
|
17
|
+
when PyObjectWrapper
|
18
|
+
builtins.callable(obj.__pyptr__)
|
19
|
+
when PyPtr
|
20
|
+
builtins.callable(obj)
|
21
|
+
else
|
22
|
+
raise TypeError, "unexpected argument type #{obj.class} (expected PyCall::PyPtr or its wrapper)"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def dir(obj)
|
27
|
+
case obj
|
28
|
+
when PyObjectWrapper
|
29
|
+
builtins.dir(obj.__pyptr__)
|
30
|
+
when PyPtr
|
31
|
+
builtins.dir(obj)
|
32
|
+
else
|
33
|
+
raise TypeError, "unexpected argument type #{obj.class} (expected PyCall::PyPtr or its wrapper)"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def eval(expr, globals: nil, locals: nil)
|
38
|
+
globals ||= import_module(:__main__).__dict__
|
39
|
+
builtins.eval(expr, globals, locals)
|
40
|
+
end
|
41
|
+
|
42
|
+
def exec(code, globals: nil, locals: nil)
|
43
|
+
globals ||= import_module(:__main__).__dict__
|
44
|
+
if PYTHON_VERSION >= '3'
|
45
|
+
builtins.exec(code, globals, locals)
|
46
|
+
else
|
47
|
+
import_module('PyCall.six').exec_(code, globals, locals)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def import_module(name)
|
52
|
+
LibPython::Helpers.import_module(name)
|
53
|
+
end
|
54
|
+
|
55
|
+
def len(obj)
|
56
|
+
case obj
|
57
|
+
when PyObjectWrapper
|
58
|
+
builtins.len(obj.__pyptr__)
|
59
|
+
when PyPtr
|
60
|
+
builtins.len(obj)
|
61
|
+
else
|
62
|
+
raise TypeError, "unexpected argument type #{obj.class} (expected PyCall::PyPtr or its wrapper)"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def sys
|
67
|
+
@sys ||= import_module('sys')
|
68
|
+
end
|
69
|
+
|
70
|
+
def tuple(iterable=nil)
|
71
|
+
pyptr = if iterable
|
72
|
+
builtins.tuple.(iterable)
|
73
|
+
else
|
74
|
+
builtins.tuple.()
|
75
|
+
end
|
76
|
+
Tuple.wrap_pyptr(pyptr)
|
77
|
+
end
|
78
|
+
|
79
|
+
def with(ctx)
|
80
|
+
begin
|
81
|
+
yield ctx.__enter__
|
82
|
+
rescue PyError => err
|
83
|
+
raise err unless ctx.__exit__(err.type, err.value, err.traceback)
|
84
|
+
rescue Exception => err
|
85
|
+
# TODO: support telling what exception has been catched
|
86
|
+
raise err unless ctx.__exit__(err.class, err, err.backtrace_locations)
|
87
|
+
else
|
88
|
+
ctx.__exit__(nil, nil, nil)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
data/lib/pycall/dict.rb
CHANGED
@@ -1,102 +1,48 @@
|
|
1
1
|
module PyCall
|
2
|
+
Dict = builtins.dict
|
2
3
|
class Dict
|
3
|
-
|
4
|
+
register_python_type_mapping
|
4
5
|
|
5
|
-
|
6
|
-
case init
|
7
|
-
when LibPython::PyObjectStruct
|
8
|
-
super
|
9
|
-
when nil
|
10
|
-
new(LibPython.PyDict_New())
|
11
|
-
when Hash
|
12
|
-
new.tap do |dict|
|
13
|
-
init.each do |key, value|
|
14
|
-
dict[key] = value
|
15
|
-
end
|
16
|
-
end
|
17
|
-
else
|
18
|
-
raise TypeError, "the argument must be a PyObject or a Hash"
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def [](key)
|
23
|
-
key = key.to_s if key.is_a? Symbol
|
24
|
-
key = key.__pyobj__ if key.respond_to?(:__pyobj__)
|
25
|
-
value = if key.is_a? String
|
26
|
-
LibPython.PyDict_GetItemString(__pyobj__, key).to_ruby
|
27
|
-
else
|
28
|
-
LibPython.PyDict_GetItem(__pyobj__, key).to_ruby
|
29
|
-
end
|
30
|
-
ensure
|
31
|
-
case value
|
32
|
-
when LibPython::PyObjectStruct
|
33
|
-
PyCall.incref(value)
|
34
|
-
when PyObjectWrapper
|
35
|
-
PyCall.incref(value.__pyobj__)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def []=(key, value)
|
40
|
-
key = key.to_s if key.is_a? Symbol
|
41
|
-
key = key.__pyobj__ if key.respond_to?(:__pyobj__)
|
42
|
-
value = Conversions.from_ruby(value)
|
43
|
-
value = value.__pyobj__ unless value.kind_of? LibPython::PyObjectStruct
|
44
|
-
if key.is_a? String
|
45
|
-
LibPython.PyDict_SetItemString(__pyobj__, key, value)
|
46
|
-
else
|
47
|
-
LibPython.PyDict_SetItem(__pyobj__, key, value)
|
48
|
-
end
|
49
|
-
value
|
50
|
-
end
|
51
|
-
|
52
|
-
def delete(key)
|
53
|
-
key = key.to_s if key.is_a? Symbol
|
54
|
-
key = key.__pyobj__ if key.respond_to?(:__pyobj__)
|
55
|
-
if key.is_a? String
|
56
|
-
value = LibPython.PyDict_GetItemString(__pyobj__, key).to_ruby
|
57
|
-
LibPython.PyDict_DelItemString(__pyobj__, key)
|
58
|
-
else
|
59
|
-
value = LibPython.PyDict_GetItem(__pyobj__, key).to_ruby
|
60
|
-
LibPython.PyDict_DelItem(__pyobj__, key)
|
61
|
-
end
|
62
|
-
value
|
63
|
-
end
|
64
|
-
|
65
|
-
def size
|
66
|
-
LibPython.PyDict_Size(__pyobj__)
|
67
|
-
end
|
6
|
+
include Enumerable
|
68
7
|
|
69
|
-
|
70
|
-
|
71
|
-
def keys
|
72
|
-
LibPython.PyDict_Keys(__pyobj__).to_ruby
|
8
|
+
def self.new(h)
|
9
|
+
super(h, {})
|
73
10
|
end
|
74
11
|
|
75
|
-
def
|
76
|
-
|
12
|
+
def length
|
13
|
+
PyCall.len(self)
|
77
14
|
end
|
78
15
|
|
79
16
|
def has_key?(key)
|
80
|
-
|
81
|
-
value = LibPython.PyDict_Contains(__pyobj__, key)
|
82
|
-
raise PyError.fetch if value == -1
|
83
|
-
1 == value
|
17
|
+
LibPython::Helpers.dict_contains(__pyptr__, key)
|
84
18
|
end
|
85
19
|
|
86
|
-
|
87
|
-
|
20
|
+
alias include? has_key?
|
21
|
+
alias key? has_key?
|
22
|
+
alias member? has_key?
|
23
|
+
|
24
|
+
def [](key)
|
25
|
+
super
|
26
|
+
rescue PyError
|
27
|
+
nil
|
88
28
|
end
|
89
29
|
|
90
|
-
def
|
91
|
-
|
30
|
+
def delete(key)
|
31
|
+
v = self[key]
|
32
|
+
LibPython::Helpers.delitem(__pyptr__, key)
|
33
|
+
v
|
92
34
|
end
|
93
35
|
|
94
|
-
def
|
95
|
-
|
36
|
+
def each
|
37
|
+
return enum_for unless block_given?
|
38
|
+
LibPython::Helpers.dict_each(__pyptr__, &proc)
|
39
|
+
self
|
96
40
|
end
|
97
41
|
|
98
|
-
def
|
99
|
-
|
42
|
+
def to_h
|
43
|
+
inject({}) do |h, (k, v)|
|
44
|
+
h.update(k => v)
|
45
|
+
end
|
100
46
|
end
|
101
47
|
end
|
102
48
|
end
|
data/lib/pycall/error.rb
ADDED
data/lib/pycall/import.rb
CHANGED
@@ -2,57 +2,48 @@ require 'pycall'
|
|
2
2
|
|
3
3
|
module PyCall
|
4
4
|
module Import
|
5
|
-
|
6
|
-
|
5
|
+
class << self
|
6
|
+
attr_reader :main_object
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
main_object
|
12
|
-
PyCall::Import.class_eval { @main_object = main_object }
|
11
|
+
PyCall::Import.instance_variable_set(:@main_object, self)
|
13
12
|
|
14
13
|
module PyCall
|
15
14
|
module Import
|
16
15
|
def pyimport(mod_name, as: nil)
|
17
|
-
|
18
|
-
when nil
|
19
|
-
as = mod_name
|
20
|
-
end
|
21
|
-
|
16
|
+
as = mod_name unless as
|
22
17
|
check_valid_module_variable_name(mod_name, as)
|
23
|
-
|
24
18
|
mod = PyCall.import_module(mod_name)
|
25
|
-
raise PyError.fetch unless mod
|
26
|
-
|
27
19
|
define_singleton_method(as) { mod }
|
28
20
|
end
|
29
21
|
|
30
22
|
# This function is implemented as a mimic of `import_from` function defined in `Python/ceval.c`.
|
31
23
|
def pyfrom(mod_name, import: nil)
|
32
|
-
raise ArgumentError, "missing
|
24
|
+
raise ArgumentError, "missing identifier(s) to be imported" unless import
|
33
25
|
|
34
26
|
mod_name = mod_name.to_str if mod_name.respond_to? :to_str
|
35
27
|
mod_name = mod_name.to_s if mod_name.is_a? Symbol
|
36
28
|
|
37
29
|
import = Array(import)
|
38
|
-
fromlist =
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
30
|
+
fromlist = import.map.with_index do |import_name, i|
|
31
|
+
case import_name
|
32
|
+
when assoc_array_matcher
|
33
|
+
import_name[0]
|
34
|
+
when Symbol, String
|
35
|
+
import_name
|
36
|
+
else
|
37
|
+
raise ArgumentError, "wrong type of import name #{import_name.class} (expected String or Symbol)"
|
38
|
+
end
|
47
39
|
end
|
40
|
+
from_list = PyCall.tuple(from_list)
|
48
41
|
|
49
|
-
|
50
|
-
globals =
|
51
|
-
locals =
|
42
|
+
main_dict_ptr = PyCall.import_module('__main__').__dict__.__pyptr__
|
43
|
+
globals = main_dict_ptr # FIXME: this should mimic to `import_name` function defined in `Python/ceval.c`.
|
44
|
+
locals = main_dict_ptr # FIXME: this should mimic to `import_name` function defined in `Python/ceval.c`.
|
52
45
|
level = 0 # TODO: support prefixed dots (#25)
|
53
|
-
mod = LibPython.
|
54
|
-
raise PyError.fetch if mod.null?
|
55
|
-
mod = mod.to_ruby
|
46
|
+
mod = LibPython::Helpers.import_module(mod_name, globals, locals, fromlist, level)
|
56
47
|
|
57
48
|
import.each do |import_name|
|
58
49
|
case import_name
|
@@ -62,18 +53,18 @@ module PyCall
|
|
62
53
|
name, asname = import_name, import_name
|
63
54
|
end
|
64
55
|
|
65
|
-
if PyCall.hasattr?(mod, name)
|
66
|
-
pyobj = PyCall.getattr(mod, name)
|
56
|
+
if PyCall::LibPython::Helpers.hasattr?(mod.__pyptr__, name)
|
57
|
+
pyobj = PyCall::LibPython::Helpers.getattr(mod.__pyptr__, name)
|
67
58
|
define_name(asname, pyobj)
|
68
59
|
next
|
69
60
|
end
|
70
61
|
|
71
|
-
if
|
72
|
-
pkgname =
|
62
|
+
if mod.respond_to? :__name__
|
63
|
+
pkgname = mod.__name__
|
73
64
|
fullname = "#{pkgname}.#{name}"
|
74
|
-
|
75
|
-
if
|
76
|
-
pyobj =
|
65
|
+
sys_modules = PyCall.import_module('sys').modules
|
66
|
+
if sys_modules.has_key?(fullname)
|
67
|
+
pyobj = module_dict[fullname]
|
77
68
|
define_name(asname, pyobj)
|
78
69
|
next
|
79
70
|
end
|
@@ -86,12 +77,18 @@ module PyCall
|
|
86
77
|
private
|
87
78
|
|
88
79
|
def define_name(name, pyobj)
|
89
|
-
if
|
90
|
-
|
91
|
-
|
92
|
-
|
80
|
+
if callable?(pyobj) && !type_object?(pyobj)
|
81
|
+
define_singleton_method(name) do |*args|
|
82
|
+
LibPython::Helpers.call_object(pyobj.__pyptr__, *args)
|
83
|
+
end
|
93
84
|
else
|
94
|
-
|
85
|
+
if constant_name?(name)
|
86
|
+
context = self
|
87
|
+
context = (self == PyCall::Import.main_object) ? Object : self
|
88
|
+
context.module_eval { const_set(name, pyobj) }
|
89
|
+
else
|
90
|
+
define_singleton_method(name) { pyobj }
|
91
|
+
end
|
95
92
|
end
|
96
93
|
end
|
97
94
|
|
@@ -111,5 +108,13 @@ module PyCall
|
|
111
108
|
ary.is_a?(Array) && ary.length == 2
|
112
109
|
end
|
113
110
|
end
|
111
|
+
|
112
|
+
def callable?(pyobj)
|
113
|
+
LibPython::Helpers.callable?(pyobj.__pyptr__)
|
114
|
+
end
|
115
|
+
|
116
|
+
def type_object?(pyobj)
|
117
|
+
pyobj.__pyptr__.kind_of? PyTypePtr
|
118
|
+
end
|
114
119
|
end
|
115
120
|
end
|
data/lib/pycall/init.rb
CHANGED
@@ -1,31 +1,55 @@
|
|
1
1
|
module PyCall
|
2
|
-
|
3
|
-
|
4
|
-
|
2
|
+
def self.const_missing(name)
|
3
|
+
case name
|
4
|
+
when :PyPtr, :PyTypePtr, :PyObjectWrapper, :PYTHON_DESCRIPTION, :PYTHON_VERSION
|
5
|
+
PyCall.init
|
6
|
+
const_get(name)
|
7
|
+
else
|
8
|
+
super
|
9
|
+
end
|
10
|
+
end
|
5
11
|
|
6
|
-
|
12
|
+
module LibPython
|
13
|
+
def self.const_missing(name)
|
14
|
+
case name
|
15
|
+
when :API, :Conversion, :Helpers, :PYTHON_DESCRIPTION, :PYTHON_VERSION
|
16
|
+
PyCall.init
|
17
|
+
const_get(name)
|
18
|
+
else
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
7
23
|
|
8
|
-
|
9
|
-
|
10
|
-
|
24
|
+
def self.init(python = ENV['PYTHON'])
|
25
|
+
return false if LibPython.instance_variable_defined?(:@handle)
|
26
|
+
class << PyCall
|
27
|
+
remove_method :const_missing
|
28
|
+
end
|
29
|
+
class << PyCall::LibPython
|
30
|
+
remove_method :const_missing
|
11
31
|
end
|
12
32
|
|
13
|
-
|
33
|
+
ENV['PYTHONPATH'] = [ File.expand_path('../python', __FILE__), ENV['PYTHONPATH'] ].compact.join(File::PATH_SEPARATOR)
|
14
34
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
@has_stackless_extension = false
|
35
|
+
LibPython.instance_variable_set(:@handle, LibPython::Finder.find_libpython(python))
|
36
|
+
class << LibPython
|
37
|
+
undef_method :handle
|
38
|
+
attr_reader :handle
|
20
39
|
end
|
21
40
|
|
22
|
-
|
23
|
-
|
41
|
+
begin
|
42
|
+
major, minor, _ = RUBY_VERSION.split('.')
|
43
|
+
require "#{major}.#{minor}/pycall.so"
|
44
|
+
rescue LoadError
|
45
|
+
require 'pycall.so'
|
46
|
+
end
|
24
47
|
|
25
|
-
|
26
|
-
|
27
|
-
|
48
|
+
require 'pycall/dict'
|
49
|
+
require 'pycall/list'
|
50
|
+
require 'pycall/slice'
|
51
|
+
const_set(:PYTHON_VERSION, LibPython::PYTHON_VERSION)
|
52
|
+
const_set(:PYTHON_DESCRIPTION, LibPython::PYTHON_DESCRIPTION)
|
53
|
+
true
|
28
54
|
end
|
29
|
-
|
30
|
-
__initialize_pycall__
|
31
55
|
end
|