rubypython 0.3.2 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|