pycall 0.1.0.alpha → 0.1.0.alpha.20170224
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +15 -1
- data/Gemfile +5 -0
- data/Guardfile +1 -0
- data/README.md +4 -2
- data/bin/guard +17 -0
- data/bin/rspec +17 -0
- data/config/Guardfile +30 -0
- data/examples/classifier_comparison.rb +130 -0
- data/examples/hist.rb +27 -0
- data/examples/plot_forest_importances_faces.rb +41 -0
- data/examples/sum_benchmarking.rb +48 -0
- data/lib/pycall.rb +14 -4
- data/lib/pycall/conversion.rb +120 -0
- data/lib/pycall/dict.rb +90 -0
- data/lib/pycall/eval.rb +39 -0
- data/lib/pycall/import.rb +74 -0
- data/lib/pycall/init.rb +16 -0
- data/lib/pycall/libpython.rb +331 -0
- data/lib/pycall/list.rb +65 -0
- data/lib/pycall/pyerror.rb +25 -0
- data/lib/pycall/pyobject.rb +185 -0
- data/lib/pycall/pyobject_wrapper.rb +48 -0
- data/lib/pycall/python/investigator.py +6 -0
- data/lib/pycall/set.rb +17 -0
- data/lib/pycall/slice.rb +27 -0
- data/lib/pycall/tuple.rb +54 -0
- data/lib/pycall/types.rb +15 -0
- data/lib/pycall/utils.rb +33 -0
- data/lib/pycall/version.rb +2 -2
- data/pycall.gemspec +3 -1
- metadata +41 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6d6ddea53f723b45bf9207c52746afaa083fba57
|
4
|
+
data.tar.gz: 1739c9f4e3a5d72503427540708f435694dea5aa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '08a91ee3157d872d18725d57ace058d991feb1855250307d9b9cd17feab5081a96ed03ab05941ecec073c22c3a63b2ed8c8c777c83735459e9e7b8e10e7220d7'
|
7
|
+
data.tar.gz: 2359c6d09f3bc687466e41c0e0de2287a89bd4b11587e3384d4a41dd0eb737cf75af387d149efbb8322baa69136d4483f3901728e6b2b36545a9dbc5e6fe8d35
|
data/.travis.yml
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
sudo: false
|
2
2
|
language: ruby
|
3
|
+
|
3
4
|
rvm:
|
5
|
+
- 2.1.10
|
6
|
+
- 2.2.5
|
4
7
|
- 2.3.1
|
5
|
-
|
8
|
+
- ruby-head
|
9
|
+
|
10
|
+
addons:
|
11
|
+
apt:
|
12
|
+
packages:
|
13
|
+
- python3
|
14
|
+
- python3-dev
|
15
|
+
- python3-all
|
16
|
+
|
17
|
+
before_install:
|
18
|
+
- gem update --system
|
19
|
+
- gem update bundler
|
data/Gemfile
CHANGED
data/Guardfile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
./config/Guardfile
|
data/README.md
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
#
|
1
|
+
# PyCall
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/mrkn/pycall.svg?branch=master)](https://travis-ci.org/mrkn/pycall)
|
2
4
|
|
3
5
|
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/pycall`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
6
|
|
@@ -32,7 +34,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
32
34
|
|
33
35
|
## Contributing
|
34
36
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
37
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/mrkn/pycall.
|
36
38
|
|
37
39
|
|
38
40
|
## License
|
data/bin/guard
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
#
|
4
|
+
# This file was generated by Bundler.
|
5
|
+
#
|
6
|
+
# The application 'guard' is installed as part of a gem, and
|
7
|
+
# this file is here to facilitate running it.
|
8
|
+
#
|
9
|
+
|
10
|
+
require "pathname"
|
11
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
12
|
+
Pathname.new(__FILE__).realpath)
|
13
|
+
|
14
|
+
require "rubygems"
|
15
|
+
require "bundler/setup"
|
16
|
+
|
17
|
+
load Gem.bin_path("guard", "guard")
|
data/bin/rspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
#
|
4
|
+
# This file was generated by Bundler.
|
5
|
+
#
|
6
|
+
# The application 'rspec' is installed as part of a gem, and
|
7
|
+
# this file is here to facilitate running it.
|
8
|
+
#
|
9
|
+
|
10
|
+
require "pathname"
|
11
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
12
|
+
Pathname.new(__FILE__).realpath)
|
13
|
+
|
14
|
+
require "rubygems"
|
15
|
+
require "bundler/setup"
|
16
|
+
|
17
|
+
load Gem.bin_path("rspec-core", "rspec")
|
data/config/Guardfile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# More info at https://github.com/guard/guard#readme
|
2
|
+
|
3
|
+
if RUBY_PLATFORM =~ /darwin/
|
4
|
+
notification :terminal_notifier, app_name: 'pycall.gem ::', activate: 'com.googlecode.iTerm2'
|
5
|
+
end
|
6
|
+
|
7
|
+
directories %w(config lib spec).select { |d|
|
8
|
+
Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")
|
9
|
+
}
|
10
|
+
|
11
|
+
watch('config/Guardfile') do
|
12
|
+
UI.info "Exiting guard because config changed"
|
13
|
+
exit 0
|
14
|
+
end
|
15
|
+
|
16
|
+
guard :rspec, cmd: 'bin/rspec' do
|
17
|
+
require "guard/rspec/dsl"
|
18
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
19
|
+
|
20
|
+
# RSpec files
|
21
|
+
rspec = dsl.rspec
|
22
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
23
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
24
|
+
watch(rspec.spec_files)
|
25
|
+
|
26
|
+
# Ruby files
|
27
|
+
ruby = dsl.ruby
|
28
|
+
dsl.watch_spec_files_for(ruby.lib_files)
|
29
|
+
watch('lib/pycall/libpython.rb') { 'spec' }
|
30
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'pycall/import'
|
2
|
+
include PyCall::Import
|
3
|
+
|
4
|
+
pyimport 'numpy', as: :np
|
5
|
+
pyimport 'matplotlib.pyplot', as: :plt
|
6
|
+
pyimport 'matplotlib.colors', as: :mplc
|
7
|
+
pyfrom 'sklearn.cross_validation', import: :train_test_split
|
8
|
+
pyfrom 'sklearn.preprocessing', import: :StandardScaler
|
9
|
+
pyfrom 'sklearn.datasets', import: %i(make_moons make_circles make_classification)
|
10
|
+
pyfrom 'sklearn.neighbors', import: :KNeighborsClassifier
|
11
|
+
pyfrom 'sklearn.svm', import: :SVC
|
12
|
+
pyfrom 'sklearn.tree', import: :DecisionTreeClassifier
|
13
|
+
pyfrom 'sklearn.ensemble', import: %i(RandomForestClassifier AdaBoostClassifier)
|
14
|
+
pyfrom 'sklearn.naive_bayes', import: :GaussianNB
|
15
|
+
pyfrom 'sklearn.discriminant_analysis', import: %i(LinearDiscriminantAnalysis QuadraticDiscriminantAnalysis)
|
16
|
+
|
17
|
+
h = 0.02 # step size in the mesh
|
18
|
+
|
19
|
+
names = [
|
20
|
+
'Nearest Neighbors',
|
21
|
+
'Linear SVM',
|
22
|
+
'RBF SVM',
|
23
|
+
'Decision Tree',
|
24
|
+
'Random Forest',
|
25
|
+
'AdaBoost',
|
26
|
+
'Naive Bayes',
|
27
|
+
'Linear Discriminant Analysis',
|
28
|
+
'Quadratic Discriminant Analysis'
|
29
|
+
]
|
30
|
+
|
31
|
+
classifiers = [
|
32
|
+
KNeighborsClassifier.(3),
|
33
|
+
SVC.(kernel: 'linear', C: 0.025),
|
34
|
+
SVC.(gamma: 2, C: 1),
|
35
|
+
DecisionTreeClassifier.(max_depth: 5),
|
36
|
+
RandomForestClassifier.(max_depth: 5, n_estimators: 10, max_features: 1),
|
37
|
+
AdaBoostClassifier.(),
|
38
|
+
GaussianNB.(),
|
39
|
+
LinearDiscriminantAnalysis.(),
|
40
|
+
QuadraticDiscriminantAnalysis.()
|
41
|
+
]
|
42
|
+
|
43
|
+
x, y = make_classification.(
|
44
|
+
n_features: 2,
|
45
|
+
n_redundant: 0,
|
46
|
+
n_informative: 2,
|
47
|
+
random_state: 1,
|
48
|
+
n_clusters_per_class: 1
|
49
|
+
)
|
50
|
+
|
51
|
+
np.random.seed.(42)
|
52
|
+
x += 2 * np.random.random_sample.(x.shape)
|
53
|
+
linearly_separable = PyCall.tuple(x, y)
|
54
|
+
|
55
|
+
datasets = [
|
56
|
+
make_moons.(noise: 0.3, random_state: 0),
|
57
|
+
make_circles.(noise: 0.2, factor: 0.5, random_state: 1),
|
58
|
+
linearly_separable
|
59
|
+
]
|
60
|
+
|
61
|
+
fig = plt.figure.(figsize: PyCall.tuple(27, 9))
|
62
|
+
i = 1
|
63
|
+
all = PyCall.slice(nil)
|
64
|
+
datasets.each do |ds|
|
65
|
+
x, y = ds
|
66
|
+
x = StandardScaler.().fit_transform.(x)
|
67
|
+
x_train, x_test, y_train, y_test = train_test_split.(x, y, test_size: 0.4)
|
68
|
+
|
69
|
+
x_min, x_max = np.min.(x[all, 0]) - 0.5, np.max.(x[all, 0]) + 0.5
|
70
|
+
y_min, y_max = np.min.(x[all, 1]) - 0.5, np.max.(x[all, 1]) + 0.5
|
71
|
+
|
72
|
+
xx, yy = np.meshgrid.(
|
73
|
+
np.linspace.(x_min, x_max, ((x_max - x_min)/h).round),
|
74
|
+
np.linspace.(y_min, y_max, ((y_max - y_min)/h).round),
|
75
|
+
)
|
76
|
+
mesh_points = np.dstack.(PyCall.tuple(xx.ravel.(), yy.ravel.()))[0, all, all]
|
77
|
+
|
78
|
+
# just plot the dataset first
|
79
|
+
cm = plt.cm.RdBu
|
80
|
+
cm_bright = mplc.ListedColormap.(["#FF0000", "#0000FF"])
|
81
|
+
ax = plt.subplot.(datasets.length, classifiers.length + 1, i)
|
82
|
+
# plot the training points
|
83
|
+
ax.scatter.(x_train[all, 0], x_train[all, 1], c: y_train, cmap: cm_bright)
|
84
|
+
# and testing points
|
85
|
+
ax.scatter.(x_test[all, 0], x_test[all, 1], c: y_test, cmap: cm_bright, alpha: 0.6)
|
86
|
+
|
87
|
+
ax.set_xlim.(np.min.(xx), np.max.(xx))
|
88
|
+
ax.set_ylim.(np.min.(yy), np.max.(yy))
|
89
|
+
ax.set_xticks.(PyCall.tuple())
|
90
|
+
ax.set_yticks.(PyCall.tuple())
|
91
|
+
i += 1
|
92
|
+
|
93
|
+
# iterate over classifiers
|
94
|
+
names.zip(classifiers).each do |name, clf|
|
95
|
+
ax = plt.subplot.(datasets.length, classifiers.length + 1, i)
|
96
|
+
clf.fit.(x_train, y_train)
|
97
|
+
scor = clf.score.(x_test, y_test)
|
98
|
+
|
99
|
+
# Plot the decision boundary. For that, we will assign a color to each
|
100
|
+
# point in the mesh [x_min, x_max]x[y_min, y_max]
|
101
|
+
begin
|
102
|
+
# not implemented for some
|
103
|
+
z = clf.decision_function.(mesh_points)
|
104
|
+
rescue
|
105
|
+
z = clf.predict_proba.(mesh_points)[all, 1]
|
106
|
+
end
|
107
|
+
|
108
|
+
# Put the result into a color plot
|
109
|
+
z = z.reshape.(xx.shape)
|
110
|
+
ax.contourf.(xx, yy, z, cmap: cm, alpha: 0.8)
|
111
|
+
|
112
|
+
# Plot also the training points
|
113
|
+
ax.scatter.(x_train[all, 0], x_train[all, 1], c: y_train, cmap: cm_bright)
|
114
|
+
# and testing points
|
115
|
+
ax.scatter.(x_test[all, 0], x_test[all, 1], c: y_test, cmap: cm_bright, alpha: 0.6)
|
116
|
+
|
117
|
+
ax.set_xlim.(np.min.(xx), np.max.(xx))
|
118
|
+
ax.set_ylim.(np.min.(yy), np.max.(yy))
|
119
|
+
ax.set_xticks.(PyCall.tuple())
|
120
|
+
ax.set_yticks.(PyCall.tuple())
|
121
|
+
ax.set_title.(name)
|
122
|
+
|
123
|
+
ax.text.(np.max.(xx) - 0.3, np.min.(yy) + 0.3, "%.2f" % scor, size: 15, horizontalalignment: 'right')
|
124
|
+
|
125
|
+
i += 1
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
fig.subplots_adjust.(left: 0.02, right: 0.98)
|
130
|
+
plt.show.()
|
data/examples/hist.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'pycall/import'
|
2
|
+
include PyCall::Import
|
3
|
+
|
4
|
+
pyimport 'numpy', as: 'np'
|
5
|
+
pyimport 'matplotlib.mlab', as: 'mlab'
|
6
|
+
pyimport 'matplotlib.pyplot', as: 'plt'
|
7
|
+
|
8
|
+
np.random.seed.(0)
|
9
|
+
|
10
|
+
mu = 100
|
11
|
+
sigma = 15
|
12
|
+
x = mu + sigma * np.random.randn.(437)
|
13
|
+
|
14
|
+
num_bins = 50
|
15
|
+
|
16
|
+
fig, ax = plt.subplots.()
|
17
|
+
|
18
|
+
n, bins, patches = ax.hist.(x, num_bins, normed: 1)
|
19
|
+
|
20
|
+
y = mlab.normpdf.(bins, mu, sigma)
|
21
|
+
ax.plot.(bins, y, '--')
|
22
|
+
ax.set_xlabel.('Smarts')
|
23
|
+
ax.set_ylabel.('Probability density')
|
24
|
+
ax.set_title.('Histogram of IQ: $\mu=100$, $\sigma=15$')
|
25
|
+
|
26
|
+
fig.tight_layout.()
|
27
|
+
plt.show.()
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'pycall/import'
|
2
|
+
|
3
|
+
include PyCall::Import
|
4
|
+
|
5
|
+
pyimport 'numpy', as: 'np'
|
6
|
+
pyimport 'matplotlib.pyplot', as: 'plt'
|
7
|
+
|
8
|
+
pyfrom 'sklearn.datasets', import: 'fetch_olivetti_faces'
|
9
|
+
pyfrom 'sklearn.ensemble', import: 'ExtraTreesClassifier'
|
10
|
+
|
11
|
+
# Number of cores to use to perform parallel fitting of the forest model
|
12
|
+
n_jobs = 1
|
13
|
+
|
14
|
+
# Load the faces datasets
|
15
|
+
data = fetch_olivetti_faces.()
|
16
|
+
x = data.images.reshape.(PyCall.tuple(PyCall.len(data.images), -1))
|
17
|
+
y = data.target
|
18
|
+
|
19
|
+
mask = y < 5 # Limit to 5 classes
|
20
|
+
x = x[mask]
|
21
|
+
y = y[mask]
|
22
|
+
|
23
|
+
# Build a forest and compute the pixel importances
|
24
|
+
puts "Fitting ExtraTreesClassifier on faces data with #{n_jobs} cores..."
|
25
|
+
t0 = Time.now
|
26
|
+
forest = ExtraTreesClassifier.(
|
27
|
+
n_estimators: 1_000,
|
28
|
+
max_features: 128,
|
29
|
+
n_jobs: n_jobs,
|
30
|
+
random_state: 0
|
31
|
+
)
|
32
|
+
|
33
|
+
forest = forest.fit.(x, y)
|
34
|
+
puts "done in %0.3fs" % (Time.now - t0)
|
35
|
+
importances = forest.feature_importances_
|
36
|
+
importances = importances.reshape.(data.images[0].shape)
|
37
|
+
|
38
|
+
# Plot pixel importances
|
39
|
+
plt.matshow.(importances, cmap: plt.cm.hot)
|
40
|
+
plt.title.("Pixel importances with forests of trees")
|
41
|
+
plt.show.()
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'pycall/import'
|
2
|
+
include PyCall::Import
|
3
|
+
|
4
|
+
require 'benchmark'
|
5
|
+
pyimport :pandas, as: :pd
|
6
|
+
pyimport :seaborn, as: :sns
|
7
|
+
pyimport 'matplotlib.pyplot', as: :plt
|
8
|
+
|
9
|
+
array = Array.new(100_000) { rand }
|
10
|
+
|
11
|
+
trials = 100
|
12
|
+
results = { method: [], runtime: [] }
|
13
|
+
|
14
|
+
# Array#sum
|
15
|
+
trials.times do
|
16
|
+
results[:method] << 'sum'
|
17
|
+
results[:runtime] << Benchmark.realtime { array.sum }
|
18
|
+
end
|
19
|
+
|
20
|
+
# Array#inject(:+)
|
21
|
+
trials.times do
|
22
|
+
results[:method] << 'inject'
|
23
|
+
results[:runtime] << Benchmark.realtime { array.inject(:+) }
|
24
|
+
end
|
25
|
+
|
26
|
+
# while
|
27
|
+
def while_sum(ary)
|
28
|
+
sum, i, n = 0, 0, ary.length
|
29
|
+
while i < n
|
30
|
+
sum += ary[i]
|
31
|
+
i += 1
|
32
|
+
end
|
33
|
+
sum
|
34
|
+
end
|
35
|
+
|
36
|
+
trials.times do
|
37
|
+
results[:method] << 'while'
|
38
|
+
results[:runtime] << Benchmark.realtime { while_sum(array) }
|
39
|
+
end
|
40
|
+
|
41
|
+
# visualization
|
42
|
+
|
43
|
+
df = pd.DataFrame.(PyCall::Dict.new(results))
|
44
|
+
sns.barplot.(x: 'method', y: 'runtime', data: df)
|
45
|
+
plt.title.("Array summation benchmark (#{trials} trials)")
|
46
|
+
plt.xlabel.('Summation method')
|
47
|
+
plt.ylabel.('Average runtime [sec]')
|
48
|
+
plt.show.()
|
data/lib/pycall.rb
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
require "pycall/version"
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
2
|
+
require "pycall/libpython"
|
3
|
+
require "pycall/pyobject"
|
4
|
+
require "pycall/pyerror"
|
5
|
+
require "pycall/eval"
|
6
|
+
require "pycall/types"
|
7
|
+
require "pycall/conversion"
|
8
|
+
require "pycall/pyobject_wrapper"
|
9
|
+
require "pycall/tuple"
|
10
|
+
require "pycall/list"
|
11
|
+
require "pycall/dict"
|
12
|
+
require "pycall/set"
|
13
|
+
require "pycall/slice"
|
14
|
+
require "pycall/utils"
|
15
|
+
require "pycall/init"
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module PyCall
|
2
|
+
module Conversions
|
3
|
+
def self.from_ruby(obj)
|
4
|
+
case obj
|
5
|
+
when PyObject
|
6
|
+
obj
|
7
|
+
when PyObjectWrapper
|
8
|
+
obj.__pyobj__
|
9
|
+
when TrueClass, FalseClass
|
10
|
+
LibPython.PyBool_FromLong(obj ? 1 : 0)
|
11
|
+
when Integer
|
12
|
+
LibPython.PyInt_FromSsize_t(obj)
|
13
|
+
when Float
|
14
|
+
LibPython.PyFloat_FromDouble(obj)
|
15
|
+
when String
|
16
|
+
case obj.encoding
|
17
|
+
when Encoding::US_ASCII, Encoding::BINARY
|
18
|
+
LibPython.PyString_FromStringAndSize(obj, obj.bytesize)
|
19
|
+
else
|
20
|
+
obj = obj.encode(Encoding::UTF_8)
|
21
|
+
LibPython.PyUnicode_DecodeUTF8(obj, obj.bytesize, nil)
|
22
|
+
end
|
23
|
+
when Symbol
|
24
|
+
from_ruby(obj.to_s)
|
25
|
+
when Array
|
26
|
+
PyCall::List.new(obj).__pyobj__
|
27
|
+
else
|
28
|
+
LibPython.Py_None
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.convert_to_boolean(py_obj)
|
33
|
+
0 != LibPython.PyInt_AsSsize_t(py_obj)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.convert_to_integer(py_obj)
|
37
|
+
LibPython.PyInt_AsSsize_t(py_obj)
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.convert_to_float(py_obj)
|
41
|
+
LibPython.PyFloat_AsDouble(py_obj)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.convert_to_complex(py_obj)
|
45
|
+
real = LibPython.PyComplex_RealAsDouble(py_obj)
|
46
|
+
imag = LibPython.PyComplex_ImagAsDouble(py_obj)
|
47
|
+
Complex(real, imag)
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.convert_to_string(py_obj)
|
51
|
+
FFI::MemoryPointer.new(:string) do |str_ptr|
|
52
|
+
FFI::MemoryPointer.new(:int) do |len_ptr|
|
53
|
+
res = LibPython.PyString_AsStringAndSize(py_obj, str_ptr, len_ptr)
|
54
|
+
return nil if res == -1 # FIXME: error
|
55
|
+
|
56
|
+
len = len_ptr.get(:int, 0)
|
57
|
+
return str_ptr.get_pointer(0).read_string(len)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.convert_to_array(py_obj, force_list: true, array_class: Array)
|
63
|
+
case
|
64
|
+
when force_list || py_obj.kind_of?(LibPython.PyList_Type)
|
65
|
+
len = LibPython.PySequence_Size(py_obj)
|
66
|
+
array_class.new(len) do |i|
|
67
|
+
LibPython.PySequence_GetItem(py_obj, i).to_ruby
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.convert_to_tuple(py_obj)
|
73
|
+
PyCall::Tuple.new(py_obj)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class PyObject
|
78
|
+
def to_ruby
|
79
|
+
return nil if self.null? || self.py_none?
|
80
|
+
|
81
|
+
case self
|
82
|
+
when LibPython.PyBool_Type
|
83
|
+
return Conversions.convert_to_boolean(self)
|
84
|
+
|
85
|
+
when LibPython.PyInt_Type
|
86
|
+
return Conversions.convert_to_integer(self)
|
87
|
+
|
88
|
+
when LibPython.PyLong_Type
|
89
|
+
# TODO: should make Bignum
|
90
|
+
|
91
|
+
when LibPython.PyFloat_Type
|
92
|
+
return Conversions.convert_to_float(self)
|
93
|
+
|
94
|
+
when LibPython.PyComplex_Type
|
95
|
+
return Conversions.convert_to_complex(self)
|
96
|
+
|
97
|
+
when LibPython.PyString_Type
|
98
|
+
return Conversions.convert_to_string(self)
|
99
|
+
|
100
|
+
when LibPython.PyUnicode_Type
|
101
|
+
py_str_ptr = LibPython.PyUnicode_AsUTF8String(self)
|
102
|
+
return Conversions.convert_to_string(py_str_ptr).force_encoding(Encoding::UTF_8)
|
103
|
+
|
104
|
+
when LibPython.PyList_Type
|
105
|
+
return Conversions.convert_to_array(self)
|
106
|
+
|
107
|
+
when LibPython.PyTuple_Type
|
108
|
+
return Conversions.convert_to_tuple(self)
|
109
|
+
|
110
|
+
when LibPython.PyDict_Type
|
111
|
+
return PyCall::Dict.new(self)
|
112
|
+
|
113
|
+
when LibPython.PySet_Type
|
114
|
+
return PyCall::Set.new(self)
|
115
|
+
end
|
116
|
+
|
117
|
+
self
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|