rubypython 0.3.2 → 0.5.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/.gemtest +0 -0
- data/.gitignore +13 -0
- data/.hgignore +14 -0
- data/.hgtags +7 -0
- data/.rspec +1 -1
- data/Contributors.rdoc +9 -0
- data/History.rdoc +148 -0
- data/{License.txt → License.rdoc} +7 -1
- data/Manifest.txt +15 -10
- data/PostInstall.txt +11 -4
- data/README.rdoc +272 -0
- data/Rakefile +107 -22
- data/autotest/discover.rb +1 -0
- data/lib/rubypython.rb +214 -120
- data/lib/rubypython/blankobject.rb +16 -14
- data/lib/rubypython/conversion.rb +242 -173
- data/lib/rubypython/legacy.rb +30 -31
- data/lib/rubypython/macros.rb +43 -34
- data/lib/rubypython/operators.rb +103 -101
- data/lib/rubypython/options.rb +41 -44
- data/lib/rubypython/pygenerator.rb +61 -0
- data/lib/rubypython/pymainclass.rb +46 -29
- data/lib/rubypython/pyobject.rb +193 -177
- data/lib/rubypython/python.rb +189 -176
- data/lib/rubypython/pythonerror.rb +54 -63
- data/lib/rubypython/pythonexec.rb +123 -0
- data/lib/rubypython/rubypyproxy.rb +213 -137
- data/lib/rubypython/type.rb +20 -0
- data/spec/basic_spec.rb +50 -0
- data/spec/callback_spec.rb +7 -17
- data/spec/conversion_spec.rb +7 -21
- data/spec/legacy_spec.rb +1 -16
- data/spec/pymainclass_spec.rb +6 -15
- data/spec/pyobject_spec.rb +39 -64
- data/spec/python_helpers/basics.py +20 -0
- data/spec/python_helpers/objects.py +24 -20
- data/spec/pythonerror_spec.rb +5 -17
- data/spec/refcnt_spec.rb +4 -10
- data/spec/rubypyclass_spec.rb +1 -11
- data/spec/rubypyproxy_spec.rb +45 -54
- data/spec/rubypython_spec.rb +45 -57
- data/spec/spec_helper.rb +49 -33
- metadata +87 -63
- data.tar.gz.sig +0 -0
- data/History.markdown +0 -97
- data/README.markdown +0 -105
- data/lib/rubypython/core_ext/string.rb +0 -7
- data/lib/rubypython/version.rb +0 -9
- data/spec/python_helpers/objects.pyc +0 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,123 @@
|
|
1
|
+
# A class that represents a \Python executable.
|
2
|
+
#
|
3
|
+
# End users may get the instance that represents the current running \Python
|
4
|
+
# interpreter (from +RubyPython.python+), but should not directly
|
5
|
+
# instantiate this class.
|
6
|
+
class RubyPython::PythonExec
|
7
|
+
# Based on the name of or path to the \Python executable provided, will
|
8
|
+
# determine:
|
9
|
+
#
|
10
|
+
# * The full path to the \Python executable.
|
11
|
+
# * The version of \Python being run.
|
12
|
+
# * The system prefix.
|
13
|
+
# * The main loadable \Python library for this version.
|
14
|
+
def initialize(python_executable)
|
15
|
+
@python = python_executable || "python"
|
16
|
+
@python = %x(#{@python} -c "import sys; print sys.executable").chomp
|
17
|
+
|
18
|
+
@version = run_command 'import sys; print "%d.%d" % sys.version_info[:2]'
|
19
|
+
|
20
|
+
@realname = @python.dup
|
21
|
+
if @realname !~ /#{@version}$/
|
22
|
+
@realname = "#{@python}#{@version}"
|
23
|
+
end
|
24
|
+
@basename = File.basename(@realname)
|
25
|
+
|
26
|
+
@sys_prefix = run_command 'import sys; print sys.prefix'
|
27
|
+
@library = find_python_lib
|
28
|
+
end
|
29
|
+
|
30
|
+
def find_python_lib
|
31
|
+
# By default, the library name will be something like
|
32
|
+
# libpython2.6.so, but that won't always work.
|
33
|
+
libbase = "#{FFI::Platform::LIBPREFIX}#{@basename}"
|
34
|
+
libext = FFI::Platform::LIBSUFFIX
|
35
|
+
libname = "#{libbase}.#{libext}"
|
36
|
+
|
37
|
+
# We may need to look in multiple locations for Python, so let's
|
38
|
+
# build this as an array.
|
39
|
+
locations = [ File.join(@sys_prefix, "lib", libname) ]
|
40
|
+
|
41
|
+
if FFI::Platform.mac?
|
42
|
+
# On the Mac, let's add a special case that has even a different
|
43
|
+
# libname. This may not be fully useful on future versions of OS
|
44
|
+
# X, but it should work on 10.5 and 10.6. Even if it doesn't, the
|
45
|
+
# next step will (/usr/lib/libpython<version>.dylib is a symlink
|
46
|
+
# to the correct location).
|
47
|
+
locations << File.join(@sys_prefix, "Python")
|
48
|
+
# Let's also look in the location that was originally set in this
|
49
|
+
# library:
|
50
|
+
File.join(@sys_prefix, "lib", "#{@realname}", "config", libname)
|
51
|
+
end
|
52
|
+
|
53
|
+
if FFI::Platform.unix?
|
54
|
+
# On Unixes, let's look in some standard alternative places, too.
|
55
|
+
# Just in case. Some Unixes don't include a .so symlink when they
|
56
|
+
# should, so let's look for the base case of .so.1, too.
|
57
|
+
[ libname, "#{libname}.1" ].each do |name|
|
58
|
+
locations << File.join("/opt/local/lib", name)
|
59
|
+
locations << File.join("/opt/lib", name)
|
60
|
+
locations << File.join("/usr/local/lib", name)
|
61
|
+
locations << File.join("/usr/lib", name)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Let's add alternative extensions; again, just in case.
|
66
|
+
locations.dup.each do |location|
|
67
|
+
path = File.dirname(location)
|
68
|
+
base = File.basename(location, ".#{libext}")
|
69
|
+
locations << File.join(path, "#{base}.so") # Standard Unix
|
70
|
+
locations << File.join(path, "#{base}.dylib") # Mac OS X
|
71
|
+
locations << File.join(path, "#{base}.dll") # Windows
|
72
|
+
locations << File.join(path, "#{base}.a") # Non-DLL
|
73
|
+
end
|
74
|
+
|
75
|
+
# Remove redundant locations
|
76
|
+
locations.uniq!
|
77
|
+
|
78
|
+
library = nil
|
79
|
+
|
80
|
+
locations.each do |location|
|
81
|
+
if File.exists? location
|
82
|
+
library = location
|
83
|
+
break
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
library
|
88
|
+
end
|
89
|
+
private :find_python_lib
|
90
|
+
|
91
|
+
# The python executable to use.
|
92
|
+
attr_reader :python
|
93
|
+
# The real name of the python executable (with version).
|
94
|
+
attr_reader :realname
|
95
|
+
# The sys.prefix for Python.
|
96
|
+
attr_reader :sys_prefix
|
97
|
+
# The Python library.
|
98
|
+
attr_reader :library
|
99
|
+
# The version
|
100
|
+
attr_reader :version
|
101
|
+
|
102
|
+
# Run a Python command-line command.
|
103
|
+
def run_command(command)
|
104
|
+
%x(#{@python} -c '#{command}').chomp if @python
|
105
|
+
end
|
106
|
+
|
107
|
+
def to_s
|
108
|
+
@realname
|
109
|
+
end
|
110
|
+
|
111
|
+
def inspect
|
112
|
+
if @python
|
113
|
+
"#<#{realname} #{sys_prefix}>"
|
114
|
+
else
|
115
|
+
"#<invalid interpreter>"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def invalidate!
|
120
|
+
@python = @version = @realname = @sys_prefix = @library = nil
|
121
|
+
end
|
122
|
+
private :invalidate!
|
123
|
+
end
|
@@ -5,69 +5,80 @@ require 'rubypython/operators'
|
|
5
5
|
require 'rubypython/blankobject'
|
6
6
|
|
7
7
|
module RubyPython
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#method calls to
|
11
|
-
#
|
12
|
-
#will decrement its objects reference count when it is garbage collected.
|
8
|
+
# In most cases, users will interact with RubyPyProxy objects that hold
|
9
|
+
# references to active objects in the \Python interpreter. RubyPyProxy
|
10
|
+
# delegates method calls to \Python objects, wrapping and returning the
|
11
|
+
# results as RubyPyProxy objects.
|
13
12
|
#
|
14
|
-
#
|
15
|
-
#is
|
13
|
+
# The allocation, deallocation, and reference counting on RubyPyProxy
|
14
|
+
# objects is automatic: RubyPython takes care of it all. When the object
|
15
|
+
# is garbage collected, the instance will automatically decrement its
|
16
|
+
# object reference count.
|
16
17
|
#
|
17
|
-
#
|
18
|
-
|
19
|
-
#Any method which is forwarded to a Python object may be called with
|
20
|
-
#a block. The result of the method will passed as the argument to
|
21
|
-
#that block.
|
18
|
+
# [NOTE:] All RubyPyProxy objects become invalid when the \Python
|
19
|
+
# interpreter is halted.
|
22
20
|
#
|
23
|
-
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
# irb(main):003:1* 2*f.rubify
|
28
|
-
# irb(main):004:1> end
|
29
|
-
# => 20.0
|
30
|
-
# irb(main):005:0> RubyPython.stop
|
31
|
-
# => true
|
21
|
+
# == Calling Methods With Blocks
|
22
|
+
# Any method which is forwarded to a \Python object may be called with a
|
23
|
+
# block. The result of the method will passed as the argument to that
|
24
|
+
# block.
|
32
25
|
#
|
26
|
+
# RubyPython.run do
|
27
|
+
# sys = RubyPython.import 'sys'
|
28
|
+
# sys.version { |v| v.rubify.split(' ') }
|
29
|
+
# end
|
30
|
+
# # => [ "2.6.1", … ]
|
31
|
+
#
|
32
|
+
# == Passing Procs and Methods to \Python Methods
|
33
|
+
# RubyPython supports passing Proc and Method objects to \Python methods.
|
34
|
+
# The Proc or Method object must be passed explicitly. As seen above,
|
35
|
+
# supplying a block to a method will result in the return value of the
|
36
|
+
# method call being passed to the block.
|
33
37
|
#
|
34
|
-
#
|
35
|
-
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#seen above, supplying a block to a method will result in the return
|
39
|
-
#value of the method call being passed to the block.
|
38
|
+
# When a Proc or Method is supplied as a callback, then arguments that it
|
39
|
+
# will be called with will be wrapped \Python objects. It will therefore
|
40
|
+
# typically be necessary to write a wrapper around any Ruby callback that
|
41
|
+
# requires native Ruby objects.
|
40
42
|
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#@example Passing a Proc to Python
|
45
|
-
# #Python Code
|
46
|
-
# def apply_callback(callback, argument):
|
47
|
-
# return callback(argument)
|
48
|
-
#
|
49
|
-
# #IRB Session
|
50
|
-
# irb(main):001:0> RubyPython.start
|
51
|
-
# => true
|
52
|
-
# irb(main):002:0> sys = RubyPython.import 'sys'
|
53
|
-
# => <module 'sys' (built-in)>
|
54
|
-
# irb(main):003:0> sys.path.append('.')
|
55
|
-
# => None
|
56
|
-
# irb(main):004:0> sample = RubyPython.import 'sample'
|
57
|
-
# => <module 'sample' from './sample.pyc'>
|
58
|
-
# irb(main):005:0> callback = Proc.new do |arg|
|
59
|
-
# irb(main):006:1* arg * 2
|
60
|
-
# irb(main):007:1> end
|
61
|
-
# => #<Proc:0x000001018df490@(irb):5>
|
62
|
-
# irb(main):008:0> sample.apply_callback(callback, 21).rubify
|
63
|
-
# => 42
|
64
|
-
# irb(main):009:0> RubyPython.stop
|
43
|
+
# # Python Code: sample.py
|
44
|
+
# def apply_callback(callback, argument):
|
45
|
+
# return callback(argument)
|
65
46
|
#
|
47
|
+
# # IRB Session
|
48
|
+
# >> RubyPython.start
|
49
|
+
# => true
|
50
|
+
# >> sys = RubyPython.import 'sys'
|
51
|
+
# => <module 'sys' (built-in)>
|
52
|
+
# >> sys.path.append('.')
|
53
|
+
# => None
|
54
|
+
# >> sample = RubyPython.import 'sample'
|
55
|
+
# => <module 'sample' from './sample.pyc'>
|
56
|
+
# >> callback = Proc.new { |arg| arg * 2 }
|
57
|
+
# => # <Proc:0x000001018df490@(irb):5>
|
58
|
+
# >> sample.apply_callback(callback, 21).rubify
|
59
|
+
# => 42
|
60
|
+
# >> RubyPython.stop
|
61
|
+
# => true
|
66
62
|
class RubyPyProxy < BlankObject
|
67
63
|
include Operators
|
68
64
|
|
69
65
|
attr_reader :pObject
|
70
66
|
|
67
|
+
# Creates a \Python proxy for the provided Ruby object.
|
68
|
+
#
|
69
|
+
# Only the following Ruby types can be represented in \Python:
|
70
|
+
# * String
|
71
|
+
# * Array
|
72
|
+
# * Hash
|
73
|
+
# * Fixnum
|
74
|
+
# * Bignum
|
75
|
+
# * Float
|
76
|
+
# * Symbol (as a String)
|
77
|
+
# * Proc
|
78
|
+
# * Method
|
79
|
+
# * +true+ (as True)
|
80
|
+
# * +false+ (as False)
|
81
|
+
# * +nil+ (as None)
|
71
82
|
def initialize(pObject)
|
72
83
|
if pObject.kind_of? PyObject
|
73
84
|
@pObject = pObject
|
@@ -76,12 +87,12 @@ module RubyPython
|
|
76
87
|
end
|
77
88
|
end
|
78
89
|
|
79
|
-
#Handles the job of wrapping up anything returned by a
|
80
|
-
#instance. The behavior differs depending on the value of
|
81
|
-
#
|
82
|
-
#object is wrapped by an instance of
|
83
|
-
#active, RubyPython first attempts to convert the returned object to a
|
84
|
-
#native Ruby type, and then only wraps the object if this fails.
|
90
|
+
# Handles the job of wrapping up anything returned by a RubyPyProxy
|
91
|
+
# instance. The behavior differs depending on the value of
|
92
|
+
# +RubyPython.legacy_mode+. If legacy mode is inactive, every returned
|
93
|
+
# object is wrapped by an instance of +RubyPyProxy+. If legacy mode is
|
94
|
+
# active, RubyPython first attempts to convert the returned object to a
|
95
|
+
# native Ruby type, and then only wraps the object if this fails.
|
85
96
|
def _wrap(pyobject)
|
86
97
|
if pyobject.class?
|
87
98
|
RubyPyClass.new(pyobject)
|
@@ -93,28 +104,45 @@ module RubyPython
|
|
93
104
|
rescue Conversion::UnsupportedConversion => exc
|
94
105
|
RubyPyProxy.new pyobject
|
95
106
|
end
|
107
|
+
private :_wrap
|
96
108
|
|
97
109
|
reveal(:respond_to?)
|
98
110
|
|
99
|
-
#
|
111
|
+
# The standard Ruby +#respond_to?+ method has been renamed to allow
|
112
|
+
# RubyPython to query if the proxied \Python object supports the method
|
113
|
+
# desired. Setter methods (e.g., +foo=+) are always supported.
|
100
114
|
alias :is_real_method? :respond_to?
|
101
115
|
|
102
|
-
#RubyPython checks the attribute dictionary of the wrapped object
|
103
|
-
#
|
104
|
-
#
|
105
|
-
#respond_to? method has been aliased to is_real_method?.
|
116
|
+
# RubyPython checks the attribute dictionary of the wrapped object to
|
117
|
+
# check whether it will respond to a method call. This should not return
|
118
|
+
# false positives but it may return false negatives. The built-in Ruby
|
119
|
+
# respond_to? method has been aliased to is_real_method?.
|
106
120
|
def respond_to?(mname)
|
107
121
|
return true if is_real_method?(mname)
|
108
122
|
mname = mname.to_s
|
109
|
-
return true if mname
|
123
|
+
return true if mname =~ /=$/
|
110
124
|
@pObject.hasAttr(mname)
|
111
125
|
end
|
112
126
|
|
113
|
-
#
|
127
|
+
# Delegates method calls to proxied \Python objects.
|
128
|
+
#
|
129
|
+
# == Delegation Rules
|
130
|
+
# 1. If the method ends with a question-mark (e.g., +nil?+), it can only
|
131
|
+
# be a Ruby method on RubyPyProxy. Attempt to reveal it (RubyPyProxy
|
132
|
+
# is a BlankObject) and call it.
|
133
|
+
# 2. If the method ends with equals signs (e.g., +value=+) it's a setter
|
134
|
+
# and we can always set an attribute on a \Python object.
|
135
|
+
# 3. If the method ends with an exclamation point (e.g., +foo!+) we are
|
136
|
+
# attempting to call a method with keyword arguments.
|
137
|
+
# 4. The Python method or value will be called, if it's callable.
|
138
|
+
# 5. RubyPython will wrap the return value in a RubyPyProxy object
|
139
|
+
# (unless legacy_mode has been turned on).
|
140
|
+
# 6. If a block has been provided, the wrapped return value will be
|
141
|
+
# passed into the block.
|
114
142
|
def method_missing(name, *args, &block)
|
115
143
|
name = name.to_s
|
116
144
|
|
117
|
-
if
|
145
|
+
if name =~ /\?$/
|
118
146
|
begin
|
119
147
|
RubyPyProxy.reveal(name.to_sym)
|
120
148
|
return self.__send__(name.to_sym, *args, &block)
|
@@ -124,24 +152,17 @@ module RubyPython
|
|
124
152
|
end
|
125
153
|
end
|
126
154
|
|
155
|
+
kwargs = false
|
127
156
|
|
128
|
-
if
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
if(!@pObject.hasAttr(name) and !setter)
|
136
|
-
raise NoMethodError.new(name)
|
157
|
+
if name =~ /=$/
|
158
|
+
return @pObject.setAttr(name.chomp('='),
|
159
|
+
PyObject.convert(*args).first)
|
160
|
+
elsif name =~ /!$/
|
161
|
+
kwargs = true
|
162
|
+
name.chomp! "!"
|
137
163
|
end
|
138
164
|
|
139
|
-
|
140
|
-
args = PyObject.convert(*args)
|
141
|
-
|
142
|
-
if setter
|
143
|
-
return @pObject.setAttr(name, args[0])
|
144
|
-
end
|
165
|
+
raise NoMethodError.new(name) if !@pObject.hasAttr(name)
|
145
166
|
|
146
167
|
pFunc = @pObject.getAttr(name)
|
147
168
|
|
@@ -149,77 +170,108 @@ module RubyPython
|
|
149
170
|
if args.empty? and pFunc.class?
|
150
171
|
pReturn = pFunc
|
151
172
|
else
|
173
|
+
if kwargs and args.last.is_a?(Hash)
|
174
|
+
pKeywords = PyObject.convert(args.pop).first
|
175
|
+
end
|
176
|
+
|
177
|
+
orig_args = args
|
178
|
+
args = PyObject.convert(*args)
|
152
179
|
pTuple = PyObject.buildArgTuple(*args)
|
153
|
-
pReturn =
|
154
|
-
|
155
|
-
|
180
|
+
pReturn = if pKeywords
|
181
|
+
pFunc.callObjectKeywords(pTuple, pKeywords)
|
182
|
+
else
|
183
|
+
pFunc.callObject(pTuple)
|
184
|
+
end
|
185
|
+
|
186
|
+
# Clean up unused Python vars instead of waiting on Ruby's GC to
|
187
|
+
# do it.
|
188
|
+
pFunc.xDecref
|
189
|
+
pTuple.xDecref
|
190
|
+
pKeywords.xDecref if pKeywords
|
191
|
+
orig_args.each_with_index do |arg, i|
|
192
|
+
# Only decref objects that were created in PyObject.convert.
|
193
|
+
if !arg.kind_of?(RubyPython::PyObject) and !arg.kind_of?(RubyPython::RubyPyProxy)
|
194
|
+
args[i].xDecref
|
195
|
+
end
|
156
196
|
end
|
197
|
+
|
198
|
+
raise PythonError.handle_error if PythonError.error?
|
157
199
|
end
|
158
200
|
else
|
159
201
|
pReturn = pFunc
|
160
202
|
end
|
161
203
|
|
162
|
-
|
204
|
+
result = _wrap(pReturn)
|
205
|
+
|
206
|
+
if block
|
207
|
+
block.call(result)
|
208
|
+
else
|
209
|
+
result
|
210
|
+
end
|
163
211
|
end
|
164
212
|
|
165
|
-
#RubyPython will attempt to translate the wrapped object into a native
|
166
|
-
#Ruby object. This will only succeed for simple
|
213
|
+
# RubyPython will attempt to translate the wrapped object into a native
|
214
|
+
# Ruby object. This will only succeed for simple built-in type.
|
167
215
|
def rubify
|
168
216
|
@pObject.rubify
|
169
217
|
end
|
170
218
|
|
171
|
-
#Returns the
|
172
|
-
#object's
|
173
|
-
#
|
174
|
-
#@return [String]
|
219
|
+
# Returns the String representation of the wrapped object via a call to
|
220
|
+
# the object's <tt>__repr__</tt> method, or the +repr+ method in PyMain.
|
175
221
|
def inspect
|
176
222
|
self.__repr__.rubify
|
177
223
|
rescue PythonError, NoMethodError
|
178
224
|
RubyPython::PyMain.repr(self).rubify
|
179
225
|
end
|
180
226
|
|
181
|
-
#Returns the string representation of the wrapped object via a call to
|
182
|
-
#object's
|
183
|
-
#
|
184
|
-
#@return [String]
|
227
|
+
# Returns the string representation of the wrapped object via a call to
|
228
|
+
# the object's <tt>__str__</tt> method or the +str+ method in PyMain.
|
185
229
|
def to_s
|
186
230
|
self.__str__.rubify
|
187
231
|
rescue PythonError, NoMethodError
|
188
232
|
RubyPython::PyMain.str(self).rubify
|
189
233
|
end
|
190
234
|
|
191
|
-
#Converts the wrapped Python object to a Ruby Array. Note that this
|
192
|
-
#one level, so a nested array will remain a proxy object.
|
193
|
-
#objects which have an
|
235
|
+
# Converts the wrapped \Python object to a Ruby Array. Note that this
|
236
|
+
# only converts one level, so a nested array will remain a proxy object.
|
237
|
+
# Only wrapped objects which have an <tt>__iter__</tt> method may be
|
238
|
+
# converted using +to_a+.
|
239
|
+
#
|
240
|
+
# Note that for \Python Dict objects, this method returns what you would
|
241
|
+
# get in \Python, not in Ruby: +a_dict.to_a+ returns an array of the
|
242
|
+
# dictionary's keys.
|
194
243
|
#
|
195
|
-
#
|
196
|
-
#
|
197
|
-
#
|
198
|
-
|
199
|
-
|
200
|
-
#
|
201
|
-
#
|
202
|
-
#
|
203
|
-
#
|
204
|
-
#
|
205
|
-
#
|
206
|
-
# irb(main):004:0> a_list.to_a
|
207
|
-
# => [1, 'a', 2, 'b']
|
208
|
-
# irb(main):005:0> RubyPython.stop
|
209
|
-
# => true
|
210
|
-
#
|
211
|
-
#@example Dict
|
212
|
-
# irb(main):001:0> RubyPython.start
|
213
|
-
# => true
|
214
|
-
# irb(main):002:0> a_dict = RubyPython::RubyPyProxy.new({1 => '2', :three => [4,5]})
|
215
|
-
# => {1: '2', 'three': [4, 5]}
|
216
|
-
# irb(main):003:0> a_dict.kind_of? RubyPython::RubyPyProxy
|
217
|
-
# => true
|
218
|
-
# irb(main):004:0> a_dict.to_a
|
219
|
-
# => [1, 'three']
|
220
|
-
# irb(main):005:0> RubyPython.stop
|
221
|
-
# => true
|
244
|
+
# === List #to_a Returns an Array
|
245
|
+
# >> RubyPython.start
|
246
|
+
# => true
|
247
|
+
# >> list = RubyPython::RubyPyProxy.new([1, 'a', 2, 'b'])
|
248
|
+
# => [1, 'a', 2, 'b']
|
249
|
+
# >> list.kind_of? RubyPython::RubyPyProxy
|
250
|
+
# => true
|
251
|
+
# >> list.to_a
|
252
|
+
# => [1, 'a', 2, 'b']
|
253
|
+
# >> RubyPython.stop
|
254
|
+
# => true
|
222
255
|
#
|
256
|
+
# === Dict #to_a Returns An Array of Keys
|
257
|
+
# >> RubyPython.start
|
258
|
+
# => true
|
259
|
+
# >> dict = RubyPython::RubyPyProxy.new({1 => '2', :three => [4,5]})
|
260
|
+
# => {1: '2', 'three': [4, 5]}
|
261
|
+
# >> dict.kind_of? RubyPython::RubyPyProxy
|
262
|
+
# => true
|
263
|
+
# >> dict.to_a
|
264
|
+
# => [1, 'three']
|
265
|
+
# >> RubyPython.stop
|
266
|
+
# => true
|
267
|
+
#
|
268
|
+
# === Non-Array Values Do Not Convert
|
269
|
+
# >> RubyPython.start
|
270
|
+
# => true
|
271
|
+
# >> item = RubyPython::RubyPyProxy.new(42)
|
272
|
+
# => 42
|
273
|
+
# >> item.to_a
|
274
|
+
# NoMethodError: __iter__
|
223
275
|
def to_a
|
224
276
|
iter = self.__iter__
|
225
277
|
ary = []
|
@@ -231,30 +283,54 @@ module RubyPython
|
|
231
283
|
ary
|
232
284
|
end
|
233
285
|
|
234
|
-
|
286
|
+
# Returns the methods on the \Python object by calling the +dir+
|
287
|
+
# built-in.
|
288
|
+
def methods
|
289
|
+
pObject.dir.map { |x| x.to_sym }
|
290
|
+
end
|
235
291
|
|
236
|
-
|
237
|
-
|
238
|
-
|
292
|
+
# Creates a PyEnumerable for this object. The object must have the
|
293
|
+
# <tt>__iter__</tt> method.
|
294
|
+
def to_enum
|
295
|
+
PyEnumerable.new(@pObject)
|
296
|
+
end
|
239
297
|
end
|
240
298
|
|
241
|
-
#A class to wrap Python
|
242
|
-
|
299
|
+
# A class to wrap \Python modules. It behaves exactly the same as
|
300
|
+
# RubyPyProxy. It is just here for Bookkeeping and aesthetics.
|
301
|
+
class RubyPyModule < RubyPyProxy; end
|
243
302
|
|
244
|
-
|
245
|
-
|
303
|
+
# A class to wrap \Python classes.
|
304
|
+
class RubyPyClass < RubyPyProxy
|
305
|
+
# Create an instance of the wrapped class. This is a workaround for the
|
306
|
+
# fact that \Python classes are meant to be callable.
|
246
307
|
def new(*args)
|
247
308
|
args = PyObject.convert(*args)
|
248
309
|
pTuple = PyObject.buildArgTuple(*args)
|
249
310
|
pReturn = @pObject.callObject(pTuple)
|
250
|
-
if PythonError.error?
|
251
|
-
raise PythonError.handle_error
|
252
|
-
end
|
311
|
+
raise PythonError.handle_error if PythonError.error?
|
253
312
|
RubyPyInstance.new pReturn
|
254
313
|
end
|
255
314
|
end
|
256
315
|
|
257
|
-
#An object representing an instance of a Python
|
258
|
-
|
316
|
+
# An object representing an instance of a \Python class. It behaves
|
317
|
+
# exactly the same as RubyPyProxy. It is just here for Bookkeeping and
|
318
|
+
# aesthetics.
|
319
|
+
class RubyPyInstance < RubyPyProxy; end
|
320
|
+
|
321
|
+
# An object representing a Python enumerable object.
|
322
|
+
class PyEnumerable < RubyPyProxy
|
323
|
+
include Enumerable
|
324
|
+
|
325
|
+
def each
|
326
|
+
iter = self.__iter__
|
327
|
+
loop do
|
328
|
+
begin
|
329
|
+
yield iter.next
|
330
|
+
rescue RubyPython::PythonError => exc
|
331
|
+
return if exc.message =~ /StopIteration/
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
259
335
|
end
|
260
336
|
end
|