lokeshh_rubypython 0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.autotest +3 -0
- data/.gitignore +18 -0
- data/.hgignore +20 -0
- data/.hgtags +12 -0
- data/.rspec +2 -0
- data/Contributors.rdoc +11 -0
- data/History.rdoc +214 -0
- data/License.rdoc +26 -0
- data/Manifest.txt +46 -0
- data/PostInstall.txt +16 -0
- data/README.rdoc +272 -0
- data/Rakefile +118 -0
- data/autotest/discover.rb +1 -0
- data/lib/rubypython/blankobject.rb +23 -0
- data/lib/rubypython/conversion.rb +332 -0
- data/lib/rubypython/interpreter.rb +262 -0
- data/lib/rubypython/macros.rb +56 -0
- data/lib/rubypython/operators.rb +120 -0
- data/lib/rubypython/pygenerator.rb +61 -0
- data/lib/rubypython/pymainclass.rb +80 -0
- data/lib/rubypython/pyobject.rb +189 -0
- data/lib/rubypython/python.rb +199 -0
- data/lib/rubypython/pythonerror.rb +80 -0
- data/lib/rubypython/rubypyproxy.rb +328 -0
- data/lib/rubypython/tuple.rb +10 -0
- data/lib/rubypython/type.rb +20 -0
- data/lib/rubypython.rb +229 -0
- data/spec/basic_spec.rb +50 -0
- data/spec/callback_spec.rb +53 -0
- data/spec/conversion_spec.rb +68 -0
- data/spec/pymainclass_spec.rb +24 -0
- data/spec/pyobject_spec.rb +202 -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 +98 -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 +71 -0
- data/website/index.rhtml +36 -0
- data/website/robots.txt +5 -0
- data/website/stylesheets/960.css +549 -0
- data/website/stylesheets/border-radius.htc +143 -0
- data/website/stylesheets/screen.css +132 -0
- metadata +297 -0
@@ -0,0 +1,262 @@
|
|
1
|
+
# -*- ruby encoding: utf-8 -*-
|
2
|
+
|
3
|
+
class RubyPython::InvalidInterpreter < Exception
|
4
|
+
end
|
5
|
+
|
6
|
+
##
|
7
|
+
# An instance of this class represents information about a particular
|
8
|
+
# \Python interpreter.
|
9
|
+
#
|
10
|
+
# This class represents the current Python interpreter.
|
11
|
+
# A class that represents a \Python executable.
|
12
|
+
#
|
13
|
+
# End users may get the instance that represents the current running \Python
|
14
|
+
# interpreter (from +RubyPython.python+), but should not directly
|
15
|
+
# instantiate this class.
|
16
|
+
class RubyPython::Interpreter
|
17
|
+
|
18
|
+
##
|
19
|
+
# Compare the current Interpreter to the provided Interpreter or
|
20
|
+
# configuration hash. A configuration hash will be converted to an
|
21
|
+
# Interpreter object before being compared.
|
22
|
+
# :python_exe basename is used. If comparing against another Interpreter
|
23
|
+
# object, the Interpreter basename and version are used.
|
24
|
+
def ==(other)
|
25
|
+
other = self.class.new(other) if other.kind_of? Hash
|
26
|
+
return false unless other.kind_of? self.class
|
27
|
+
(self.version == other.version) && (self.version_name == other.version_name)
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# Create a new instance of an Interpreter instance describing a particular
|
32
|
+
# \Python executable and shared library.
|
33
|
+
#
|
34
|
+
# Expects a hash that matches the configuration options provided to
|
35
|
+
# RubyPython.start; currently only one value is recognized in that hash:
|
36
|
+
#
|
37
|
+
# * <tt>:python_exe</tt>: Specifies the name of the python executable to
|
38
|
+
# run.
|
39
|
+
def initialize(options = {})
|
40
|
+
@python_exe = options[:python_exe]
|
41
|
+
# Windows: 'C:\\Python27\python.exe'
|
42
|
+
# Mac OS X: '/usr/bin/
|
43
|
+
|
44
|
+
# The default interpreter might be python3 on some systems
|
45
|
+
rc, majorversion = runpy "import sys; print(sys.version_info[0])"
|
46
|
+
if majorversion == "3"
|
47
|
+
warn "The python interpreter is python 3, switching to python2"
|
48
|
+
@python_exe = "python2"
|
49
|
+
end
|
50
|
+
|
51
|
+
rc, @python = runpy "import sys; print sys.executable"
|
52
|
+
if rc.exitstatus.nonzero?
|
53
|
+
raise RubyPython::InvalidInterpreter, "An invalid interpreter was specified."
|
54
|
+
end
|
55
|
+
rc, @version = runpy "import sys; print '%d.%d' % sys.version_info[:2]"
|
56
|
+
rc, @sys_prefix = runpy "import sys; print sys.prefix"
|
57
|
+
|
58
|
+
if ::FFI::Platform.windows?
|
59
|
+
flat_version = @version.tr('.', '')
|
60
|
+
basename = File.basename(@python, '.exe')
|
61
|
+
|
62
|
+
if basename =~ /(?:#{@version}|#{flat_version})$/
|
63
|
+
@version_name = basename
|
64
|
+
else
|
65
|
+
@version_name = "#{basename}#{flat_version}"
|
66
|
+
end
|
67
|
+
else
|
68
|
+
basename = File.basename(@python)
|
69
|
+
if basename =~ /#{@version}/
|
70
|
+
@version_name = basename
|
71
|
+
elsif basename.end_with?("2")
|
72
|
+
@version_name = "#{basename[0..-2]}#{@version}"
|
73
|
+
else
|
74
|
+
@version_name = "#{basename}#{@version}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
@library = find_python_lib
|
79
|
+
end
|
80
|
+
|
81
|
+
def find_python_lib
|
82
|
+
# By default, the library name will be something like
|
83
|
+
# libpython2.6.so, but that won't always work.
|
84
|
+
@libbase = "#{::FFI::Platform::LIBPREFIX}#{@version_name}"
|
85
|
+
@libext = ::FFI::Platform::LIBSUFFIX
|
86
|
+
@libname = "#{@libbase}.#{@libext}"
|
87
|
+
|
88
|
+
# We may need to look in multiple locations for Python, so let's
|
89
|
+
# build this as an array.
|
90
|
+
@locations = [ File.join(@sys_prefix, "lib", @libname) ]
|
91
|
+
|
92
|
+
if ::FFI::Platform.mac?
|
93
|
+
# On the Mac, let's add a special case that has even a different
|
94
|
+
# @libname. This may not be fully useful on future versions of OS
|
95
|
+
# X, but it should work on 10.5 and 10.6. Even if it doesn't, the
|
96
|
+
# next step will (/usr/lib/libpython<version>.dylib is a symlink
|
97
|
+
# to the correct location).
|
98
|
+
@locations << File.join(@sys_prefix, "Python")
|
99
|
+
# Let's also look in the location that was originally set in this
|
100
|
+
# library:
|
101
|
+
File.join(@sys_prefix, "lib", "#{@realname}", "config", @libname)
|
102
|
+
end
|
103
|
+
|
104
|
+
if ::FFI::Platform.unix?
|
105
|
+
# On Unixes, let's look in some standard alternative places, too.
|
106
|
+
# Just in case. Some Unixes don't include a .so symlink when they
|
107
|
+
# should, so let's look for the base cases of .so.1 and .so.1.0, too.
|
108
|
+
[ @libname, "#{@libname}.1", "#{@libname}.1.0" ].each do |name|
|
109
|
+
if ::FFI::Platform::ARCH != 'i386'
|
110
|
+
@locations << File.join("/opt/local/lib64", name)
|
111
|
+
@locations << File.join("/opt/lib64", name)
|
112
|
+
@locations << File.join("/usr/local/lib64", name)
|
113
|
+
@locations << File.join("/usr/lib64", name)
|
114
|
+
@locations << File.join("/usr/lib/x86_64-linux-gnu", name)
|
115
|
+
end
|
116
|
+
@locations << File.join("/opt/local/lib", name)
|
117
|
+
@locations << File.join("/opt/lib", name)
|
118
|
+
@locations << File.join("/usr/local/lib", name)
|
119
|
+
@locations << File.join("/usr/lib", name)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
if ::FFI::Platform.windows?
|
124
|
+
# On Windows, the appropriate DLL is usually be found in
|
125
|
+
# %SYSTEMROOT%\system or %SYSTEMROOT%\system32; as a fallback we'll
|
126
|
+
# use C:\Windows\system{,32} as well as the install directory and the
|
127
|
+
# install directory + libs.
|
128
|
+
system_root = File.expand_path(ENV['SYSTEMROOT']).gsub(/\\/, '/')
|
129
|
+
@locations << File.join(system_root, 'system', @libname)
|
130
|
+
@locations << File.join(system_root, 'system32', @libname)
|
131
|
+
@locations << File.join("C:/WINDOWS", "System", @libname)
|
132
|
+
@locations << File.join("C:/WINDOWS", "System32", @libname)
|
133
|
+
@locations << File.join(sys_prefix, @libname)
|
134
|
+
@locations << File.join(sys_prefix, 'libs', @libname)
|
135
|
+
@locations << File.join(system_root, "SysWOW64", @libname)
|
136
|
+
@locations << File.join("C:/WINDOWS", "SysWOW64", @libname)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Let's add alternative extensions; again, just in case.
|
140
|
+
@locations.dup.each do |location|
|
141
|
+
path = File.dirname(location)
|
142
|
+
base = File.basename(location, ".#{@libext}")
|
143
|
+
@locations << File.join(path, "#{base}.so") # Standard Unix
|
144
|
+
@locations << File.join(path, "#{base}.dylib") # Mac OS X
|
145
|
+
@locations << File.join(path, "#{base}.dll") # Windows
|
146
|
+
end
|
147
|
+
|
148
|
+
# Remove redundant locations
|
149
|
+
@locations.uniq!
|
150
|
+
|
151
|
+
library = nil
|
152
|
+
|
153
|
+
@locations.each do |location|
|
154
|
+
if File.exists? location
|
155
|
+
library = location
|
156
|
+
break
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
library
|
161
|
+
end
|
162
|
+
private :find_python_lib
|
163
|
+
|
164
|
+
def valid?
|
165
|
+
if @python.nil? or @python.empty?
|
166
|
+
false
|
167
|
+
elsif @library.nil? or @library.empty?
|
168
|
+
false
|
169
|
+
else
|
170
|
+
true
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
##
|
175
|
+
# The name of the \Python executable that is used. This is the value of
|
176
|
+
# 'sys.executable' for the \Python interpreter provided in
|
177
|
+
# <tt>:python_exe</tt> or 'python' if it is not provided.
|
178
|
+
#
|
179
|
+
# On Mac OS X Lion (10.7), this value is '/usr/bin/python' for 'python'.
|
180
|
+
attr_reader :python
|
181
|
+
##
|
182
|
+
# The version of the \Python interpreter. This is a decimalized version of
|
183
|
+
# 'sys.version_info[:2]' (such that \Python 2.7.1 is reported as '2.7').
|
184
|
+
attr_reader :version
|
185
|
+
##
|
186
|
+
# The system prefix for the \Python interpreter. This is the value of
|
187
|
+
# 'sys.prefix'.
|
188
|
+
attr_reader :sys_prefix
|
189
|
+
##
|
190
|
+
# The basename of the \Python interpreter with a version number. This is
|
191
|
+
# mostly an intermediate value used to find the shared \Python library,
|
192
|
+
# but /usr/bin/python is often a link to /usr/bin/python2.7 so it may be
|
193
|
+
# of value. Note that this does *not* include the full path to the
|
194
|
+
# interpreter.
|
195
|
+
attr_reader :version_name
|
196
|
+
|
197
|
+
# The \Python library.
|
198
|
+
attr_reader :library
|
199
|
+
|
200
|
+
# Run a Python command-line command.
|
201
|
+
def runpy(command)
|
202
|
+
i = @python || @python_exe || 'python'
|
203
|
+
if ::FFI::Platform.windows?
|
204
|
+
o = %x(#{i} -c "#{command}" 2> NUL:)
|
205
|
+
else
|
206
|
+
o = %x(#{i} -c "#{command}" 2> /dev/null)
|
207
|
+
end
|
208
|
+
|
209
|
+
[ $?, o.chomp ]
|
210
|
+
end
|
211
|
+
private :runpy
|
212
|
+
|
213
|
+
def inspect(debug = false)
|
214
|
+
if debug
|
215
|
+
debug_s
|
216
|
+
elsif @python
|
217
|
+
"#<#{self.class}: #{python} v#{version} #{sys_prefix} #{version_name}>"
|
218
|
+
else
|
219
|
+
"#<#{self.class}: invalid interpreter>"
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def debug_s(format = nil)
|
224
|
+
system = ""
|
225
|
+
system << "windows " if ::FFI::Platform.windows?
|
226
|
+
system << "mac " if ::FFI::Platform.mac?
|
227
|
+
system << "unix " if ::FFI::Platform.unix?
|
228
|
+
system << "unknown " if system.empty?
|
229
|
+
|
230
|
+
case format
|
231
|
+
when :report
|
232
|
+
s = <<-EOS
|
233
|
+
python_exe: #{@python_exe}
|
234
|
+
python: #{@python}
|
235
|
+
version: #{@version}
|
236
|
+
sys_prefix: #{@sys_prefix}
|
237
|
+
version_name: #{@version_name}
|
238
|
+
platform: #{system.chomp}
|
239
|
+
library: #{@library.inspect}
|
240
|
+
libbase: #{@libbase}
|
241
|
+
libext: #{@libext}
|
242
|
+
libname: #{@libname}
|
243
|
+
locations: #{@locations.inspect}
|
244
|
+
EOS
|
245
|
+
else
|
246
|
+
s = "#<#{self.class}: "
|
247
|
+
s << "python_exe=#{@python_exe.inspect} "
|
248
|
+
s << "python=#{@python.inspect} "
|
249
|
+
s << "version=#{@version.inspect} "
|
250
|
+
s << "sys_prefix=#{@sys_prefix.inspect} "
|
251
|
+
s << "version_name=#{@version_name.inspect} "
|
252
|
+
s << system
|
253
|
+
s << "library=#{@library.inspect} "
|
254
|
+
s << "libbase=#{@libbase.inspect} "
|
255
|
+
s << "libext=#{@libext.inspect} "
|
256
|
+
s << "libname=#{@libname.inspect} "
|
257
|
+
s << "locations=#{@locations.inspect}"
|
258
|
+
end
|
259
|
+
|
260
|
+
s
|
261
|
+
end
|
262
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
require 'rubypython/python'
|
3
|
+
|
4
|
+
# Contains Python C API macros reimplemented in Ruby. For internal use only.
|
5
|
+
module RubyPython::Macros #:nodoc:
|
6
|
+
# Returns the reference count for the provided pointer.
|
7
|
+
def self.Py_REFCNT(pObjPointer)
|
8
|
+
pStruct = RubyPython::Python::PyObjectStruct.new pObjPointer
|
9
|
+
pStruct[:ob_refcnt]
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns the object type for the provided pointer.
|
13
|
+
def self.Py_TYPE(pObjPointer)
|
14
|
+
pStruct = RubyPython::Python::PyObjectStruct.new pObjPointer
|
15
|
+
pStruct[:ob_type]
|
16
|
+
end
|
17
|
+
|
18
|
+
# This has been modified from the C API macro to allow for multiple
|
19
|
+
# pointer objects to be passed. It simplifies a number of checks.
|
20
|
+
def self.PyObject_TypeCheck(pObject, pTypePointer)
|
21
|
+
type = self.Py_TYPE(pObject)
|
22
|
+
|
23
|
+
[ pTypePointer ].flatten.each do |pointer|
|
24
|
+
return 1 if type == pointer
|
25
|
+
end
|
26
|
+
|
27
|
+
return 0
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.Py_True
|
31
|
+
RubyPython::Python.Py_TrueStruct.to_ptr
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.Py_False
|
35
|
+
RubyPython::Python.Py_ZeroStruct.to_ptr
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.Py_None
|
39
|
+
RubyPython::Python.Py_NoneStruct.to_ptr
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.Py_RETURN_FALSE
|
43
|
+
RubyPython::Python.Py_IncRef(self.Py_False)
|
44
|
+
self.Py_False
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.Py_RETURN_TRUE
|
48
|
+
RubyPython::Python.Py_IncRef(self.Py_True)
|
49
|
+
self.Py_True
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.Py_RETURN_NONE
|
53
|
+
RubyPython::Python.Py_IncRef(self.Py_None)
|
54
|
+
self.Py_None
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# A mixin module to provide method delegation to a proxy class. This is done
|
2
|
+
# either by delegating to methods defined on the wrapped object or by using
|
3
|
+
# the \Python _operator_ module. A large number of the methods are
|
4
|
+
# dynamically generated and so their documentation is not provided here. In
|
5
|
+
# general all operators that can be overloaded are delegated.
|
6
|
+
module RubyPython::Operators
|
7
|
+
# Provides access to the \Python _operator_ module.
|
8
|
+
def self.operator_
|
9
|
+
@@operator ||= RubyPython.import('operator')
|
10
|
+
end
|
11
|
+
|
12
|
+
# Creates a method to delegate a binary operation.
|
13
|
+
# [rname] The name of the Ruby method for this operation. Can be either a
|
14
|
+
# Symbol or a String.
|
15
|
+
# [pname] The name of the \Python magic method to which this method should
|
16
|
+
# be delegated.
|
17
|
+
def self.bin_op(rname, pname)
|
18
|
+
define_method rname.to_sym do |other|
|
19
|
+
self.__send__(pname, other)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Creates a method to delegate a relational operator. The result of the
|
24
|
+
# delegated method will always be converted to a Ruby type so that simple
|
25
|
+
# boolean testing may occur. These methods are implemented with calls the
|
26
|
+
# _operator_ module.
|
27
|
+
#
|
28
|
+
# [rname] The name of the Ruby method for this operation. Can be a Symbol
|
29
|
+
# or a String.
|
30
|
+
# [pname] The name of the \Python magic method to which this method should
|
31
|
+
# be delegated.
|
32
|
+
def self.rel_op(rname, pname)
|
33
|
+
define_method rname.to_sym do |other|
|
34
|
+
RubyPython::Operators.operator_.__send__(pname, self, other).rubify
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Creates a method to delegate a relational operator.
|
39
|
+
# These methods are implemented with calls the _operator_ module.
|
40
|
+
# [rname] The name of the Ruby method for this operation. Can be a Symbol
|
41
|
+
# or a String.
|
42
|
+
# [pname] The name of the \Python magic method to which this method should
|
43
|
+
# be delegated.
|
44
|
+
def self.unary_op(rname, pname)
|
45
|
+
define_method rname.to_sym do
|
46
|
+
RubyPython::Operators.operator_.__send__(pname, self)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
[
|
51
|
+
[:+, '__add__'],
|
52
|
+
[:-, '__sub__'],
|
53
|
+
[:*, '__mul__'],
|
54
|
+
[:/, '__div__'],
|
55
|
+
[:&, '__and__'],
|
56
|
+
[:^, '__xor__'],
|
57
|
+
[:%, '__mod__'],
|
58
|
+
[:**, '__pow__'],
|
59
|
+
[:>>, '__rshift__'],
|
60
|
+
[:<<, '__lshift__'],
|
61
|
+
[:|, '__or__']
|
62
|
+
].each do |args|
|
63
|
+
bin_op *args
|
64
|
+
end
|
65
|
+
|
66
|
+
[
|
67
|
+
[:~, :__invert__],
|
68
|
+
[:+@, :__pos__],
|
69
|
+
[:-@, :__neg__]
|
70
|
+
].each do |args|
|
71
|
+
unary_op *args
|
72
|
+
end
|
73
|
+
|
74
|
+
[
|
75
|
+
[:==, 'eq'],
|
76
|
+
[:<, 'lt'],
|
77
|
+
[:<=, 'le'],
|
78
|
+
[:>, 'gt'],
|
79
|
+
[:>=, 'ge'],
|
80
|
+
[:equal?, 'is_']
|
81
|
+
].each do |args|
|
82
|
+
rel_op *args
|
83
|
+
end
|
84
|
+
|
85
|
+
# Delegates object indexed access to the wrapped \Python object.
|
86
|
+
def [](index)
|
87
|
+
self.__getitem__ index
|
88
|
+
end
|
89
|
+
|
90
|
+
# Delegates setting of various indices to the wrapped \Python object.
|
91
|
+
def []=(index, value)
|
92
|
+
self.__setitem__ index, value
|
93
|
+
end
|
94
|
+
|
95
|
+
# Delegates membership testing to \Python.
|
96
|
+
def include?(item)
|
97
|
+
self.__contains__(item).rubify
|
98
|
+
end
|
99
|
+
|
100
|
+
# Delegates Comparison to \Python.
|
101
|
+
def <=>(other)
|
102
|
+
RubyPython::PyMain.cmp(self, other)
|
103
|
+
end
|
104
|
+
|
105
|
+
class << self
|
106
|
+
# Called by RubyPython when the interpreter is started or stopped so
|
107
|
+
# that the necessary preparation or cleanup can be done. For internal
|
108
|
+
# use only.
|
109
|
+
def python_interpreter_update(status)
|
110
|
+
case status
|
111
|
+
when :stop
|
112
|
+
@@operator = nil
|
113
|
+
end
|
114
|
+
end
|
115
|
+
private :python_interpreter_update
|
116
|
+
end
|
117
|
+
|
118
|
+
# Aliases eql? to == for Python objects.
|
119
|
+
alias_method :eql?, :==
|
120
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require "rubypython/python"
|
2
|
+
require "rubypython/conversion"
|
3
|
+
require 'rubypython/macros'
|
4
|
+
require 'rubypython/conversion'
|
5
|
+
require 'rubypython/pyobject'
|
6
|
+
require "rubypython/pymainclass"
|
7
|
+
require "rubypython/rubypyproxy"
|
8
|
+
|
9
|
+
if defined? Fiber
|
10
|
+
module RubyPython
|
11
|
+
class << self
|
12
|
+
# Creates a \Python generator object called +rubypython_generator+
|
13
|
+
# that accepts a callback and yields to it.
|
14
|
+
#
|
15
|
+
# *Note*: This method only exists in the RubyPython if the Fiber
|
16
|
+
# exists.
|
17
|
+
def generator_type
|
18
|
+
@generator_type ||= lambda do
|
19
|
+
code = <<-EOM
|
20
|
+
def rubypython_generator(callback):
|
21
|
+
while True:
|
22
|
+
yield callback()
|
23
|
+
EOM
|
24
|
+
|
25
|
+
globals = PyObject.new({ "__builtins__" => PyMain.builtin.pObject, })
|
26
|
+
empty_hash = PyObject.new({})
|
27
|
+
ptr = Python.PyRun_String(code, Python::PY_FILE_INPUT, globals.pointer, empty_hash.pointer)
|
28
|
+
ptr = Python.PyRun_String("rubypython_generator", Python::PY_EVAL_INPUT, globals.pointer, empty_hash.pointer)
|
29
|
+
raise PythonError.handle_error if PythonError.error?
|
30
|
+
RubyPyProxy.new(PyObject.new(ptr))
|
31
|
+
end.call
|
32
|
+
end
|
33
|
+
|
34
|
+
# Creates a Ruby lambda that acts like a \Python generator. Uses
|
35
|
+
# +RubyPython.generator_type+ and Fiber to work the generator as a
|
36
|
+
# coroutine.
|
37
|
+
#
|
38
|
+
# *Note*: This method only exists in the RubyPython if the Fiber
|
39
|
+
# exists.
|
40
|
+
def generator
|
41
|
+
return lambda do |*args|
|
42
|
+
fib = Fiber.new do
|
43
|
+
yield *args
|
44
|
+
Python.PyErr_SetNone(Python.PyExc_StopIteration)
|
45
|
+
::FFI::Pointer::NULL
|
46
|
+
end
|
47
|
+
generator_type.__call__(lambda { fib.resume })
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Performs a +Fiber.yield+ with the provided arguments, continuing the
|
52
|
+
# coroutine execution of the generator.
|
53
|
+
#
|
54
|
+
# *Note*: This method only exists in the RubyPython if the Fiber
|
55
|
+
# exists.
|
56
|
+
def yield(*args)
|
57
|
+
Fiber.yield(*args)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'rubypython/blankobject'
|
2
|
+
require 'singleton'
|
3
|
+
|
4
|
+
module RubyPython
|
5
|
+
# A singleton object providing access to the \Python <tt>__main__</tt> and
|
6
|
+
# <tt>__builtin__</tt> modules. This can be conveniently accessed through
|
7
|
+
# +PyMain+. The <tt>__main__</tt> namespace is searched before the
|
8
|
+
# <tt>__builtin__</tt> namespace. As such, naming clashes will be resolved
|
9
|
+
# in that order.
|
10
|
+
#
|
11
|
+
# RubyPython::PyMain.dir("dir") # => ['__add__', '__class__', … ]
|
12
|
+
#
|
13
|
+
# === Block Syntax
|
14
|
+
# PyMainClass provides experimental block support for called methods. A
|
15
|
+
# block may be passed to a method call and the object returned by the
|
16
|
+
# function call will be passed as an argument to the block.
|
17
|
+
#
|
18
|
+
# RubyPython::PyMain.dir("dir") { |a| a.rubify.map { |e| e.to_sym } }
|
19
|
+
# # => [:__add__, :__class__, :__contains__, … ]
|
20
|
+
class PyMainClass < RubyPython::BlankObject
|
21
|
+
include Singleton
|
22
|
+
|
23
|
+
# Returns a proxy object wrapping the \Python <tt>__main__</tt> namespace.
|
24
|
+
def main
|
25
|
+
@main ||= RubyPython.import "__main__"
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns a proxy object wrapping the \Python <tt>__builtin__</tt>
|
29
|
+
# namespace.
|
30
|
+
def builtin
|
31
|
+
@builtin ||= RubyPython.import "__builtin__"
|
32
|
+
end
|
33
|
+
|
34
|
+
# Delegates any method calls on this object to the \Python
|
35
|
+
# <tt>__main__</tt> or <tt>__builtin__</tt> namespaces, in that order. If
|
36
|
+
# a block is provided, the result of calling the \Python method will be
|
37
|
+
# yielded as an argument to the block.
|
38
|
+
#
|
39
|
+
# [name] The name of the \Python method or function to call.
|
40
|
+
# [args] The arguments to pass to the \Python method.
|
41
|
+
# [block] A block to execute with the result of calling the \Python
|
42
|
+
# method. If a block is provided, the result of the block is returned,
|
43
|
+
# not the result of the \Python method.
|
44
|
+
def method_missing(name, *args, &block)
|
45
|
+
proxy = if main.respond_to?(name)
|
46
|
+
main
|
47
|
+
elsif builtin.respond_to?(name)
|
48
|
+
builtin
|
49
|
+
else
|
50
|
+
super(name, *args)
|
51
|
+
end
|
52
|
+
result = if proxy.is_real_method?(name)
|
53
|
+
proxy.__send__(name, *args)
|
54
|
+
else
|
55
|
+
proxy.__send__(:method_missing, name, *args)
|
56
|
+
end
|
57
|
+
|
58
|
+
if block
|
59
|
+
block.call(result)
|
60
|
+
else
|
61
|
+
result
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Called by RubyPython when the interpreter is started or stopped so
|
66
|
+
# that the neccesary preparation or cleanup can be done. For internal
|
67
|
+
# use only.
|
68
|
+
def python_interpreter_update(status)
|
69
|
+
case status
|
70
|
+
when :stop
|
71
|
+
@main = nil
|
72
|
+
@builtin = nil
|
73
|
+
end
|
74
|
+
end
|
75
|
+
private :python_interpreter_update
|
76
|
+
end
|
77
|
+
|
78
|
+
# The accessible instance of PyMainClass.
|
79
|
+
PyMain = RubyPython::PyMainClass.instance
|
80
|
+
end
|