rubypython 0.5.3 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/History.rdoc +32 -0
- data/Manifest.txt +2 -5
- data/lib/rubypython.rb +73 -59
- data/lib/rubypython/conversion.rb +7 -5
- data/lib/rubypython/interpreter.rb +250 -0
- data/lib/rubypython/legacy.rb +2 -23
- data/lib/rubypython/operators.rb +10 -6
- data/lib/rubypython/pymainclass.rb +3 -2
- data/lib/rubypython/pyobject.rb +2 -1
- data/lib/rubypython/python.rb +185 -188
- data/lib/rubypython/rubypyproxy.rb +1 -1
- data/lib/rubypython/tuple.rb +10 -0
- data/spec/conversion_spec.rb +10 -2
- data/spec/python_helpers/basics.py +3 -0
- data/spec/rubypython_spec.rb +0 -24
- data/spec/spec_helper.rb +2 -0
- metadata +122 -122
- metadata.gz.sig +0 -0
- data/.gitignore +0 -13
- data/.hgignore +0 -14
- data/.hgtags +0 -9
- data/lib/rubypython/options.rb +0 -66
- data/lib/rubypython/pythonexec.rb +0 -145
data.tar.gz.sig
ADDED
Binary file
|
data/History.rdoc
CHANGED
@@ -1,3 +1,35 @@
|
|
1
|
+
=== 0.6 / 2011-04-17
|
2
|
+
* Major Enhancements:
|
3
|
+
* Previous versions of RubyPython theoretically allowed the loading of
|
4
|
+
different Python interpreters during a single Ruby session. Because of the
|
5
|
+
likelihood of crashes, this functionality has been removed. Now, if you
|
6
|
+
attempt to start RubyPython more than once while specifying different
|
7
|
+
Python interpreters, RubyPython will print a warning and continue working
|
8
|
+
with the already loaded interpreter.
|
9
|
+
* The Python interpreter DLL will only be loaded once. It is configured with
|
10
|
+
a self-removing method, Interpreter#infect! instead of require/load reload
|
11
|
+
hacks.
|
12
|
+
* Removed RubyPython.configure; since the interpreter can only be configured
|
13
|
+
once, independent configuration no longer makes sense.
|
14
|
+
* Minor Enhancements:
|
15
|
+
* RubyPython interpreter initialization is done with a Mutex synchronization
|
16
|
+
to ensure that only one Python interpreter DLL is loaded.
|
17
|
+
* Added RubyPython::Tuple, a simple subclass of ::Array that will correctly
|
18
|
+
be converted to a Python tuple object such that isinstance(x, tuple)
|
19
|
+
returns True.
|
20
|
+
* Renamed RubyPython::PythonExec to RubyPython::Interpreter. Added some
|
21
|
+
helper methods to assist with comparison. Construction is with an options
|
22
|
+
hash.
|
23
|
+
* The #update methods on Python interpreter observers are now private. This
|
24
|
+
is an implementation detail, not a public interface. (The methods have also
|
25
|
+
been renamed to #python_interpreter_update.)
|
26
|
+
* Deprecation:
|
27
|
+
* RubyPython's legacy mode (automatic unboxing of Python proxy objects where
|
28
|
+
possible) has been deprecated and will be removed in the next non-bugfix
|
29
|
+
release after this version. Introduced a new private method
|
30
|
+
(RubyPython.legacy_mode?) to check if legacy mode is turned on so that the
|
31
|
+
deprecation warning is not printed in all uses of RubyPython.
|
32
|
+
|
1
33
|
=== 0.5.3 / 2011-10-22
|
2
34
|
* Bug Fixes:
|
3
35
|
* Improved 64-bit Python detection on Unixes with Linux-like semantics (e.g.,
|
data/Manifest.txt
CHANGED
@@ -1,7 +1,4 @@
|
|
1
1
|
.autotest
|
2
|
-
.gitignore
|
3
|
-
.hgignore
|
4
|
-
.hgtags
|
5
2
|
.rspec
|
6
3
|
Contributors.rdoc
|
7
4
|
History.rdoc
|
@@ -14,17 +11,17 @@ autotest/discover.rb
|
|
14
11
|
lib/rubypython.rb
|
15
12
|
lib/rubypython/blankobject.rb
|
16
13
|
lib/rubypython/conversion.rb
|
14
|
+
lib/rubypython/interpreter.rb
|
17
15
|
lib/rubypython/legacy.rb
|
18
16
|
lib/rubypython/macros.rb
|
19
17
|
lib/rubypython/operators.rb
|
20
|
-
lib/rubypython/options.rb
|
21
18
|
lib/rubypython/pygenerator.rb
|
22
19
|
lib/rubypython/pymainclass.rb
|
23
20
|
lib/rubypython/pyobject.rb
|
24
21
|
lib/rubypython/python.rb
|
25
22
|
lib/rubypython/pythonerror.rb
|
26
|
-
lib/rubypython/pythonexec.rb
|
27
23
|
lib/rubypython/rubypyproxy.rb
|
24
|
+
lib/rubypython/tuple.rb
|
28
25
|
lib/rubypython/type.rb
|
29
26
|
spec/basic_spec.rb
|
30
27
|
spec/callback_spec.rb
|
data/lib/rubypython.rb
CHANGED
@@ -15,39 +15,34 @@
|
|
15
15
|
# puts cPickle.dumps("RubyPython is awesome!").rubify
|
16
16
|
# RubyPython.stop
|
17
17
|
module RubyPython
|
18
|
-
VERSION = '0.
|
19
|
-
|
20
|
-
# Do not load the FFI interface by default. Wait until the user asks for
|
21
|
-
# it.
|
22
|
-
@load_ffi = false
|
23
|
-
|
24
|
-
# Indicates whether the \Python DLL has been loaded. For internal use
|
25
|
-
# only.
|
26
|
-
def self.load_ffi? #:nodoc:
|
27
|
-
@load_ffi
|
28
|
-
end
|
18
|
+
VERSION = '0.6.0'
|
29
19
|
end
|
30
20
|
|
31
21
|
require 'rubypython/blankobject'
|
32
|
-
require 'rubypython/
|
22
|
+
require 'rubypython/interpreter'
|
33
23
|
require 'rubypython/python'
|
34
24
|
require 'rubypython/pythonerror'
|
35
25
|
require 'rubypython/pyobject'
|
36
26
|
require 'rubypython/rubypyproxy'
|
37
27
|
require 'rubypython/pymainclass'
|
38
28
|
require 'rubypython/pygenerator'
|
29
|
+
require 'rubypython/tuple'
|
30
|
+
require 'thread'
|
39
31
|
|
40
32
|
module RubyPython
|
41
33
|
class << self
|
42
|
-
|
43
|
-
#
|
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.
|
44
39
|
#
|
45
|
-
# ===
|
40
|
+
# === Proxy Mode
|
46
41
|
# By default, +legacy_mode+ is +false+, meaning that any object returned
|
47
|
-
# from a \Python function call will be wrapped in
|
48
|
-
# +RubyPyProxy+ or one of its subclasses. This allows
|
49
|
-
# calls to be forwarded to the \Python object, even if it
|
50
|
-
# be a native Ruby object.
|
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.
|
51
46
|
#
|
52
47
|
# RubyPython.session do
|
53
48
|
# string = RubyPython.import 'string'
|
@@ -60,8 +55,8 @@ module RubyPython
|
|
60
55
|
# If +legacy_mode+ is +true+, RubyPython automatically tries to convert
|
61
56
|
# returned objects to native Ruby object types. If there is no such
|
62
57
|
# conversion, the object remains wrapped in +RubyPyProxy+. This
|
63
|
-
# behaviour is the same as RubyPython 0.2 and earlier. This mode is
|
64
|
-
#
|
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.
|
65
60
|
#
|
66
61
|
# RubyPython.legacy_mode = true
|
67
62
|
# RubyPython.session do
|
@@ -69,10 +64,32 @@ module RubyPython
|
|
69
64
|
# ascii_letters = string.ascii_letters
|
70
65
|
# puts ascii_letters.isalpha # throws NoMethodError
|
71
66
|
# end
|
72
|
-
|
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
|
73
79
|
|
74
|
-
|
75
|
-
|
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
|
76
93
|
# \Python code. Returns +true+ if the interpreter was started; +false+
|
77
94
|
# otherwise.
|
78
95
|
#
|
@@ -90,27 +107,30 @@ module RubyPython
|
|
90
107
|
# sys = RubyPython.import 'sys'
|
91
108
|
# p sys.version # => "2.7.1"
|
92
109
|
# RubyPython.stop
|
93
|
-
#
|
94
|
-
# *NOTE*: In the current version of RubyPython, it _is_ possible to
|
95
|
-
# change \Python interpreters in a single Ruby process execution, but it
|
96
|
-
# is *strongly* discouraged as this may lead to segmentation faults.
|
97
|
-
# This feature is highly experimental and may be disabled in the future.
|
98
110
|
def start(options = {})
|
99
|
-
|
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
|
100
127
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
reload_library
|
128
|
+
unless defined? RubyPython::Python.ffi_libraries
|
129
|
+
Runtime.__send__(:infect!, RubyPython::Python)
|
130
|
+
end
|
105
131
|
end
|
106
132
|
|
107
133
|
return false if RubyPython::Python.Py_IsInitialized != 0
|
108
|
-
|
109
|
-
if @reload
|
110
|
-
reload_library
|
111
|
-
@reload = false
|
112
|
-
end
|
113
|
-
|
114
134
|
RubyPython::Python.Py_Initialize
|
115
135
|
notify :start
|
116
136
|
true
|
@@ -195,7 +215,7 @@ module RubyPython
|
|
195
215
|
# run(options = {}) { block to execute in RubyPython context }
|
196
216
|
def run(options = {}, &block)
|
197
217
|
start(options)
|
198
|
-
module_eval(&block)
|
218
|
+
self.module_eval(&block)
|
199
219
|
ensure
|
200
220
|
stop
|
201
221
|
end
|
@@ -218,23 +238,28 @@ module RubyPython
|
|
218
238
|
# This feature is highly experimental and may be disabled in the future.
|
219
239
|
def start_from_virtualenv(virtualenv)
|
220
240
|
result = start(:python_exe => File.join(virtualenv, "bin", "python"))
|
221
|
-
|
241
|
+
activate_virtualenv
|
222
242
|
result
|
223
243
|
end
|
224
244
|
|
225
|
-
# Returns an object describing the
|
245
|
+
# Returns an object describing the active Python interpreter. Returns
|
246
|
+
# +nil+ if there is no active interpreter.
|
226
247
|
def python
|
227
|
-
|
248
|
+
if self.const_defined? :Runtime
|
249
|
+
self::Runtime
|
250
|
+
else
|
251
|
+
nil
|
252
|
+
end
|
228
253
|
end
|
229
254
|
|
230
255
|
# Used to activate the virtualenv.
|
231
|
-
def
|
256
|
+
def activate_virtualenv
|
232
257
|
imp = import("imp")
|
233
258
|
imp.load_source("activate_this",
|
234
|
-
File.join(File.dirname(RubyPython::
|
259
|
+
File.join(File.dirname(RubyPython::Runtime.python),
|
235
260
|
"activate_this.py"))
|
236
261
|
end
|
237
|
-
private :
|
262
|
+
private :activate_virtualenv
|
238
263
|
|
239
264
|
def add_observer(object)
|
240
265
|
@observers ||= []
|
@@ -247,21 +272,10 @@ module RubyPython
|
|
247
272
|
@observers ||= []
|
248
273
|
@observers.each do |o|
|
249
274
|
next if nil === o
|
250
|
-
o.
|
275
|
+
o.__send__ :python_interpreter_update, status
|
251
276
|
end
|
252
277
|
end
|
253
278
|
private :notify
|
254
|
-
|
255
|
-
def reload_library
|
256
|
-
# Invalidate the current Python instance, if defined.
|
257
|
-
if defined? RubyPython::Python::EXEC and RubyPython::Python::EXEC
|
258
|
-
RubyPython::Python::EXEC.instance_eval { invalidate! }
|
259
|
-
end
|
260
|
-
remove_const :Python
|
261
|
-
load RubyPython::PYTHON_RB
|
262
|
-
true
|
263
|
-
end
|
264
|
-
private :reload_library
|
265
279
|
end
|
266
280
|
|
267
281
|
add_observer PyMain
|
@@ -30,8 +30,8 @@ module RubyPython::Conversion
|
|
30
30
|
pList
|
31
31
|
end
|
32
32
|
|
33
|
-
# Convert a Ruby Array
|
34
|
-
# PyTupleObject.
|
33
|
+
# Convert a Ruby Array (including the subclass RubyPython::Tuple) to
|
34
|
+
# \Python \tuple. Returns an FFI::Pointer to a PyTupleObject.
|
35
35
|
def self.rtopArrayToTuple(rArray)
|
36
36
|
pList = rtopArrayToList(rArray)
|
37
37
|
pTuple = RubyPython::Python.PySequence_Tuple(pList)
|
@@ -126,6 +126,8 @@ module RubyPython::Conversion
|
|
126
126
|
case rObj
|
127
127
|
when String
|
128
128
|
rtopString rObj
|
129
|
+
when RubyPython::Tuple
|
130
|
+
rtopArrayToTuple rObj
|
129
131
|
when Array
|
130
132
|
# If this object is going to be used as a hash key we should make it a
|
131
133
|
# tuple instead of a list
|
@@ -221,13 +223,13 @@ module RubyPython::Conversion
|
|
221
223
|
RubyPython::Python.PyFloat_AsDouble(pNum)
|
222
224
|
end
|
223
225
|
|
224
|
-
# Convert an FFI::Pointer to a \Python Tuple (PyTupleObject) to
|
225
|
-
# Array.
|
226
|
+
# Convert an FFI::Pointer to a \Python Tuple (PyTupleObject) to an
|
227
|
+
# instance of RubyPython::Tuple, a subclass of the Ruby Array class.
|
226
228
|
def self.ptorTuple(pTuple)
|
227
229
|
pList = RubyPython::Python.PySequence_List pTuple
|
228
230
|
rArray = ptorList pList
|
229
231
|
RubyPython::Python.Py_DecRef pList
|
230
|
-
rArray
|
232
|
+
RubyPython::Tuple.tuple(rArray)
|
231
233
|
end
|
232
234
|
|
233
235
|
# Convert an FFI::Pointer to a \Python Dictionary (PyDictObject) to a Ruby
|
@@ -0,0 +1,250 @@
|
|
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
|
+
rc, @python = runpy "import sys; print sys.executable"
|
44
|
+
if rc.exitstatus.nonzero?
|
45
|
+
raise RubyPython::InvalidInterpreter, "An invalid interpreter was specified."
|
46
|
+
end
|
47
|
+
rc, @version = runpy "import sys; print '%d.%d' % sys.version_info[:2]"
|
48
|
+
rc, @sys_prefix = runpy "import sys; print sys.prefix"
|
49
|
+
|
50
|
+
if FFI::Platform.windows?
|
51
|
+
flat_version = @version.tr('.', '')
|
52
|
+
basename = File.basename(@python, '.exe')
|
53
|
+
|
54
|
+
if basename =~ /(?:#{@version}|#{flat_version})$/
|
55
|
+
@version_name = basename
|
56
|
+
else
|
57
|
+
@version_name = "#{basename}#{flat_version}"
|
58
|
+
end
|
59
|
+
else
|
60
|
+
basename = File.basename(@python)
|
61
|
+
|
62
|
+
if basename =~ /#{@version}/
|
63
|
+
@version_name = basename
|
64
|
+
else
|
65
|
+
@version_name = "#{basename}#{@version}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
@library = find_python_lib
|
70
|
+
end
|
71
|
+
|
72
|
+
def find_python_lib
|
73
|
+
# By default, the library name will be something like
|
74
|
+
# libpython2.6.so, but that won't always work.
|
75
|
+
@libbase = "#{FFI::Platform::LIBPREFIX}#{@version_name}"
|
76
|
+
@libext = FFI::Platform::LIBSUFFIX
|
77
|
+
@libname = "#{@libbase}.#{@libext}"
|
78
|
+
|
79
|
+
# We may need to look in multiple locations for Python, so let's
|
80
|
+
# build this as an array.
|
81
|
+
@locations = [ File.join(@sys_prefix, "lib", @libname) ]
|
82
|
+
|
83
|
+
if FFI::Platform.mac?
|
84
|
+
# On the Mac, let's add a special case that has even a different
|
85
|
+
# @libname. This may not be fully useful on future versions of OS
|
86
|
+
# X, but it should work on 10.5 and 10.6. Even if it doesn't, the
|
87
|
+
# next step will (/usr/lib/libpython<version>.dylib is a symlink
|
88
|
+
# to the correct location).
|
89
|
+
@locations << File.join(@sys_prefix, "Python")
|
90
|
+
# Let's also look in the location that was originally set in this
|
91
|
+
# library:
|
92
|
+
File.join(@sys_prefix, "lib", "#{@realname}", "config", @libname)
|
93
|
+
end
|
94
|
+
|
95
|
+
if FFI::Platform.unix?
|
96
|
+
# On Unixes, let's look in some standard alternative places, too.
|
97
|
+
# Just in case. Some Unixes don't include a .so symlink when they
|
98
|
+
# should, so let's look for the base case of .so.1, too.
|
99
|
+
[ @libname, "#{@libname}.1" ].each do |name|
|
100
|
+
@locations << File.join("/opt/local/lib", name)
|
101
|
+
@locations << File.join("/opt/lib", name)
|
102
|
+
@locations << File.join("/usr/local/lib", name)
|
103
|
+
@locations << File.join("/usr/lib", name)
|
104
|
+
@locations << File.join("/opt/local/lib64", name)
|
105
|
+
@locations << File.join("/opt/lib64", name)
|
106
|
+
@locations << File.join("/usr/local/lib64", name)
|
107
|
+
@locations << File.join("/usr/lib64", name)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
if FFI::Platform.windows?
|
112
|
+
# On Windows, the appropriate DLL is usually be found in
|
113
|
+
# %SYSTEMROOT%\system or %SYSTEMROOT%\system32; as a fallback we'll
|
114
|
+
# use C:\Windows\system{,32} as well as the install directory and the
|
115
|
+
# install directory + libs.
|
116
|
+
system_root = File.expand_path(ENV['SYSTEMROOT']).gsub(/\\/, '/')
|
117
|
+
@locations << File.join(system_root, 'system', @libname)
|
118
|
+
@locations << File.join(system_root, 'system32', @libname)
|
119
|
+
@locations << File.join("C:/WINDOWS", "System", @libname)
|
120
|
+
@locations << File.join("C:/WINDOWS", "System32", @libname)
|
121
|
+
@locations << File.join(sys_prefix, @libname)
|
122
|
+
@locations << File.join(sys_prefix, 'libs', @libname)
|
123
|
+
@locations << File.join(system_root, "SysWOW64", @libname)
|
124
|
+
@locations << File.join("C:/WINDOWS", "SysWOW64", @libname)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Let's add alternative extensions; again, just in case.
|
128
|
+
@locations.dup.each do |location|
|
129
|
+
path = File.dirname(location)
|
130
|
+
base = File.basename(location, ".#{@libext}")
|
131
|
+
@locations << File.join(path, "#{base}.so") # Standard Unix
|
132
|
+
@locations << File.join(path, "#{base}.dylib") # Mac OS X
|
133
|
+
@locations << File.join(path, "#{base}.dll") # Windows
|
134
|
+
end
|
135
|
+
|
136
|
+
# Remove redundant locations
|
137
|
+
@locations.uniq!
|
138
|
+
|
139
|
+
library = nil
|
140
|
+
|
141
|
+
@locations.each do |location|
|
142
|
+
if File.exists? location
|
143
|
+
library = location
|
144
|
+
break
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
library
|
149
|
+
end
|
150
|
+
private :find_python_lib
|
151
|
+
|
152
|
+
def valid?
|
153
|
+
if @python.nil? or @python.empty?
|
154
|
+
false
|
155
|
+
elsif @library.nil? or @library.empty?
|
156
|
+
false
|
157
|
+
else
|
158
|
+
true
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
##
|
163
|
+
# The name of the \Python executable that is used. This is the value of
|
164
|
+
# 'sys.executable' for the \Python interpreter provided in
|
165
|
+
# <tt>:python_exe</tt> or 'python' if it is not provided.
|
166
|
+
#
|
167
|
+
# On Mac OS X Lion (10.7), this value is '/usr/bin/python' for 'python'.
|
168
|
+
attr_reader :python
|
169
|
+
##
|
170
|
+
# The version of the \Python interpreter. This is a decimalized version of
|
171
|
+
# 'sys.version_info[:2]' (such that \Python 2.7.1 is reported as '2.7').
|
172
|
+
attr_reader :version
|
173
|
+
##
|
174
|
+
# The system prefix for the \Python interpreter. This is the value of
|
175
|
+
# 'sys.prefix'.
|
176
|
+
attr_reader :sys_prefix
|
177
|
+
##
|
178
|
+
# The basename of the \Python interpreter with a version number. This is
|
179
|
+
# mostly an intermediate value used to find the shared \Python library,
|
180
|
+
# but /usr/bin/python is often a link to /usr/bin/python2.7 so it may be
|
181
|
+
# of value. Note that this does *not* include the full path to the
|
182
|
+
# interpreter.
|
183
|
+
attr_reader :version_name
|
184
|
+
|
185
|
+
# The \Python library.
|
186
|
+
attr_reader :library
|
187
|
+
|
188
|
+
# Run a Python command-line command.
|
189
|
+
def runpy(command)
|
190
|
+
i = @python || @python_exe || 'python'
|
191
|
+
if FFI::Platform.windows?
|
192
|
+
o = %x(#{i} -c "#{command}" 2> NUL:)
|
193
|
+
else
|
194
|
+
o = %x(#{i} -c "#{command}" 2> /dev/null)
|
195
|
+
end
|
196
|
+
|
197
|
+
[ $?, o.chomp ]
|
198
|
+
end
|
199
|
+
private :runpy
|
200
|
+
|
201
|
+
def inspect(debug = false)
|
202
|
+
if debug
|
203
|
+
debug_s
|
204
|
+
elsif @python
|
205
|
+
"#<#{self.class}: #{python} v#{version} #{sys_prefix} #{version_name}>"
|
206
|
+
else
|
207
|
+
"#<#{self.class}: invalid interpreter>"
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def debug_s(format = nil)
|
212
|
+
system = ""
|
213
|
+
system << "windows " if FFI::Platform.windows?
|
214
|
+
system << "mac " if FFI::Platform.mac?
|
215
|
+
system << "unix " if FFI::Platform.unix?
|
216
|
+
system << "unknown " if system.empty?
|
217
|
+
|
218
|
+
case format
|
219
|
+
when :report
|
220
|
+
s = <<-EOS
|
221
|
+
python_exe: #{@python_exe}
|
222
|
+
python: #{@python}
|
223
|
+
version: #{@version}
|
224
|
+
sys_prefix: #{@sys_prefix}
|
225
|
+
version_name: #{@version_name}
|
226
|
+
platform: #{system.chomp}
|
227
|
+
library: #{@library.inspect}
|
228
|
+
libbase: #{@libbase}
|
229
|
+
libext: #{@libext}
|
230
|
+
libname: #{@libname}
|
231
|
+
locations: #{@locations.inspect}
|
232
|
+
EOS
|
233
|
+
else
|
234
|
+
s = "#<#{self.class}: "
|
235
|
+
s << "python_exe=#{@python_exe.inspect} "
|
236
|
+
s << "python=#{@python.inspect} "
|
237
|
+
s << "version=#{@version.inspect} "
|
238
|
+
s << "sys_prefix=#{@sys_prefix.inspect} "
|
239
|
+
s << "version_name=#{@version_name.inspect} "
|
240
|
+
s << system
|
241
|
+
s << "library=#{@library.inspect} "
|
242
|
+
s << "libbase=#{@libbase.inspect} "
|
243
|
+
s << "libext=#{@libext.inspect} "
|
244
|
+
s << "libname=#{@libname.inspect} "
|
245
|
+
s << "locations=#{@locations.inspect}"
|
246
|
+
end
|
247
|
+
|
248
|
+
s
|
249
|
+
end
|
250
|
+
end
|