pybind 0.1.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.
@@ -0,0 +1,81 @@
1
+ module PyBind
2
+ module Utils
3
+ BUILTIN_FUNCS = %w[
4
+ getattr hasattr setattr delattr
5
+ id type dir len iter next
6
+ ]
7
+
8
+ BUILTIN_FUNCS.each do |func|
9
+ define_method(func) do |*args|
10
+ PyBind.builtin.get_attribute(func).call(*args)
11
+ end
12
+ end
13
+
14
+ MODULE_SHORTCUTS = %w[
15
+ types traceback
16
+ ]
17
+
18
+ MODULE_SHORTCUTS.each do |mod|
19
+ define_method(mod) do |*args|
20
+ PyBind.import(mod)
21
+ end
22
+ end
23
+
24
+ def None
25
+ LibPython.Py_None
26
+ end
27
+
28
+ def True
29
+ LibPython.Py_True
30
+ end
31
+
32
+ def False
33
+ LibPython.Py_False
34
+ end
35
+
36
+ def eval(str)
37
+ dict = main_dict
38
+ eval_func = PyBind.builtin.get_attribute('eval')
39
+ eval_func.call(str, dict, dict)
40
+ end
41
+
42
+ def execfile(filename)
43
+ dict = main_dict
44
+ if PyBind.builtin.has_attribute?('execfile')
45
+ execfile_func = PyBind.builtin.get_attribute('execfile')
46
+ execfile_func.call(filename, dict, dict)
47
+ else
48
+ open_func = PyBind.builtin.get_attribute('open')
49
+ exec_func = PyBind.builtin.get_attribute('exec')
50
+ content = open_func.call(filename).get_attribute('read').call()
51
+ exec_func.(content, dict, dict)
52
+ end
53
+ end
54
+
55
+ def callable?(pyobj)
56
+ pystruct = pyobj.to_python_struct
57
+ LibPython.PyCallable_Check(pystruct) == 1
58
+ end
59
+
60
+ def incref(pyobj)
61
+ pystruct = pyobj.to_python_struct
62
+ LibPython.Py_IncRef(pystruct)
63
+ pyobj
64
+ end
65
+
66
+ def decref(pyobj)
67
+ pystruct = pyobj.to_python_struct
68
+ LibPython.Py_DecRef(pystruct)
69
+ pystruct.send :pointer=, FFI::Pointer::NULL
70
+ pyobj
71
+ end
72
+
73
+ private
74
+
75
+ def main_dict
76
+ LibPython.PyModule_GetDict(PyBind.import("__main__").to_python_struct).to_ruby
77
+ end
78
+ end
79
+
80
+ extend Utils
81
+ end
@@ -0,0 +1,3 @@
1
+ module PyBind
2
+ VERSION = '0.1.0'.freeze
3
+ end
@@ -0,0 +1,120 @@
1
+ require 'pybind/wrapper/attr_accessor'
2
+ require 'pybind/wrapper/rich_comparer'
3
+ require 'pybind/wrapper/operator'
4
+
5
+ module PyBind
6
+ module PyObjectClassMethods
7
+ def pybind_type(pytype, &block)
8
+ raise ArgumentError, "#{self} is already bound with #{@pystruct}" if @pystruct
9
+ define_singleton_method :from_python, &block if block
10
+ @pystruct = pytype.to_python_struct
11
+ end
12
+
13
+ def python_instance?(pyobj)
14
+ return false unless @pystruct
15
+ pystruct = pyobj.to_python_struct
16
+ value = LibPython.PyObject_IsInstance(pystruct, @pystruct)
17
+ raise PyError.fetch if value == -1
18
+ value == 1
19
+ end
20
+
21
+ def python_subclass?(pyobj)
22
+ return false unless @pystruct
23
+ pystruct = pyobj.to_python_struct
24
+ value = LibPython.PyObject_IsSubclass(pystruct, @pystruct)
25
+ raise PyError.fetch if value == -1
26
+ value == 1
27
+ end
28
+
29
+ def from_python(pystruct)
30
+ new(pystruct)
31
+ end
32
+
33
+ def to_python_struct
34
+ @pystruct
35
+ end
36
+ alias_method :python_type, :to_python_struct
37
+ alias_method :to_python, :to_python_struct
38
+ end
39
+
40
+ module PyClassWrapper
41
+ def self.included(mod)
42
+ mod.extend(PyObjectClassMethods)
43
+ Types.register_type(mod)
44
+ end
45
+ end
46
+
47
+ module PyObjectWrapper
48
+ include AttrAccessor
49
+ include RichComparer
50
+ include Operator
51
+
52
+ def initialize(pystruct)
53
+ raise TypeError, "the argument must be a PyObjectStruct" unless pystruct.kind_of? PyObjectStruct
54
+ @pystruct = pystruct
55
+ end
56
+
57
+ def call(*args, **kwargs)
58
+ args = PyTuple[*args]
59
+ kwargs = kwargs.empty? ? PyObject.null : PyDict.new(kwargs)
60
+ res = LibPython.PyObject_Call(@pystruct, args.to_python_struct, kwargs.to_python_struct)
61
+ raise PyError.fetch unless LibPython.PyErr_Occurred().null?
62
+ res.to_ruby
63
+ end
64
+
65
+ def to_s
66
+ str = LibPython.PyObject_Str(@pystruct)
67
+ return str.to_ruby unless str.null?
68
+ super
69
+ end
70
+
71
+ def inspect
72
+ str = LibPython.PyObject_Repr(@pystruct)
73
+ return "#<#{self.class.name || python_type} #{str.to_ruby}>" unless str.null?
74
+ super
75
+ end
76
+
77
+ def methods
78
+ LibPython.PyObject_Dir(@pystruct).to_ruby.map &:to_sym
79
+ end
80
+
81
+ extend Forwardable
82
+ def_delegators :@pystruct, :null?, :none?
83
+
84
+ def python_type
85
+ LibPython.PyObject_Type(@pystruct).to_ruby
86
+ end
87
+
88
+ def to_python_struct
89
+ @pystruct
90
+ end
91
+ alias_method :to_python, :to_python_struct
92
+
93
+ def self.included(mod)
94
+ mod.extend(PyObjectClassMethods)
95
+ Types.register_type(mod)
96
+ end
97
+
98
+ private
99
+
100
+ def method_missing(name, *args, **kwargs)
101
+ attr_name = name.to_s
102
+ is_setter = attr_name.end_with?("=")
103
+ attr_name = attr_name.chomp('=') if is_setter
104
+
105
+ if has_attribute?(attr_name)
106
+ if is_setter
107
+ set_attribute(attr_name, *args)
108
+ else
109
+ autocall_method_missing(get_attribute(attr_name), *args, **kwargs)
110
+ end
111
+ else
112
+ super
113
+ end
114
+ end
115
+
116
+ def autocall_method_missing(value, *args, **kwargs)
117
+ value
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,53 @@
1
+ module PyBind
2
+ module AttrAccessor
3
+ def get_attribute(name, default = nil)
4
+ value = LibPython.PyObject_GetAttrString(@pystruct, name.to_s)
5
+ if value.null?
6
+ raise PyError.fetch unless default
7
+ return default
8
+ end
9
+ value.to_ruby
10
+ end
11
+
12
+ def set_attribute(name, value)
13
+ value = value.to_python
14
+ ret = LibPython.PyObject_SetAttrString(@pystruct, name.to_s, value)
15
+ raise PyError.fetch if ret == -1
16
+ self
17
+ end
18
+
19
+ def remove_attribute(name)
20
+ value = LibPython.PyObject_GetAttrString(@pystruct, name.to_s)
21
+ raise PyError.fetch if value.null?
22
+ ret = if LibPython.respond_to? :PyObject_DelAttrString
23
+ LibPython.PyObject_DelAttrString(@pystruct, name.to_s)
24
+ else
25
+ LibPython.PyObject_SetAttrString(@pystruct, name.to_s, PyBind.None)
26
+ end
27
+ raise PyError.fetch if ret == -1
28
+ value.to_ruby
29
+ end
30
+
31
+ def has_attribute?(name)
32
+ LibPython.PyObject_HasAttrString(@pystruct, name.to_s) == 1
33
+ end
34
+
35
+ def [](*indices)
36
+ key = TypeCast.to_python_arguments(indices)
37
+ value = LibPython.PyObject_GetItem(@pystruct, key)
38
+ raise PyError.fetch if value.null?
39
+ value.to_ruby
40
+ end
41
+
42
+ def []=(*indices_and_value)
43
+ value = indices_and_value.pop
44
+ indices = indices_and_value
45
+ key = TypeCast.to_python_arguments(indices)
46
+ value = value.to_python
47
+ ret = LibPython.PyObject_SetItem(@pystruct, pykey, value)
48
+ raise PyError.fetch if ret == -1
49
+ self
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,82 @@
1
+ module PyBind
2
+ module Operator
3
+ BINARY_OPERATION_OPFUNCS = {
4
+ :+ => :PyNumber_Add,
5
+ :- => :PyNumber_Subtract,
6
+ :* => :PyNumber_Multiply,
7
+ :/ => :PyNumber_TrueDivide,
8
+ :% => :PyNumber_Remainder,
9
+ :<< => :PyNumber_Lshift,
10
+ :>> => :PyNumber_Rshift,
11
+ :& => :PyNumber_And,
12
+ :^ => :PyNumber_Xor,
13
+ :| => :PyNumber_Or
14
+ }.freeze
15
+
16
+ UNARY_OPERATION_OPFUNCS = {
17
+ :+@ => :PyNumber_Positive,
18
+ :-@ => :PyNumber_Negative,
19
+ :~ => :PyNumber_Invert,
20
+ }.freeze
21
+
22
+ def __binary_operate__(other, op)
23
+ opfunc = BINARY_OPERATION_OPFUNCS[op]
24
+ raise ArgumentError, "Unknown binary operation op: #{op}" unless opfunc
25
+
26
+ other = other.to_python
27
+ value = LibPython.send(opfunc, @pystruct, other)
28
+ raise PyError.fetch if value.null?
29
+ value.to_ruby
30
+ end
31
+
32
+ BINARY_OPERATION_OPFUNCS.keys.each do |op|
33
+ define_method(op) do |other|
34
+ __binary_operate__(other, op)
35
+ end
36
+ end
37
+
38
+ def __unary_operate__(op)
39
+ opfunc = UNARY_OPERATION_OPFUNCS[op]
40
+ raise ArgumentError, "Unknown unary operation op: #{op}" unless opfunc
41
+
42
+ value = LibPython.send(opfunc, @pystruct)
43
+ raise PyError.fetch if value.null?
44
+ value.to_ruby
45
+ end
46
+
47
+ UNARY_OPERATION_OPFUNCS.keys.each do |op|
48
+ define_method(op) do |other|
49
+ __unary_operate__(op)
50
+ end
51
+ end
52
+
53
+ def **(other)
54
+ other = other.to_python
55
+ value = LibPython.PyNumber_Power(@pystruct, other, PyBind.None)
56
+ raise PyError.fetch if value.null?
57
+ value.to_ruby
58
+ end
59
+
60
+ def <=>(other)
61
+ if LibPython.respond_to? :PyObject_Compare
62
+ other = other.to_python
63
+ value = LibPython.PyObject_Compare(@pystruct, other)
64
+ raise PyError.fetch unless LibPython.PyErr_Occurred().null?
65
+ value
66
+ else
67
+ (self > other) - (self < other)
68
+ end
69
+ end
70
+
71
+ def ===(other)
72
+ other = other.to_python
73
+ @pystruct.to_ptr == other.to_ptr
74
+ end
75
+
76
+ def !
77
+ value = LibPython.PyObject_Not(@pystruct)
78
+ raise PyError.fetch if value == -1
79
+ value == 1
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,39 @@
1
+ module PyBind
2
+ module RichComparer
3
+ Py_LT = 0
4
+ Py_LE = 1
5
+ Py_EQ = 2
6
+ Py_NE = 3
7
+ Py_GT = 4
8
+ Py_GE = 5
9
+
10
+ RICH_COMPARISON_OPCODES = {
11
+ :< => Py_LT,
12
+ :<= => Py_LE,
13
+ :== => Py_EQ,
14
+ :!= => Py_NE,
15
+ :> => Py_GT,
16
+ :>= => Py_GE
17
+ }.freeze
18
+
19
+ def __rich_compare__(other, op)
20
+ opcode = RICH_COMPARISON_OPCODES[op]
21
+ raise ArgumentError, "Unknown comparison op: #{op}" unless opcode
22
+
23
+ other = other.to_python
24
+ return other.null? if @pystruct.null?
25
+ return false if other.null?
26
+
27
+ value = LibPython.PyObject_RichCompare(@pystruct, other, opcode)
28
+ raise PyError.fetch if value.null?
29
+ value.to_ruby
30
+ end
31
+
32
+ RICH_COMPARISON_OPCODES.keys.each do |op|
33
+ define_method(op) do |other|
34
+ __rich_compare__(other, op)
35
+ end
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,40 @@
1
+ # coding: utf-8
2
+
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'pybind/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'pybind'
9
+ spec.version = PyBind::VERSION
10
+ spec.authors = ['Theo Li']
11
+ spec.email = ['bbtfrr@gmail.com']
12
+
13
+ spec.summary = 'Seamless operability between Ruby and Python'
14
+ spec.description = 'Seamless operability between Ruby and Python'
15
+ spec.homepage = 'https://github.com/bbtfr/pybind'
16
+ spec.license = 'MIT'
17
+
18
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
19
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
20
+ if spec.respond_to?(:metadata)
21
+ spec.metadata['allowed_push_host'] = "https://rubygems.org"
22
+ else
23
+ raise 'RubyGems 2.0 or newer is required to protect against ' \
24
+ 'public gem pushes.'
25
+ end
26
+
27
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
28
+ f.match(%r{^(test|spec|features)/})
29
+ end
30
+ spec.bindir = 'exe'
31
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
32
+ spec.require_paths = ['lib']
33
+
34
+ spec.add_dependency 'ffi'
35
+
36
+ spec.add_development_dependency 'bundler', '~> 1.14'
37
+ spec.add_development_dependency 'rake', '~> 10.0'
38
+ spec.add_development_dependency 'rspec', '~> 3.0'
39
+ spec.add_development_dependency 'pry', '~> 0.10.4'
40
+ end
metadata ADDED
@@ -0,0 +1,157 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pybind
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Theo Li
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-05-10 00:00:00.000000000 Z
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
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.14'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.14'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.10.4
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.10.4
83
+ description: Seamless operability between Ruby and Python
84
+ email:
85
+ - bbtfrr@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".rspec"
92
+ - ".travis.yml"
93
+ - CODE_OF_CONDUCT.md
94
+ - Gemfile
95
+ - LICENSE
96
+ - README.md
97
+ - Rakefile
98
+ - bin/console
99
+ - bin/setup
100
+ - examples/autocall_functions.rb
101
+ - examples/hello_world.py
102
+ - examples/hello_world.rb
103
+ - examples/mnist.softmax.rb
104
+ - examples/numpy.rb
105
+ - lib/pybind.rb
106
+ - lib/pybind/autocall.rb
107
+ - lib/pybind/core_ext/basic.rb
108
+ - lib/pybind/error.rb
109
+ - lib/pybind/import.rb
110
+ - lib/pybind/init.rb
111
+ - lib/pybind/libpython.rb
112
+ - lib/pybind/python/investigator.py
113
+ - lib/pybind/struct.rb
114
+ - lib/pybind/typecast.rb
115
+ - lib/pybind/types.rb
116
+ - lib/pybind/types/basic.rb
117
+ - lib/pybind/types/dict.rb
118
+ - lib/pybind/types/function.rb
119
+ - lib/pybind/types/list.rb
120
+ - lib/pybind/types/object.rb
121
+ - lib/pybind/types/sequence.rb
122
+ - lib/pybind/types/set.rb
123
+ - lib/pybind/types/slice.rb
124
+ - lib/pybind/types/tuple.rb
125
+ - lib/pybind/utils.rb
126
+ - lib/pybind/version.rb
127
+ - lib/pybind/wrapper.rb
128
+ - lib/pybind/wrapper/attr_accessor.rb
129
+ - lib/pybind/wrapper/operator.rb
130
+ - lib/pybind/wrapper/rich_comparer.rb
131
+ - pybind.gemspec
132
+ homepage: https://github.com/bbtfr/pybind
133
+ licenses:
134
+ - MIT
135
+ metadata:
136
+ allowed_push_host: https://rubygems.org
137
+ post_install_message:
138
+ rdoc_options: []
139
+ require_paths:
140
+ - lib
141
+ required_ruby_version: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ required_rubygems_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ requirements: []
152
+ rubyforge_project:
153
+ rubygems_version: 2.6.11
154
+ signing_key:
155
+ specification_version: 4
156
+ summary: Seamless operability between Ruby and Python
157
+ test_files: []