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.
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
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,23 @@
1
+ import sys
2
+
3
+ PY3 = sys.version_info[0] == 3
4
+
5
+ if PY3:
6
+ import builtins
7
+ else:
8
+ import __builtin__ as builtins
9
+
10
+ if PY3:
11
+ exec_ = getattr(builtins, 'exec')
12
+ else:
13
+ def exec_(_code_, _globals_=None, _locals_=None):
14
+ """Execute code in a namespace."""
15
+ if _globals_ is None:
16
+ frame = sys._getframe(1)
17
+ _globals_ = frame.f_globals
18
+ if _locals_ is None:
19
+ _locals_ = frame.f_locals
20
+ del frame
21
+ elif _locals_ is None:
22
+ _locals_ = _globals_
23
+ exec("""exec _code_ in _globals_, _locals_""")
@@ -0,0 +1,79 @@
1
+ require 'pycall/pyobject_wrapper'
2
+
3
+ module PyCall
4
+ module PyTypeObjectWrapper
5
+ include PyObjectWrapper
6
+
7
+ def self.extend_object(cls)
8
+ unless cls.kind_of? Class
9
+ raise TypeError, "PyTypeObjectWrapper cannot extend non-class objects"
10
+ end
11
+ pyptr = cls.instance_variable_get(:@__pyptr__)
12
+ unless pyptr.kind_of? PyTypePtr
13
+ raise TypeError, "@__pyptr__ should have PyCall::PyTypePtr object"
14
+ end
15
+ super
16
+ cls.include PyObjectWrapper
17
+ end
18
+
19
+ def inherited(subclass)
20
+ subclass.instance_variable_set(:@__pyptr__, __pyptr__)
21
+ end
22
+
23
+ def new(*args)
24
+ wrap_pyptr(LibPython::Helpers.call_object(__pyptr__, *args))
25
+ end
26
+
27
+ def wrap_pyptr(pyptr)
28
+ return pyptr if pyptr.kind_of? self
29
+ pyptr = pyptr.__pyptr__ if pyptr.kind_of? PyObjectWrapper
30
+ unless pyptr.kind_of? PyPtr
31
+ raise TypeError, "unexpected argument type #{pyptr.class} (expected PyCall::PyPtr)"
32
+ end
33
+ unless pyptr.kind_of? __pyptr__
34
+ raise TypeError, "unexpected argument Python type #{pyptr.__ob_type__.__tp_name__} (expected #{__pyptr__.__tp_name__})"
35
+ end
36
+ allocate.tap do |obj|
37
+ obj.instance_variable_set(:@__pyptr__, pyptr)
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def register_python_type_mapping
44
+ PyCall::Conversion.register_python_type_mapping(__pyptr__, self)
45
+ end
46
+ end
47
+
48
+ module_function
49
+
50
+ class WrapperClassCache < WrapperObjectCache
51
+ def initialize
52
+ types = [LibPython::API::PyType_Type]
53
+ types << LibPython::API::PyClass_Type if defined? LibPython::API::PyClass_Type
54
+ super(*types)
55
+ end
56
+
57
+ def check_wrapper_object(wrapper_object)
58
+ unless wrapper_object.kind_of?(Class) && wrapper_object.kind_of?(PyTypeObjectWrapper)
59
+ raise TypeError, "unexpected type #{wrapper_object.class} (expected Class extended by PyTypeObjectWrapper)"
60
+ end
61
+ end
62
+
63
+ def self.instance
64
+ @instance ||= self.new
65
+ end
66
+ end
67
+
68
+ private_constant :WrapperClassCache
69
+
70
+ def wrap_class(pytypeptr)
71
+ check_isclass(pytypeptr)
72
+ WrapperClassCache.instance.lookup(pytypeptr) do
73
+ Class.new do |cls|
74
+ cls.instance_variable_set(:@__pyptr__, pytypeptr)
75
+ cls.extend PyTypeObjectWrapper
76
+ end
77
+ end
78
+ end
79
+ end
data/lib/pycall/slice.rb CHANGED
@@ -1,27 +1,8 @@
1
1
  module PyCall
2
+ Slice = builtins.slice
2
3
  class Slice
3
- include PyObjectWrapper
4
-
5
- def self.new(*args)
6
- start, stop, step = nil
7
- case args.length
8
- when 1
9
- stop = args[0]
10
- return super(stop) if stop.kind_of?(LibPython::PyObjectStruct)
11
- when 2
12
- start, stop = args
13
- when 3
14
- start, stop, step = args
15
- else
16
- much_or_few = args.length > 3 ? 'much' : 'few'
17
- raise ArgumentError, "too #{much_or_few} arguments (#{args.length} for 1..3)"
18
- end
19
- start = start ? Conversions.from_ruby(start) : LibPython::PyObjectStruct.null
20
- stop = stop ? Conversions.from_ruby(stop) : LibPython::PyObjectStruct.null
21
- step = step ? Conversions.from_ruby(step) : LibPython::PyObjectStruct.null
22
- pyobj = LibPython.PySlice_New(start, stop, step)
23
- return pyobj.to_ruby unless pyobj.null?
24
- raise PyError.fetch
4
+ def self.all
5
+ new(nil)
25
6
  end
26
7
  end
27
8
  end
data/lib/pycall/tuple.rb CHANGED
@@ -38,13 +38,7 @@ module PyCall
38
38
  end
39
39
 
40
40
  def to_a
41
- [].tap do |ary|
42
- i, n = 0, length
43
- while i < n
44
- ary << self[i]
45
- i += 1
46
- end
47
- end
41
+ Array.new(length) {|i| self[i] }
48
42
  end
49
43
 
50
44
  alias to_ary to_a
@@ -1,3 +1,3 @@
1
1
  module PyCall
2
- VERSION = "0.1.0.alpha.20170711"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -0,0 +1,61 @@
1
+ module PyCall
2
+ class WrapperObjectCache
3
+ def initialize(*restricted_pytypes)
4
+ unless restricted_pytypes.empty?
5
+ restricted_pytypes.each do |pytype|
6
+ next if pytype.kind_of? PyTypePtr
7
+ raise TypeError, "unexpected type of object in the arguments (#{pytype.class} for PyCall::PyTypePtr)"
8
+ end
9
+ end
10
+ @restricted_pytypes = restricted_pytypes
11
+ @wrapper_object_table = {}
12
+ @wrapped_pyptr_table = {}
13
+ @weakref_table = {}
14
+ end
15
+
16
+ def lookup(pyptr)
17
+ # TODO: check pytypeptr type
18
+ unless pyptr.kind_of? PyPtr
19
+ raise TypeError, "unexpected argument type #{pyptr.class} (expected PyCall::PyPtr)"
20
+ end
21
+
22
+ unless @restricted_pytypes.empty?
23
+ unless @restricted_pytypes.any? {|pytype| pyptr.kind_of? pytype }
24
+ raise TypeError, "unexpected argument Python type #{pyptr.__ob_type__.__name__} (expected either of them in [#{@restricted_pytypes.map(&:__tp_name__).join(', ')}])"
25
+ end
26
+ end
27
+
28
+ wrapper_object_id = @wrapper_object_table[pyptr.__address__]
29
+ if wrapper_object_id
30
+ wrapper_object = ObjectSpace._id2ref(wrapper_object_id) rescue nil
31
+ return wrapper_object if wrapper_object
32
+ end
33
+
34
+ wrapper_object = yield(pyptr)
35
+ check_wrapper_object(wrapper_object)
36
+ register_wrapper_object(pyptr, wrapper_object)
37
+
38
+ wrapper_object
39
+ end
40
+
41
+ def check_wrapper_object(wrapper_object)
42
+ unless wrapper_object.kind_of?(PyObjectWrapper)
43
+ raise TypeError, "unexpected wrapper object (expected an object extended by PyObjectWrapper)"
44
+ end
45
+ end
46
+
47
+ def register_wrapper_object(pyptr, wrapper_object)
48
+ @wrapper_object_table[pyptr.__address__] = wrapper_object.__id__
49
+ @wrapped_pyptr_table[wrapper_object.__id__] = pyptr.__address__
50
+ ObjectSpace.define_finalizer(wrapper_object, &method(:unregister_wrapper_object))
51
+ # TODO: weakref
52
+ self
53
+ end
54
+
55
+ def unregister_wrapper_object(wrapper_object_id)
56
+ pyptr_addr = @wrapped_pyptr_table.delete(wrapper_object_id)
57
+ @wrapper_object_table.delete(pyptr_addr) if pyptr_addr
58
+ self
59
+ end
60
+ end
61
+ end
data/pycall.gemspec CHANGED
@@ -27,12 +27,14 @@ Gem::Specification.new do |spec|
27
27
  spec.bindir = "exe"
28
28
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
29
29
  spec.require_paths = ["lib"]
30
-
31
- spec.add_dependency "ffi"
30
+ spec.extensions = ["ext/pycall/extconf.rb"]
32
31
 
33
32
  spec.add_development_dependency "bundler", "~> 1.13"
34
33
  spec.add_development_dependency "rake", "~> 10.0"
34
+ spec.add_development_dependency "rake-compiler"
35
+ spec.add_development_dependency "rake-compiler-dock"
35
36
  spec.add_development_dependency "rspec", "~> 3.0"
36
37
  spec.add_development_dependency "launchy"
37
38
  spec.add_development_dependency "pry"
39
+ spec.add_development_dependency "pry-byebug"
38
40
  end
data/tasks/pycall.rake ADDED
@@ -0,0 +1,7 @@
1
+ namespace :pycall do
2
+ desc 'Show PYTHON_DESCRIPTION'
3
+ task :PYTHON_DESCRIPTION do
4
+ require 'pycall'
5
+ puts PyCall::PYTHON_DESCRIPTION
6
+ end
7
+ end
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pycall
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.alpha.20170711
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kenta Murata
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-07-11 00:00:00.000000000 Z
11
+ date: 2017-09-07 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: ffi
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: bundler
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -52,6 +38,34 @@ dependencies:
52
38
  - - "~>"
53
39
  - !ruby/object:Gem::Version
54
40
  version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake-compiler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake-compiler-dock
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: rspec
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -94,16 +108,32 @@ dependencies:
94
108
  - - ">="
95
109
  - !ruby/object:Gem::Version
96
110
  version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: pry-byebug
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
97
125
  description: pycall
98
126
  email:
99
127
  - mrkn@mrkn.jp
100
128
  executables: []
101
- extensions: []
129
+ extensions:
130
+ - ext/pycall/extconf.rb
102
131
  extra_rdoc_files: []
103
132
  files:
104
133
  - ".gitignore"
105
134
  - ".rspec"
106
135
  - ".travis.yml"
136
+ - CHANGES.md
107
137
  - Gemfile
108
138
  - LICENSE.txt
109
139
  - README.md
@@ -130,33 +160,41 @@ files:
130
160
  - examples/notebooks/xkcd_style.ipynb
131
161
  - examples/plot_forest_importances_faces.rb
132
162
  - examples/sum_benchmarking.rb
163
+ - ext/pycall/extconf.rb
164
+ - ext/pycall/gc.c
165
+ - ext/pycall/libpython.c
166
+ - ext/pycall/pycall.c
167
+ - ext/pycall/pycall_internal.h
168
+ - ext/pycall/range.c
169
+ - ext/pycall/ruby_wrapper.c
133
170
  - lib/pycall.rb
134
171
  - lib/pycall/conversion.rb
135
172
  - lib/pycall/dict.rb
136
- - lib/pycall/eval.rb
137
- - lib/pycall/exception.rb
173
+ - lib/pycall/error.rb
138
174
  - lib/pycall/gc_guard.rb
139
175
  - lib/pycall/import.rb
140
176
  - lib/pycall/init.rb
141
177
  - lib/pycall/iruby_helper.rb
142
178
  - lib/pycall/libpython.rb
179
+ - lib/pycall/libpython/finder.rb
143
180
  - lib/pycall/libpython/pyobject_struct.rb
144
181
  - lib/pycall/libpython/pytypeobject_struct.rb
145
182
  - lib/pycall/list.rb
183
+ - lib/pycall/pretty_print.rb
146
184
  - lib/pycall/pyerror.rb
147
- - lib/pycall/pyobject.rb
148
185
  - lib/pycall/pyobject_wrapper.rb
186
+ - lib/pycall/python/PyCall/__init__.py
187
+ - lib/pycall/python/PyCall/six.py
149
188
  - lib/pycall/python/investigator.py
150
- - lib/pycall/ruby_wrapper.rb
189
+ - lib/pycall/pytypeobject_wrapper.rb
151
190
  - lib/pycall/set.rb
152
191
  - lib/pycall/slice.rb
153
192
  - lib/pycall/tuple.rb
154
- - lib/pycall/type_object.rb
155
- - lib/pycall/types.rb
156
- - lib/pycall/utils.rb
157
193
  - lib/pycall/version.rb
194
+ - lib/pycall/wrapper_object_cache.rb
158
195
  - pycall.gemspec
159
196
  - tasks/docker.rake
197
+ - tasks/pycall.rake
160
198
  homepage: https://github.com/mrkn/pycall
161
199
  licenses:
162
200
  - MIT
@@ -172,12 +210,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
172
210
  version: '0'
173
211
  required_rubygems_version: !ruby/object:Gem::Requirement
174
212
  requirements:
175
- - - ">"
213
+ - - ">="
176
214
  - !ruby/object:Gem::Version
177
- version: 1.3.1
215
+ version: '0'
178
216
  requirements: []
179
217
  rubyforge_project:
180
- rubygems_version: 2.6.11
218
+ rubygems_version: 2.6.13
181
219
  signing_key:
182
220
  specification_version: 4
183
221
  summary: pycall
data/lib/pycall/eval.rb DELETED
@@ -1,57 +0,0 @@
1
- module PyCall
2
- module Eval
3
- Py_file_input = 257
4
- Py_eval_input = 258
5
-
6
- def self.input_type(sym)
7
- return Py_file_input if sym == :file
8
- return Py_eval_input if sym == :eval
9
- raise ArgumentError, "Unknown input_type for compile Python code"
10
- end
11
-
12
- def self.eval(str, filename: "pycall", input_type: :eval)
13
- input_type = self.input_type(input_type)
14
- globals_ptr = main_dict.__pyobj__
15
- locals_ptr = main_dict.__pyobj__
16
- defer_sigint do
17
- py_code_ptr = LibPython.Py_CompileString(str, filename, input_type)
18
- raise PyError.fetch if py_code_ptr.null?
19
- LibPython.PyEval_EvalCode(py_code_ptr, globals_ptr, locals_ptr)
20
- end
21
- end
22
-
23
- class << self
24
- private
25
-
26
- def main_dict
27
- @main_dict ||= PyCall.import_module("__main__") do |main_module|
28
- PyCall.incref(LibPython.PyModule_GetDict(main_module.__pyobj__)).to_ruby
29
- end
30
- end
31
-
32
- def defer_sigint
33
- # TODO: should be implemented
34
- yield
35
- end
36
- end
37
- end
38
-
39
- def self.import_module(name)
40
- name = name.to_s if name.kind_of? Symbol
41
- raise TypeError, "name must be a String" unless name.kind_of? String
42
- value = LibPython.PyImport_ImportModule(name)
43
- raise PyError.fetch if value.null?
44
- value = value.to_ruby
45
- return value unless block_given?
46
- begin
47
- yield value
48
- ensure
49
- PyCall.decref(value.__pyobj__)
50
- end
51
- end
52
-
53
- def self.eval(str, conversion: true, filename: "pycall", input_type: :eval)
54
- result = Eval.eval(str, filename: filename, input_type: input_type)
55
- conversion ? result.to_ruby : result
56
- end
57
- end