rubypython 0.5.3 → 0.6.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.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
|