pybind 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []