rubypython-raspi 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.
- data/.autotest +3 -0
- data/.gitignore +18 -0
- data/.hgignore +20 -0
- data/.hgtags +10 -0
- data/.rspec +2 -0
- data/Contributors.rdoc +10 -0
- data/History.rdoc +192 -0
- data/License.rdoc +26 -0
- data/Manifest.txt +43 -0
- data/PostInstall.txt +16 -0
- data/README.rdoc +272 -0
- data/Rakefile +108 -0
- data/autotest/discover.rb +1 -0
- data/lib/rubypython.rb +284 -0
- data/lib/rubypython/blankobject.rb +23 -0
- data/lib/rubypython/conversion.rb +286 -0
- data/lib/rubypython/legacy.rb +18 -0
- data/lib/rubypython/macros.rb +56 -0
- data/lib/rubypython/operators.rb +124 -0
- data/lib/rubypython/pygenerator.rb +61 -0
- data/lib/rubypython/pymainclass.rb +80 -0
- data/lib/rubypython/pyobject.rb +232 -0
- data/lib/rubypython/python.rb +195 -0
- data/lib/rubypython/pythonerror.rb +80 -0
- data/lib/rubypython/rubypyproxy.rb +336 -0
- data/lib/rubypython/type.rb +20 -0
- data/spec/basic_spec.rb +50 -0
- data/spec/callback_spec.rb +53 -0
- data/spec/conversion_spec.rb +68 -0
- data/spec/legacy_spec.rb +46 -0
- data/spec/pymainclass_spec.rb +24 -0
- data/spec/pyobject_spec.rb +246 -0
- data/spec/python_helpers/basics.py +23 -0
- data/spec/python_helpers/errors.py +2 -0
- data/spec/python_helpers/objects.py +48 -0
- data/spec/pythonerror_spec.rb +52 -0
- data/spec/refcnt_spec.rb +62 -0
- data/spec/rubypyclass_spec.rb +10 -0
- data/spec/rubypyproxy_spec.rb +261 -0
- data/spec/rubypython_spec.rb +59 -0
- data/spec/spec_helper.rb +67 -0
- metadata +200 -0
data/Rakefile
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
# -*- ruby encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
|
6
|
+
Hoe.plugin :doofus
|
7
|
+
Hoe.plugin :gemspec
|
8
|
+
Hoe.plugin :rubyforge
|
9
|
+
Hoe.plugin :git
|
10
|
+
Hoe.plugin :hg
|
11
|
+
|
12
|
+
Hoe.spec 'rubypython' do
|
13
|
+
self.rubyforge_name = self.name
|
14
|
+
|
15
|
+
developer('Steeve Morin', 'swiuzzz+rubypython@gmail.com')
|
16
|
+
developer('Austin Ziegler', 'austin@rubyforge.org')
|
17
|
+
developer('Zach Raines', 'raineszm+rubypython@gmail.com')
|
18
|
+
|
19
|
+
self.remote_rdoc_dir = 'rdoc'
|
20
|
+
self.rsync_args << ' --exclude=statsvn/'
|
21
|
+
|
22
|
+
self.history_file = 'History.rdoc'
|
23
|
+
self.readme_file = 'README.rdoc'
|
24
|
+
self.extra_rdoc_files = FileList["*.rdoc"].to_a
|
25
|
+
|
26
|
+
self.extra_deps << ['ffi', '~> 1.0.7']
|
27
|
+
self.extra_deps << ['blankslate', '>= 2.1.2.3']
|
28
|
+
|
29
|
+
self.extra_dev_deps << ['rspec', '~> 2.0']
|
30
|
+
self.extra_dev_deps << ['tilt', '~> 1.0']
|
31
|
+
|
32
|
+
self.spec_extras[:requirements] = [ "Python, ~> 2.4" ]
|
33
|
+
end
|
34
|
+
|
35
|
+
namespace :website do
|
36
|
+
desc "Build the website files."
|
37
|
+
task :build => [ "website/index.html" ]
|
38
|
+
|
39
|
+
deps = FileList["website/**/*"].exclude { |f| File.directory? f }
|
40
|
+
deps.include(*%w(Rakefile))
|
41
|
+
deps.include(*FileList["*.rdoc"].to_a)
|
42
|
+
deps.exclude(*%w(website/index.html website/images/*))
|
43
|
+
|
44
|
+
file "website/index.html" => deps do |t|
|
45
|
+
require 'tilt'
|
46
|
+
require 'rubypython'
|
47
|
+
|
48
|
+
puts "Generating #{t.name}…"
|
49
|
+
|
50
|
+
# Let's modify the rdoc for presenation purposes.
|
51
|
+
body_rdoc = File.read("README.rdoc")
|
52
|
+
|
53
|
+
contrib = File.read("Contributors.rdoc")
|
54
|
+
body_rdoc.gsub!(/^:include: Contributors.rdoc/, contrib)
|
55
|
+
|
56
|
+
license = File.read("License.rdoc")
|
57
|
+
body_rdoc.sub!(/^:include: License.rdoc/, license)
|
58
|
+
toc_elements = body_rdoc.scan(/^(=+) (.*)$/)
|
59
|
+
toc_elements.map! { |e| [ e[0].count('='), e[1] ] }
|
60
|
+
body_rdoc.gsub!(/^(=.*)/) { "#{$1.downcase}" }
|
61
|
+
body = Tilt::RDocTemplate.new(nil) { body_rdoc }.render
|
62
|
+
|
63
|
+
title = nil
|
64
|
+
body.gsub!(%r{<h1>(.*)</h1>}) { title = $1; "" }
|
65
|
+
|
66
|
+
toc_elements = toc_elements.select { |e| e[0].between?(2, 3) }
|
67
|
+
|
68
|
+
last_level = 0
|
69
|
+
toc = ""
|
70
|
+
|
71
|
+
toc_elements.each do |element|
|
72
|
+
level, text = *element
|
73
|
+
ltext = text.downcase
|
74
|
+
id = text.downcase.gsub(/[^a-z]+/, '-')
|
75
|
+
|
76
|
+
body.gsub!(%r{<h#{level}>#{ltext}</h#{level}>}) {
|
77
|
+
%Q(<h#{level} id="#{id}">#{ltext}</h#{level}>)
|
78
|
+
}
|
79
|
+
|
80
|
+
if last_level != level
|
81
|
+
if level > last_level
|
82
|
+
toc << "<ol>"
|
83
|
+
else
|
84
|
+
toc << "</li></ol></li>"
|
85
|
+
end
|
86
|
+
|
87
|
+
last_level = level
|
88
|
+
end
|
89
|
+
|
90
|
+
toc << %Q(<li><a href="##{id}">#{text}</a>)
|
91
|
+
end
|
92
|
+
toc << "</li></ol>"
|
93
|
+
|
94
|
+
template = Tilt.new("website/index.rhtml", :trim => "<>%")
|
95
|
+
context = {
|
96
|
+
:title => title,
|
97
|
+
:toc => toc,
|
98
|
+
:body => body,
|
99
|
+
:download => "http://rubyforge.org/frs/?group_id=6737",
|
100
|
+
:version => RubyPython::VERSION,
|
101
|
+
:modified => Time.now
|
102
|
+
}
|
103
|
+
File.open(t.name, "w") { |f| f.write template.render(self, context) } end
|
104
|
+
end
|
105
|
+
|
106
|
+
task "docs" => "website:build"
|
107
|
+
|
108
|
+
# vim: syntax=ruby
|
@@ -0,0 +1 @@
|
|
1
|
+
Autotest.add_discovery { "rspec2" }
|
data/lib/rubypython.rb
ADDED
@@ -0,0 +1,284 @@
|
|
1
|
+
# RubyPython is a bridge between the Ruby and \Python interpreters. It
|
2
|
+
# embeds a \Python interpreter in the Ruby application's process using FFI
|
3
|
+
# and provides a means for wrapping, converting, and calling \Python objects
|
4
|
+
# and methods.
|
5
|
+
#
|
6
|
+
# == Usage
|
7
|
+
# The \Python interpreter must be started before the RubyPython bridge is
|
8
|
+
# functional. The user can either manually manage the running of the
|
9
|
+
# interpreter as shown below, or use the +RubyPython.run+ or
|
10
|
+
# +RubyPython.session+ methods to automatically start and stop the
|
11
|
+
# interpreter.
|
12
|
+
#
|
13
|
+
# RubyPython.start
|
14
|
+
# cPickle = RubyPython.import "cPickle"
|
15
|
+
# puts cPickle.dumps("RubyPython is awesome!").rubify
|
16
|
+
# RubyPython.stop
|
17
|
+
module RubyPython
|
18
|
+
VERSION = '0.6'
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'rubypython/blankobject'
|
22
|
+
require 'rubypython/interpreter'
|
23
|
+
require 'rubypython/python'
|
24
|
+
require 'rubypython/pythonerror'
|
25
|
+
require 'rubypython/pyobject'
|
26
|
+
require 'rubypython/rubypyproxy'
|
27
|
+
require 'rubypython/pymainclass'
|
28
|
+
require 'rubypython/pygenerator'
|
29
|
+
require 'rubypython/tuple'
|
30
|
+
require 'thread'
|
31
|
+
|
32
|
+
module RubyPython
|
33
|
+
class << self
|
34
|
+
##
|
35
|
+
# :attr_accessor:
|
36
|
+
# Controls whether RubyPython is operating in <em>Proxy Mode</em> or
|
37
|
+
# <em>Legacy Mode</em>. This behavioural difference is deprecated as of
|
38
|
+
# RubyPython 0.6 and will be removed in a subsequent release.
|
39
|
+
#
|
40
|
+
# === Proxy Mode
|
41
|
+
# By default, +legacy_mode+ is +false+, meaning that any object returned
|
42
|
+
# from a \Python function call will be wrapped in a Ruby-Python proxy
|
43
|
+
# (an instance of +RubyPyProxy+ or one of its subclasses). This allows
|
44
|
+
# \Python method calls to be forwarded to the \Python object, even if it
|
45
|
+
# would otherwise be a native Ruby object.
|
46
|
+
#
|
47
|
+
# RubyPython.session do
|
48
|
+
# string = RubyPython.import 'string'
|
49
|
+
# ascii_letters = string.ascii_letters
|
50
|
+
# puts ascii_letters.isalpha # => True
|
51
|
+
# puts ascii_letters.rubify.isalpha # throws NoMethodError
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# === Legacy Mode
|
55
|
+
# If +legacy_mode+ is +true+, RubyPython automatically tries to convert
|
56
|
+
# returned objects to native Ruby object types. If there is no such
|
57
|
+
# conversion, the object remains wrapped in +RubyPyProxy+. This
|
58
|
+
# behaviour is the same as RubyPython 0.2 and earlier. This mode is
|
59
|
+
# deprecated as of RubyPython 0.6 and will be removed.
|
60
|
+
#
|
61
|
+
# RubyPython.legacy_mode = true
|
62
|
+
# RubyPython.session do
|
63
|
+
# string = RubyPython.import 'string'
|
64
|
+
# ascii_letters = string.ascii_letters
|
65
|
+
# puts ascii_letters.isalpha # throws NoMethodError
|
66
|
+
# end
|
67
|
+
def legacy_mode=(value)
|
68
|
+
warn_legacy_mode_deprecation unless defined? @legacy_mode
|
69
|
+
@legacy_mode = value
|
70
|
+
end
|
71
|
+
|
72
|
+
def legacy_mode
|
73
|
+
unless defined? @legacy_mode
|
74
|
+
warn_legacy_mode_deprecation
|
75
|
+
@legacy_mode = nil
|
76
|
+
end
|
77
|
+
@legacy_mode
|
78
|
+
end
|
79
|
+
|
80
|
+
def legacy_mode?
|
81
|
+
@legacy_mode = nil unless defined? @legacy_mode
|
82
|
+
@legacy_mode
|
83
|
+
end
|
84
|
+
private :legacy_mode?
|
85
|
+
|
86
|
+
def warn_legacy_mode_deprecation
|
87
|
+
warn "RubyPython's Legacy Mode is deprecated and will be removed after version #{VERSION}."
|
88
|
+
end
|
89
|
+
private :warn_legacy_mode_deprecation
|
90
|
+
|
91
|
+
## Starts the \Python interpreter. One of +RubyPython.start+,
|
92
|
+
# RubyPython.session+, or +RubyPython.run+ must be run before using any
|
93
|
+
# \Python code. Returns +true+ if the interpreter was started; +false+
|
94
|
+
# otherwise.
|
95
|
+
#
|
96
|
+
# [options] Configures the interpreter prior to starting it. Principally
|
97
|
+
# used to provide an alternative \Python interpreter to start.
|
98
|
+
#
|
99
|
+
# With no options provided:
|
100
|
+
# RubyPython.start
|
101
|
+
# sys = RubyPython.import 'sys'
|
102
|
+
# p sys.version # => "2.6.6"
|
103
|
+
# RubyPython.stop
|
104
|
+
#
|
105
|
+
# With an alternative \Python executable:
|
106
|
+
# RubyPython.start(:python_exe => 'python2.7')
|
107
|
+
# sys = RubyPython.import 'sys'
|
108
|
+
# p sys.version # => "2.7.1"
|
109
|
+
# RubyPython.stop
|
110
|
+
def start(options = {})
|
111
|
+
Mutex.new.synchronize do
|
112
|
+
# Has the Runtime interpreter been defined?
|
113
|
+
if self.const_defined?(:Runtime)
|
114
|
+
# If this constant is defined, then yes it is. Since it is, let's
|
115
|
+
# see if we should print a warning to the user.
|
116
|
+
unless Runtime == options
|
117
|
+
warn "The Python interpreter has already been loaded from #{Runtime.python} and cannot be changed in this process. Continuing with the current runtime."
|
118
|
+
end
|
119
|
+
else
|
120
|
+
interp = RubyPython::Interpreter.new(options)
|
121
|
+
if interp.valid?
|
122
|
+
self.const_set(:Runtime, interp)
|
123
|
+
else
|
124
|
+
raise RubyPython::InvalidInterpreter, "An invalid interpreter was specified."
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
unless defined? RubyPython::Python.ffi_libraries
|
129
|
+
Runtime.__send__(:infect!, RubyPython::Python)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
return false if RubyPython::Python.Py_IsInitialized != 0
|
134
|
+
RubyPython::Python.Py_Initialize
|
135
|
+
notify :start
|
136
|
+
true
|
137
|
+
end
|
138
|
+
|
139
|
+
# Stops the \Python interpreter if it is running. Returns +true+ if the
|
140
|
+
# intepreter is stopped. All wrapped \Python objects are invalid after
|
141
|
+
# invocation of this method. If you need the values within the \Python
|
142
|
+
# proxy objects, be sure to call +RubyPyProxy#rubify+ on them.
|
143
|
+
def stop
|
144
|
+
if defined? Python.Py_IsInitialized and Python.Py_IsInitialized != 0
|
145
|
+
Python.Py_Finalize
|
146
|
+
notify :stop
|
147
|
+
true
|
148
|
+
else
|
149
|
+
false
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Import a \Python module into the interpreter and return a proxy object
|
154
|
+
# for it.
|
155
|
+
#
|
156
|
+
# This is the preferred way to gain access to \Python objects.
|
157
|
+
#
|
158
|
+
# [mod_name] The name of the module to import.
|
159
|
+
def import(mod_name)
|
160
|
+
if defined? Python.Py_IsInitialized and Python.Py_IsInitialized != 0
|
161
|
+
pModule = Python.PyImport_ImportModule mod_name
|
162
|
+
raise PythonError.handle_error if PythonError.error?
|
163
|
+
pymod = PyObject.new pModule
|
164
|
+
RubyPyModule.new(pymod)
|
165
|
+
else
|
166
|
+
raise "Python has not been started."
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# Starts the \Python interpreter (optionally with options) and +yields+
|
171
|
+
# to the provided block. When the block exits for any reason, the
|
172
|
+
# \Python interpreter is stopped automatically.
|
173
|
+
#
|
174
|
+
# The last executed expression of the block is returned. Be careful that
|
175
|
+
# the last expression of the block does not return a RubyPyProxy object,
|
176
|
+
# because the proxy object will be invalidated when the interpreter is
|
177
|
+
# stopped.
|
178
|
+
#
|
179
|
+
# [options] Configures the interpreter prior to starting it. Principally
|
180
|
+
# used to provide an alternative \Python interpreter to start.
|
181
|
+
#
|
182
|
+
# *NOTE*: In the current version of RubyPython, it _is_ possible to change
|
183
|
+
# \Python interpreters in a single Ruby process execution, but it is
|
184
|
+
# *strongly* discouraged as this may lead to segmentation faults. This
|
185
|
+
# feature is highly experimental and may be disabled in the future.
|
186
|
+
#
|
187
|
+
# :call-seq:
|
188
|
+
# session(options = {}) { block to execute }
|
189
|
+
def session(options = {})
|
190
|
+
start(options)
|
191
|
+
yield
|
192
|
+
ensure
|
193
|
+
stop
|
194
|
+
end
|
195
|
+
|
196
|
+
# Starts the \Python interpreter (optionally with options) and executes
|
197
|
+
# the provided block in the RubyPython module scope. When the block
|
198
|
+
# exits for any reason, the \Python interpreter is stopped
|
199
|
+
# automatically.
|
200
|
+
#
|
201
|
+
# The last executed expression of the block is returned. Be careful that
|
202
|
+
# the last expression of the block does not return a RubyPyProxy object,
|
203
|
+
# because the proxy object will be invalidated when the interpreter is
|
204
|
+
# stopped.
|
205
|
+
#
|
206
|
+
# [options] Configures the interpreter prior to starting it. Principally
|
207
|
+
# used to provide an alternative \Python interpreter to start.
|
208
|
+
#
|
209
|
+
# *NOTE*: In the current version of RubyPython, it _is_ possible to
|
210
|
+
# change \Python interpreters in a single Ruby process execution, but it
|
211
|
+
# is *strongly* discouraged as this may lead to segmentation faults.
|
212
|
+
# This feature is highly experimental and may be disabled in the future.
|
213
|
+
#
|
214
|
+
# :call-seq:
|
215
|
+
# run(options = {}) { block to execute in RubyPython context }
|
216
|
+
def run(options = {}, &block)
|
217
|
+
start(options)
|
218
|
+
self.module_eval(&block)
|
219
|
+
ensure
|
220
|
+
stop
|
221
|
+
end
|
222
|
+
|
223
|
+
# Starts the \Python interpreter for a
|
224
|
+
# {virtualenv}[http://pypi.python.org/pypi/virtualenv] virtual
|
225
|
+
# environment. Returns +true+ if the interpreter was started.
|
226
|
+
#
|
227
|
+
# [virtualenv] The root path to the virtualenv-installed \Python
|
228
|
+
# interpreter.
|
229
|
+
#
|
230
|
+
# RubyPython.start_from_virtualenv('/path/to/virtualenv')
|
231
|
+
# sys = RubyPython.import 'sys'
|
232
|
+
# p sys.version # => "2.7.1"
|
233
|
+
# RubyPython.stop
|
234
|
+
#
|
235
|
+
# *NOTE*: In the current version of RubyPython, it _is_ possible to
|
236
|
+
# change \Python interpreters in a single Ruby process execution, but it
|
237
|
+
# is *strongly* discouraged as this may lead to segmentation faults.
|
238
|
+
# This feature is highly experimental and may be disabled in the future.
|
239
|
+
def start_from_virtualenv(virtualenv)
|
240
|
+
result = start(:python_exe => File.join(virtualenv, "bin", "python"))
|
241
|
+
activate_virtualenv
|
242
|
+
result
|
243
|
+
end
|
244
|
+
|
245
|
+
# Returns an object describing the active Python interpreter. Returns
|
246
|
+
# +nil+ if there is no active interpreter.
|
247
|
+
def python
|
248
|
+
if self.const_defined? :Runtime
|
249
|
+
self::Runtime
|
250
|
+
else
|
251
|
+
nil
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
# Used to activate the virtualenv.
|
256
|
+
def activate_virtualenv
|
257
|
+
imp = import("imp")
|
258
|
+
imp.load_source("activate_this",
|
259
|
+
File.join(File.dirname(RubyPython::Runtime.python),
|
260
|
+
"activate_this.py"))
|
261
|
+
end
|
262
|
+
private :activate_virtualenv
|
263
|
+
|
264
|
+
def add_observer(object)
|
265
|
+
@observers ||= []
|
266
|
+
@observers << object
|
267
|
+
true
|
268
|
+
end
|
269
|
+
private :add_observer
|
270
|
+
|
271
|
+
def notify(status)
|
272
|
+
@observers ||= []
|
273
|
+
@observers.each do |o|
|
274
|
+
next if nil === o
|
275
|
+
o.__send__ :python_interpreter_update, status
|
276
|
+
end
|
277
|
+
end
|
278
|
+
private :notify
|
279
|
+
end
|
280
|
+
|
281
|
+
add_observer PyMain
|
282
|
+
add_observer Operators
|
283
|
+
add_observer PyObject::AutoPyPointer
|
284
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'blankslate'
|
2
|
+
|
3
|
+
# This document is the basis of the RubyPyProxy precisely because it hides
|
4
|
+
# the implementation of so many things that should be forwarded on to the
|
5
|
+
# Python object. This class is for internal use only.
|
6
|
+
#
|
7
|
+
# Note that in Ruby 1.9, BasicObject might be a better choice, but there are
|
8
|
+
# some decisions made in the rest of the library that make this harder. I
|
9
|
+
# don't see a clean way to integrate both Ruby 1.8 and 1.9 support for this.
|
10
|
+
class RubyPython::BlankObject < ::BlankSlate #:nodoc:
|
11
|
+
class << self
|
12
|
+
def hide(name)
|
13
|
+
if instance_methods.include?(name) and
|
14
|
+
name.to_s !~ /^(__|instance_eval|object_id)/
|
15
|
+
@hidden_methods ||= {}
|
16
|
+
@hidden_methods[name.to_sym] = instance_method(name)
|
17
|
+
undef_method name
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
instance_methods.each { |m| hide(m) }
|
23
|
+
end
|
@@ -0,0 +1,286 @@
|
|
1
|
+
require 'rubypython/python'
|
2
|
+
require 'rubypython/macros'
|
3
|
+
|
4
|
+
# Acts as a namespace for methods to bidirectionally convert between native
|
5
|
+
# Ruby types and native \Python types. Unsupported conversions raise
|
6
|
+
# UnsupportedConversion.
|
7
|
+
#
|
8
|
+
# The methods in this module should be considered internal implementation to
|
9
|
+
# RubyPython as they all return FFI pointers to \Python objects.
|
10
|
+
module RubyPython::Conversion
|
11
|
+
# Raised when RubyPython does not know how to convert an object from
|
12
|
+
# \Python to Ruby or vice versa.
|
13
|
+
class UnsupportedConversion < Exception; end
|
14
|
+
|
15
|
+
# Convert a Ruby string to a \Python string. Returns an FFI::Pointer to
|
16
|
+
# a PyStringObject.
|
17
|
+
def self.rtopString(rString)
|
18
|
+
size = rString.respond_to?(:bytesize) ? rString.bytesize : rString.size
|
19
|
+
RubyPython::Python.PyString_FromStringAndSize(rString, size)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Convert a Ruby Array to \Python List. Returns an FFI::Pointer to
|
23
|
+
# a PyListObject.
|
24
|
+
def self.rtopArrayToList(rArray)
|
25
|
+
size = rArray.length
|
26
|
+
pList = RubyPython::Python.PyList_New size
|
27
|
+
rArray.each_with_index do |el, i|
|
28
|
+
RubyPython::Python.PyList_SetItem pList, i, rtopObject(el)
|
29
|
+
end
|
30
|
+
pList
|
31
|
+
end
|
32
|
+
|
33
|
+
# Convert a Ruby Array (including the subclass RubyPython::Tuple) to
|
34
|
+
# \Python \tuple. Returns an FFI::Pointer to a PyTupleObject.
|
35
|
+
def self.rtopArrayToTuple(rArray)
|
36
|
+
pList = rtopArrayToList(rArray)
|
37
|
+
pTuple = RubyPython::Python.PySequence_Tuple(pList)
|
38
|
+
RubyPython::Python.Py_DecRef(pList)
|
39
|
+
pTuple
|
40
|
+
end
|
41
|
+
|
42
|
+
# Convert a Ruby Hash to a \Python Dict. Returns an FFI::Pointer to a
|
43
|
+
# PyDictObject.
|
44
|
+
def self.rtopHash(rHash)
|
45
|
+
pDict = RubyPython::Python.PyDict_New
|
46
|
+
rHash.each do |k,v|
|
47
|
+
RubyPython::Python.PyDict_SetItem pDict, rtopObject(k, key = true),
|
48
|
+
rtopObject(v)
|
49
|
+
end
|
50
|
+
pDict
|
51
|
+
end
|
52
|
+
|
53
|
+
# Convert a Ruby Fixnum to a \Python Int. Returns an FFI::Pointer to a
|
54
|
+
# PyIntObject.
|
55
|
+
def self.rtopFixnum(rNum)
|
56
|
+
RubyPython::Python.PyInt_FromLong(rNum)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Convert a Ruby Bignum to a \Python Long. Returns an FFI::Pointer to a
|
60
|
+
# PyLongObject.
|
61
|
+
def self.rtopBigNum(rNum)
|
62
|
+
RubyPython::Python.PyLong_FromLong(rNum)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Convert a Ruby float to a \Python Float. Returns an FFI::Pointer to a
|
66
|
+
# PyFloatObject.
|
67
|
+
def self.rtopFloat(rNum)
|
68
|
+
RubyPython::Python.PyFloat_FromDouble(rNum)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns a \Python False value (equivalent to Ruby's +false+). Returns an
|
72
|
+
# FFI::Pointer to Py_ZeroStruct.
|
73
|
+
def self.rtopFalse
|
74
|
+
RubyPython::Macros.Py_RETURN_FALSE
|
75
|
+
end
|
76
|
+
|
77
|
+
# Returns a \Python True value (equivalent to Ruby's +true+). Returns an
|
78
|
+
# FFI::Pointer to Py_TrueStruct.
|
79
|
+
def self.rtopTrue
|
80
|
+
RubyPython::Macros.Py_RETURN_TRUE
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns a \Python None value (equivalent to Ruby's +nil+). Returns an
|
84
|
+
# FFI::Pointer to Py_NoneStruct.
|
85
|
+
def self.rtopNone
|
86
|
+
RubyPython::Macros.Py_RETURN_NONE
|
87
|
+
end
|
88
|
+
|
89
|
+
# Convert a Ruby Symbol to a \Python String. Returns an FFI::Pointer to a
|
90
|
+
# PyStringObject.
|
91
|
+
def self.rtopSymbol(rSymbol)
|
92
|
+
RubyPython::Python.PyString_FromString rSymbol.to_s
|
93
|
+
end
|
94
|
+
|
95
|
+
# Convert a Ruby Proc to a \Python Function. Returns an FFI::Pointer to a
|
96
|
+
# PyCFunction.
|
97
|
+
def self.rtopFunction(rObj)
|
98
|
+
proc = FFI::Function.new(:pointer, [:pointer, :pointer]) do |p_self, p_args|
|
99
|
+
retval = rObj.call(*ptorTuple(p_args))
|
100
|
+
pObject = retval.is_a?(RubyPython::RubyPyProxy) ? retval.pObject : RubyPython::PyObject.new(retval)
|
101
|
+
|
102
|
+
# make sure the refcount is >1 when pObject is destroyed
|
103
|
+
pObject.xIncref
|
104
|
+
pObject.pointer
|
105
|
+
end
|
106
|
+
|
107
|
+
defn = RubyPython::Python::PyMethodDef.new
|
108
|
+
defn[:ml_name] = FFI::MemoryPointer.from_string("RubyPython::Proc::%s" % rObj.object_id)
|
109
|
+
defn[:ml_meth] = proc
|
110
|
+
defn[:ml_flags] = RubyPython::Python::METH_VARARGS
|
111
|
+
defn[:ml_doc] = nil
|
112
|
+
|
113
|
+
return RubyPython::Python.PyCFunction_New(defn, nil)
|
114
|
+
end
|
115
|
+
|
116
|
+
# This will attempt to convert a Ruby object to an equivalent \Python
|
117
|
+
# native type. Returns an FFI::Pointer to a \Python object (the
|
118
|
+
# appropriate Py…Object C structure). If the conversion is unsuccessful,
|
119
|
+
# will raise UnsupportedConversion.
|
120
|
+
#
|
121
|
+
# [rObj] A native Ruby object.
|
122
|
+
# [is_key] Set to +true+ if the provided Ruby object will be used as a
|
123
|
+
# key in a \Python +dict+. (This primarily matters for Array
|
124
|
+
# conversion.)
|
125
|
+
def self.rtopObject(rObj, is_key = false)
|
126
|
+
case rObj
|
127
|
+
when String
|
128
|
+
rtopString rObj
|
129
|
+
when RubyPython::Tuple
|
130
|
+
rtopArrayToTuple rObj
|
131
|
+
when Array
|
132
|
+
# If this object is going to be used as a hash key we should make it a
|
133
|
+
# tuple instead of a list
|
134
|
+
if is_key
|
135
|
+
rtopArrayToTuple rObj
|
136
|
+
else
|
137
|
+
rtopArrayToList rObj
|
138
|
+
end
|
139
|
+
when Hash
|
140
|
+
rtopHash rObj
|
141
|
+
when Fixnum
|
142
|
+
rtopFixnum rObj
|
143
|
+
when Bignum
|
144
|
+
rtopBignum rObj
|
145
|
+
when Float
|
146
|
+
rtopFloat rObj
|
147
|
+
when true
|
148
|
+
rtopTrue
|
149
|
+
when false
|
150
|
+
rtopFalse
|
151
|
+
when Symbol
|
152
|
+
rtopSymbol rObj
|
153
|
+
when Proc, Method
|
154
|
+
if RubyPython.legacy_mode
|
155
|
+
raise UnsupportedConversion.new("Callbacks are not supported in Legacy Mode.")
|
156
|
+
end
|
157
|
+
rtopFunction rObj
|
158
|
+
when Method
|
159
|
+
rtopFunction rObj
|
160
|
+
when nil
|
161
|
+
rtopNone
|
162
|
+
when RubyPython::PyObject
|
163
|
+
rObj.pointer
|
164
|
+
else
|
165
|
+
raise UnsupportedConversion.new("Unsupported type #{rObj.class} for conversion.")
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# Convert an FFI::Pointer to a \Python String (PyStringObject) to a Ruby
|
170
|
+
# String.
|
171
|
+
def self.ptorString(pString)
|
172
|
+
strPtr = FFI::MemoryPointer.new(:pointer)
|
173
|
+
sizePtr = FFI::MemoryPointer.new(:ssize_t)
|
174
|
+
|
175
|
+
RubyPython::Python.PyString_AsStringAndSize(pString, strPtr, sizePtr)
|
176
|
+
|
177
|
+
size = case FFI.find_type(:ssize_t)
|
178
|
+
when FFI.find_type(:long)
|
179
|
+
sizePtr.read_long
|
180
|
+
when FFI.find_type(:int)
|
181
|
+
sizePtr.read_int
|
182
|
+
when FFI.find_type(:long_long)
|
183
|
+
sizePtr.read_long_long
|
184
|
+
else
|
185
|
+
nil
|
186
|
+
end
|
187
|
+
|
188
|
+
strPtr.read_pointer.read_string(size)
|
189
|
+
end
|
190
|
+
|
191
|
+
# Convert an FFI::Pointer to a \Python List (PyListObject) to a Ruby
|
192
|
+
# Array.
|
193
|
+
def self.ptorList(pList)
|
194
|
+
rb_array = []
|
195
|
+
list_size = RubyPython::Python.PyList_Size(pList)
|
196
|
+
|
197
|
+
list_size.times do |i|
|
198
|
+
element = RubyPython::Python.PyList_GetItem(pList, i)
|
199
|
+
# PyList_GetItem returns borrowed ref
|
200
|
+
RubyPython::Python.Py_IncRef element
|
201
|
+
rObject = ptorObject(element)
|
202
|
+
rb_array.push rObject
|
203
|
+
end
|
204
|
+
|
205
|
+
rb_array
|
206
|
+
end
|
207
|
+
|
208
|
+
# Convert an FFI::Pointer to a \Python Int (PyIntObject) to a Ruby Fixnum.
|
209
|
+
def self.ptorInt(pNum)
|
210
|
+
RubyPython::Python.PyInt_AsLong(pNum)
|
211
|
+
end
|
212
|
+
|
213
|
+
# Convert an FFI::Pointer to a \Python Long (PyLongObject) to a Ruby
|
214
|
+
# Fixnum. This version does not do overflow checking, but probably should.
|
215
|
+
def self.ptorLong(pNum)
|
216
|
+
RubyPython::Python.PyLong_AsLong(pNum)
|
217
|
+
# TODO Overflow Checking
|
218
|
+
end
|
219
|
+
|
220
|
+
# Convert an FFI::Pointer to a \Python Float (PyFloatObject) to a Ruby
|
221
|
+
# Float.
|
222
|
+
def self.ptorFloat(pNum)
|
223
|
+
RubyPython::Python.PyFloat_AsDouble(pNum)
|
224
|
+
end
|
225
|
+
|
226
|
+
# Convert an FFI::Pointer to a \Python Tuple (PyTupleObject) to an
|
227
|
+
# instance of RubyPython::Tuple, a subclass of the Ruby Array class.
|
228
|
+
def self.ptorTuple(pTuple)
|
229
|
+
pList = RubyPython::Python.PySequence_List pTuple
|
230
|
+
rArray = ptorList pList
|
231
|
+
RubyPython::Python.Py_DecRef pList
|
232
|
+
RubyPython::Tuple.tuple(rArray)
|
233
|
+
end
|
234
|
+
|
235
|
+
# Convert an FFI::Pointer to a \Python Dictionary (PyDictObject) to a Ruby
|
236
|
+
# Hash.
|
237
|
+
def self.ptorDict(pDict)
|
238
|
+
rb_hash = {}
|
239
|
+
|
240
|
+
pos = FFI::MemoryPointer.new :ssize_t
|
241
|
+
pos.write_int 0
|
242
|
+
key = FFI::MemoryPointer.new :pointer
|
243
|
+
val = FFI::MemoryPointer.new :pointer
|
244
|
+
|
245
|
+
while RubyPython::Python.PyDict_Next(pDict, pos, key, val) != 0
|
246
|
+
pKey = key.read_pointer
|
247
|
+
pVal = val.read_pointer
|
248
|
+
rKey = ptorObject(pKey)
|
249
|
+
rVal = ptorObject(pVal)
|
250
|
+
rb_hash[rKey] = rVal
|
251
|
+
end
|
252
|
+
|
253
|
+
rb_hash
|
254
|
+
end
|
255
|
+
|
256
|
+
# Converts a pointer to a \Python object into a native Ruby type, if
|
257
|
+
# possible. If the conversion cannot be done, the Python object will be
|
258
|
+
# returned unmodified.
|
259
|
+
#
|
260
|
+
# [pObj] An FFI::Pointer to a \Python object.
|
261
|
+
def self.ptorObject(pObj)
|
262
|
+
if RubyPython::Macros.PyObject_TypeCheck(pObj, RubyPython::Python.PyString_Type.to_ptr) != 0
|
263
|
+
ptorString pObj
|
264
|
+
elsif RubyPython::Macros.PyObject_TypeCheck(pObj, RubyPython::Python.PyList_Type.to_ptr) != 0
|
265
|
+
ptorList pObj
|
266
|
+
elsif RubyPython::Macros.PyObject_TypeCheck(pObj, RubyPython::Python.PyInt_Type.to_ptr) != 0
|
267
|
+
ptorInt pObj
|
268
|
+
elsif RubyPython::Macros.PyObject_TypeCheck(pObj, RubyPython::Python.PyLong_Type.to_ptr) != 0
|
269
|
+
ptorLong pObj
|
270
|
+
elsif RubyPython::Macros.PyObject_TypeCheck(pObj, RubyPython::Python.PyFloat_Type.to_ptr) != 0
|
271
|
+
ptorFloat pObj
|
272
|
+
elsif RubyPython::Macros.PyObject_TypeCheck(pObj, RubyPython::Python.PyTuple_Type.to_ptr) != 0
|
273
|
+
ptorTuple pObj
|
274
|
+
elsif RubyPython::Macros.PyObject_TypeCheck(pObj, RubyPython::Python.PyDict_Type.to_ptr) != 0
|
275
|
+
ptorDict pObj
|
276
|
+
elsif pObj == RubyPython::Macros.Py_True
|
277
|
+
true
|
278
|
+
elsif pObj == RubyPython::Macros.Py_False
|
279
|
+
false
|
280
|
+
elsif pObj == RubyPython::Macros.Py_None
|
281
|
+
nil
|
282
|
+
else
|
283
|
+
pObj
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|