pycall 1.3.1 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +133 -50
- data/.github/workflows/windows.yml +127 -0
- data/.gitignore +2 -0
- data/CHANGES.md +8 -0
- data/README.md +112 -6
- data/Rakefile +81 -19
- data/examples/notebooks/leaflet.ipynb +77 -0
- data/ext/pycall/pycall_internal.h +1 -1
- data/ext/pycall/ruby_wrapper.c +11 -2
- data/lib/pycall.rb +11 -0
- data/lib/pycall/init.rb +3 -8
- data/lib/pycall/libpython/finder.rb +126 -78
- data/lib/pycall/python/investigator.py +83 -6
- data/lib/pycall/version.rb +1 -1
- data/lib/pycall/wrapper_object_cache.rb +51 -25
- data/pycall.gemspec +1 -0
- metadata +19 -5
- data/.travis.yml +0 -82
- data/appveyor.yml +0 -92
data/Rakefile
CHANGED
@@ -1,31 +1,93 @@
|
|
1
|
-
require "bundler"
|
2
|
-
|
1
|
+
require "bundler/gem_helper"
|
2
|
+
require "rake/clean"
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
base_dir = File.join(File.dirname(__FILE__))
|
5
|
+
|
6
|
+
helper = Bundler::GemHelper.new(base_dir)
|
7
|
+
helper.install
|
8
|
+
spec = helper.gemspec
|
9
|
+
|
10
|
+
def run_extconf(build_dir, extension_dir, *arguments)
|
11
|
+
cd(build_dir) do
|
12
|
+
ruby(File.join(extension_dir, "extconf.rb"), *arguments)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def make_command
|
17
|
+
if RUBY_PLATFORM =~ /mswin/
|
18
|
+
"nmake"
|
19
|
+
else
|
20
|
+
ENV["MAKE"] || find_make
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def find_make
|
25
|
+
candidates = ["gmake", "make"]
|
26
|
+
paths = ENV.fetch("PATH", "").split(File::PATH_SEPARATOR)
|
27
|
+
exeext = RbConfig::CONFIG["EXEEXT"]
|
28
|
+
candidates.each do |candidate|
|
29
|
+
paths.each do |path|
|
30
|
+
cmd = File.join(path, "#{candidate}#{exeext}")
|
31
|
+
return cmd if File.executable?(cmd)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
7
35
|
|
8
36
|
Dir[File.expand_path('../tasks/**/*.rake', __FILE__)].each {|f| load f }
|
9
37
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
38
|
+
spec.extensions.each do |extension|
|
39
|
+
extension_dir = File.join(base_dir, File.dirname(extension))
|
40
|
+
build_dir = ENV["BUILD_DIR"]
|
41
|
+
if build_dir
|
42
|
+
build_dir = File.join(build_dir, "pycall")
|
43
|
+
directory build_dir
|
44
|
+
else
|
45
|
+
build_dir = extension_dir
|
46
|
+
end
|
47
|
+
|
48
|
+
makefile = File.join(build_dir, "Makefile")
|
49
|
+
file makefile => build_dir do
|
50
|
+
run_extconf(build_dir, extension_dir)
|
51
|
+
end
|
52
|
+
|
53
|
+
CLOBBER << makefile
|
54
|
+
CLOBBER << File.join(build_dir, "mkmf.log")
|
55
|
+
|
56
|
+
desc "Configure"
|
57
|
+
task configure: makefile
|
58
|
+
|
59
|
+
desc "Compile"
|
60
|
+
task compile: makefile do
|
61
|
+
cd(build_dir) do
|
62
|
+
sh(make_command)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
task :clean do
|
67
|
+
cd(build_dir) do
|
68
|
+
sh(make_command, "clean") if File.exist?("Makefile")
|
69
|
+
end
|
17
70
|
end
|
18
71
|
end
|
19
72
|
|
20
|
-
|
73
|
+
require "rake/extensiontask"
|
74
|
+
Rake::ExtensionTask.new("pycall/spec_helper")
|
21
75
|
|
22
|
-
desc "
|
23
|
-
task
|
24
|
-
|
25
|
-
|
76
|
+
desc "Run tests"
|
77
|
+
task :test do
|
78
|
+
cd(base_dir) do
|
79
|
+
ruby("test/run-test.rb")
|
80
|
+
end
|
26
81
|
end
|
27
82
|
|
28
|
-
|
83
|
+
task default: :test
|
84
|
+
|
85
|
+
require "rspec/core/rake_task"
|
86
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
87
|
+
ext_dir = File.join(base_dir, "ext/pycall")
|
88
|
+
t.ruby_opts = "-I#{ext_dir}"
|
89
|
+
t.verbose = true
|
90
|
+
end
|
29
91
|
|
30
|
-
task :
|
92
|
+
task default: :spec
|
31
93
|
task spec: :compile
|
@@ -0,0 +1,77 @@
|
|
1
|
+
{
|
2
|
+
"cells": [
|
3
|
+
{
|
4
|
+
"cell_type": "code",
|
5
|
+
"execution_count": 1,
|
6
|
+
"id": "burning-montreal",
|
7
|
+
"metadata": {},
|
8
|
+
"outputs": [
|
9
|
+
{
|
10
|
+
"data": {
|
11
|
+
"text/plain": [
|
12
|
+
"<module 'folium' from '/opt/brew/Cellar/python@3.9/3.9.2/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/folium/__init__.py'>"
|
13
|
+
]
|
14
|
+
},
|
15
|
+
"execution_count": 1,
|
16
|
+
"metadata": {},
|
17
|
+
"output_type": "execute_result"
|
18
|
+
}
|
19
|
+
],
|
20
|
+
"source": [
|
21
|
+
"require \"pycall\"\n",
|
22
|
+
"folium = PyCall.import_module(\"folium\")"
|
23
|
+
]
|
24
|
+
},
|
25
|
+
{
|
26
|
+
"cell_type": "code",
|
27
|
+
"execution_count": 7,
|
28
|
+
"id": "resistant-agriculture",
|
29
|
+
"metadata": {
|
30
|
+
"scrolled": false
|
31
|
+
},
|
32
|
+
"outputs": [
|
33
|
+
{
|
34
|
+
"data": {
|
35
|
+
"text/html": [
|
36
|
+
"<div style=\"width:100%;\"><div style=\"position:relative;width:100%;height:0;padding-bottom:60%;\"><span style=\"color:#565656\">Make this Notebook Trusted to load map: File -> Trust Notebook</span><iframe src=\"about:blank\" style=\"position:absolute;width:100%;height:100%;left:0;top:0;border:none !important;\" data-html=%3C%21DOCTYPE%20html%3E%0A%3Chead%3E%20%20%20%20%0A%20%20%20%20%3Cmeta%20http-equiv%3D%22content-type%22%20content%3D%22text/html%3B%20charset%3DUTF-8%22%20/%3E%0A%20%20%20%20%0A%20%20%20%20%20%20%20%20%3Cscript%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20L_NO_TOUCH%20%3D%20false%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20L_DISABLE_3D%20%3D%20false%3B%0A%20%20%20%20%20%20%20%20%3C/script%3E%0A%20%20%20%20%0A%20%20%20%20%3Cstyle%3Ehtml%2C%20body%20%7Bwidth%3A%20100%25%3Bheight%3A%20100%25%3Bmargin%3A%200%3Bpadding%3A%200%3B%7D%3C/style%3E%0A%20%20%20%20%3Cstyle%3E%23map%20%7Bposition%3Aabsolute%3Btop%3A0%3Bbottom%3A0%3Bright%3A0%3Bleft%3A0%3B%7D%3C/style%3E%0A%20%20%20%20%3Cscript%20src%3D%22https%3A//cdn.jsdelivr.net/npm/leaflet%401.6.0/dist/leaflet.js%22%3E%3C/script%3E%0A%20%20%20%20%3Cscript%20src%3D%22https%3A//code.jquery.com/jquery-1.12.4.min.js%22%3E%3C/script%3E%0A%20%20%20%20%3Cscript%20src%3D%22https%3A//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js%22%3E%3C/script%3E%0A%20%20%20%20%3Cscript%20src%3D%22https%3A//cdnjs.cloudflare.com/ajax/libs/Leaflet.awesome-markers/2.0.2/leaflet.awesome-markers.js%22%3E%3C/script%3E%0A%20%20%20%20%3Clink%20rel%3D%22stylesheet%22%20href%3D%22https%3A//cdn.jsdelivr.net/npm/leaflet%401.6.0/dist/leaflet.css%22/%3E%0A%20%20%20%20%3Clink%20rel%3D%22stylesheet%22%20href%3D%22https%3A//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css%22/%3E%0A%20%20%20%20%3Clink%20rel%3D%22stylesheet%22%20href%3D%22https%3A//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css%22/%3E%0A%20%20%20%20%3Clink%20rel%3D%22stylesheet%22%20href%3D%22https%3A//maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css%22/%3E%0A%20%20%20%20%3Clink%20rel%3D%22stylesheet%22%20href%3D%22https%3A//cdnjs.cloudflare.com/ajax/libs/Leaflet.awesome-markers/2.0.2/leaflet.awesome-markers.css%22/%3E%0A%20%20%20%20%3Clink%20rel%3D%22stylesheet%22%20href%3D%22https%3A//cdn.jsdelivr.net/gh/python-visualization/folium/folium/templates/leaflet.awesome.rotate.min.css%22/%3E%0A%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Cmeta%20name%3D%22viewport%22%20content%3D%22width%3Ddevice-width%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20initial-scale%3D1.0%2C%20maximum-scale%3D1.0%2C%20user-scalable%3Dno%22%20/%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Cstyle%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23map_5fca0053c20247f19cc1284357d59a29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20position%3A%20relative%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20width%3A%20100.0%25%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20height%3A%20100.0%25%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20left%3A%200.0%25%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20top%3A%200.0%25%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%3C/style%3E%0A%20%20%20%20%20%20%20%20%0A%3C/head%3E%0A%3Cbody%3E%20%20%20%20%0A%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%20class%3D%22folium-map%22%20id%3D%22map_5fca0053c20247f19cc1284357d59a29%22%20%3E%3C/div%3E%0A%20%20%20%20%20%20%20%20%0A%3C/body%3E%0A%3Cscript%3E%20%20%20%20%0A%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20map_5fca0053c20247f19cc1284357d59a29%20%3D%20L.map%28%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22map_5fca0053c20247f19cc1284357d59a29%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20center%3A%20%5B35.68053684909772%2C%20139.75749875116222%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20crs%3A%20L.CRS.EPSG3857%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20zoom%3A%2015%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20zoomControl%3A%20true%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20preferCanvas%3A%20false%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%29%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20tile_layer_a0bac7f0648a4b10b642992e43f1cdab%20%3D%20L.tileLayer%28%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22https%3A//%7Bs%7D.tile.openstreetmap.org/%7Bz%7D/%7Bx%7D/%7By%7D.png%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%22attribution%22%3A%20%22Data%20by%20%5Cu0026copy%3B%20%5Cu003ca%20href%3D%5C%22http%3A//openstreetmap.org%5C%22%5Cu003eOpenStreetMap%5Cu003c/a%5Cu003e%2C%20under%20%5Cu003ca%20href%3D%5C%22http%3A//www.openstreetmap.org/copyright%5C%22%5Cu003eODbL%5Cu003c/a%5Cu003e.%22%2C%20%22detectRetina%22%3A%20false%2C%20%22maxNativeZoom%22%3A%2018%2C%20%22maxZoom%22%3A%2018%2C%20%22minZoom%22%3A%200%2C%20%22noWrap%22%3A%20false%2C%20%22opacity%22%3A%201%2C%20%22subdomains%22%3A%20%22abc%22%2C%20%22tms%22%3A%20false%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%29.addTo%28map_5fca0053c20247f19cc1284357d59a29%29%3B%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20marker_b70f6fe03da84c00b40a5540bf220d8d%20%3D%20L.marker%28%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5B35.67400045350403%2C%20139.75593234124372%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%29.addTo%28map_5fca0053c20247f19cc1284357d59a29%29%3B%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%0A%20%20%20%20%20%20%20%20var%20popup_b529c15361c6471ca5b708097315db55%20%3D%20L.popup%28%7B%22maxWidth%22%3A%20%22100%25%22%7D%29%3B%0A%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20html_ceaac4d79b8942afbefdcd504a795de8%20%3D%20%24%28%60%3Cdiv%20id%3D%22html_ceaac4d79b8942afbefdcd504a795de8%22%20style%3D%22width%3A%20100.0%25%3B%20height%3A%20100.0%25%3B%22%3E%E6%97%A5%E6%AF%94%E8%B0%B7%E5%85%AC%E5%9C%92%3C/div%3E%60%29%5B0%5D%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20popup_b529c15361c6471ca5b708097315db55.setContent%28html_ceaac4d79b8942afbefdcd504a795de8%29%3B%0A%20%20%20%20%20%20%20%20%0A%0A%20%20%20%20%20%20%20%20marker_b70f6fe03da84c00b40a5540bf220d8d.bindPopup%28popup_b529c15361c6471ca5b708097315db55%29%0A%20%20%20%20%20%20%20%20%3B%0A%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%0A%3C/script%3E onload=\"this.contentDocument.open();this.contentDocument.write( decodeURIComponent(this.getAttribute('data-html')));this.contentDocument.close();\" allowfullscreen webkitallowfullscreen mozallowfullscreen></iframe></div></div>"
|
37
|
+
],
|
38
|
+
"text/plain": [
|
39
|
+
"<folium.folium.Map object at 0x165c3bc10>"
|
40
|
+
]
|
41
|
+
},
|
42
|
+
"execution_count": 7,
|
43
|
+
"metadata": {},
|
44
|
+
"output_type": "execute_result"
|
45
|
+
}
|
46
|
+
],
|
47
|
+
"source": [
|
48
|
+
"map = folium.Map.new(location: [35.68053684909772, 139.75749875116222], zoom_start: 15)\n",
|
49
|
+
"folium.Marker.new([35.67400045350403, 139.75593234124372], popup: \"日比谷公園\").add_to(map)\n",
|
50
|
+
"map"
|
51
|
+
]
|
52
|
+
},
|
53
|
+
{
|
54
|
+
"cell_type": "code",
|
55
|
+
"execution_count": null,
|
56
|
+
"id": "angry-giant",
|
57
|
+
"metadata": {},
|
58
|
+
"outputs": [],
|
59
|
+
"source": []
|
60
|
+
}
|
61
|
+
],
|
62
|
+
"metadata": {
|
63
|
+
"kernelspec": {
|
64
|
+
"display_name": "Ruby 3.0.0",
|
65
|
+
"language": "ruby",
|
66
|
+
"name": "ruby"
|
67
|
+
},
|
68
|
+
"language_info": {
|
69
|
+
"file_extension": ".rb",
|
70
|
+
"mimetype": "application/x-ruby",
|
71
|
+
"name": "ruby",
|
72
|
+
"version": "3.0.0"
|
73
|
+
}
|
74
|
+
},
|
75
|
+
"nbformat": 4,
|
76
|
+
"nbformat_minor": 5
|
77
|
+
}
|
@@ -663,7 +663,7 @@ Py_ssize_t pycall_python_hexversion(void);
|
|
663
663
|
|
664
664
|
void pycall_Py_DecRef(PyObject *);
|
665
665
|
|
666
|
-
|
666
|
+
extern const rb_data_type_t pycall_pyptr_data_type;
|
667
667
|
size_t pycall_pyptr_memsize(void const *);
|
668
668
|
void pycall_pyptr_free(void *);
|
669
669
|
|
data/ext/pycall/ruby_wrapper.c
CHANGED
@@ -356,11 +356,16 @@ PyRuby_getattro_with_gvl(PyRubyObject *pyro, PyObject *pyobj_name)
|
|
356
356
|
|
357
357
|
VALUE cPyRubyPtr;
|
358
358
|
|
359
|
-
|
359
|
+
static rb_data_type_t pycall_pyrubyptr_data_type = {
|
360
360
|
"PyCall::PyRubyPtr",
|
361
361
|
{ 0, pycall_pyptr_free, pycall_pyptr_memsize, },
|
362
362
|
#ifdef RUBY_TYPED_FREE_IMMEDIATELY
|
363
|
-
|
363
|
+
# if defined _WIN32 && !defined __CYGWIN__
|
364
|
+
0,
|
365
|
+
# else
|
366
|
+
&pycall_pyptr_data_type,
|
367
|
+
# endif
|
368
|
+
0, RUBY_TYPED_FREE_IMMEDIATELY
|
364
369
|
#endif
|
365
370
|
};
|
366
371
|
|
@@ -462,6 +467,10 @@ pycall_init_ruby_wrapper(void)
|
|
462
467
|
|
463
468
|
/* PyCall::PyRubyPtr */
|
464
469
|
|
470
|
+
#if defined _WIN32 && !defined __CYGWIN__
|
471
|
+
pycall_pyrubyptr_data_type.parent = &pycall_pyptr_data_type;
|
472
|
+
#endif
|
473
|
+
|
465
474
|
cPyRubyPtr = rb_define_class_under(mPyCall, "PyRubyPtr", cPyPtr);
|
466
475
|
rb_define_alloc_func(cPyRubyPtr, pycall_pyruby_allocate);
|
467
476
|
rb_define_method(cPyRubyPtr, "__ruby_object_id__", pycall_pyruby_get_ruby_object_id, 0);
|
data/lib/pycall.rb
CHANGED
@@ -58,6 +58,17 @@ module PyCall
|
|
58
58
|
LibPython::Helpers.hasattr?(obj.__pyptr__, name)
|
59
59
|
end
|
60
60
|
|
61
|
+
def same?(left, right)
|
62
|
+
case left
|
63
|
+
when PyObjectWrapper
|
64
|
+
case right
|
65
|
+
when PyObjectWrapper
|
66
|
+
return left.__pyptr__ == right.__pyptr__
|
67
|
+
end
|
68
|
+
end
|
69
|
+
false
|
70
|
+
end
|
71
|
+
|
61
72
|
def import_module(name)
|
62
73
|
LibPython::Helpers.import_module(name)
|
63
74
|
end
|
data/lib/pycall/init.rb
CHANGED
@@ -30,20 +30,15 @@ module PyCall
|
|
30
30
|
remove_method :const_missing
|
31
31
|
end
|
32
32
|
|
33
|
-
ENV['PYTHONPATH'] = [ File.expand_path('../python', __FILE__), ENV['PYTHONPATH'] ].compact.join(File::PATH_SEPARATOR)
|
34
|
-
|
35
33
|
LibPython.instance_variable_set(:@handle, LibPython::Finder.find_libpython(python))
|
36
34
|
class << LibPython
|
37
35
|
undef_method :handle
|
38
36
|
attr_reader :handle
|
39
37
|
end
|
40
38
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
rescue LoadError
|
45
|
-
require 'pycall.so'
|
46
|
-
end
|
39
|
+
require 'pycall.so'
|
40
|
+
|
41
|
+
PyCall.sys.path.append(File.expand_path('../python', __FILE__))
|
47
42
|
|
48
43
|
require 'pycall/dict'
|
49
44
|
require 'pycall/list'
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'pycall/error'
|
2
2
|
require 'fiddle'
|
3
|
+
require 'pathname'
|
3
4
|
|
4
5
|
module PyCall
|
5
6
|
module LibPython
|
@@ -39,60 +40,102 @@ module PyCall
|
|
39
40
|
def find_libpython(python = nil)
|
40
41
|
debug_report("find_libpython(#{python.inspect})")
|
41
42
|
python, python_config = find_python_config(python)
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
suffix = python_config[:SHLIB_SUFFIX]
|
44
|
+
|
45
|
+
use_conda = (ENV.fetch("CONDA_PREFIX", nil) == File.dirname(python_config[:executable]))
|
46
|
+
python_home = if !ENV.key?("PYTHONHOME") || use_conda
|
47
|
+
python_config[:PYTHONHOME]
|
48
|
+
else
|
49
|
+
ENV["PYTHONHOME"]
|
50
|
+
end
|
51
|
+
ENV["PYTHONHOME"] = python_home
|
52
|
+
|
53
|
+
candidate_paths(python_config) do |path|
|
54
|
+
debug_report("Candidate: #{path}")
|
55
|
+
normalized = normalize_path(path, suffix)
|
56
|
+
if normalized
|
57
|
+
debug_report("Trying to dlopen: #{normalized}")
|
46
58
|
begin
|
47
|
-
return dlopen(
|
59
|
+
return dlopen(normalized)
|
48
60
|
rescue Fiddle::DLError
|
49
|
-
debug_report "#{$!.class}: #{$!.message}"
|
50
|
-
else
|
51
|
-
debug_report "Success to dlopen #{libpython.inspect} from ENV['LIBPYTHON']"
|
61
|
+
debug_report "dlopen(#{normalized.inspect}) => #{$!.class}: #{$!.message}"
|
52
62
|
end
|
63
|
+
else
|
64
|
+
debug_report("Not found.")
|
53
65
|
end
|
54
|
-
warn "WARNING(#{self}.#{__method__}) Ignore the wrong libpython location specified in ENV['LIBPYTHON']."
|
55
66
|
end
|
67
|
+
end
|
56
68
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
69
|
+
def candidate_names(python_config)
|
70
|
+
names = []
|
71
|
+
names << python_config[:LDLIBRARY] if python_config[:LDLIBRARY]
|
72
|
+
suffix = python_config[:SHLIB_SUFFIX]
|
73
|
+
if python_config[:LIBRARY]
|
74
|
+
ext = File.extname(python_config[:LIBRARY])
|
75
|
+
names << python_config[:LIBRARY].delete_suffix(ext) + suffix
|
76
|
+
end
|
77
|
+
dlprefix = if windows? then "" else "lib" end
|
78
|
+
sysdata = {
|
79
|
+
v_major: python_config[:version_major],
|
80
|
+
VERSION: python_config[:VERSION],
|
81
|
+
ABIFLAGS: python_config[:ABIFLAGS],
|
82
|
+
}
|
83
|
+
[
|
84
|
+
"python%{VERSION}%{ABIFLAGS}" % sysdata,
|
85
|
+
"python%{VERSION}" % sysdata,
|
86
|
+
"python%{v_major}" % sysdata,
|
87
|
+
"python"
|
88
|
+
].each do |stem|
|
89
|
+
names << "#{dlprefix}#{stem}#{suffix}"
|
82
90
|
end
|
83
91
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
+
names.compact!
|
93
|
+
names.uniq!
|
94
|
+
|
95
|
+
debug_report("candidate_names: #{names}")
|
96
|
+
return names
|
97
|
+
end
|
98
|
+
|
99
|
+
def candidate_paths(python_config)
|
100
|
+
# The candidate library that linked by executable
|
101
|
+
yield python_config[:linked_libpython]
|
102
|
+
|
103
|
+
lib_dirs = make_libpaths(python_config)
|
104
|
+
lib_basenames = candidate_names(python_config)
|
105
|
+
|
106
|
+
# candidates by absolute paths
|
107
|
+
lib_dirs.each do |dir|
|
108
|
+
lib_basenames.each do |name|
|
109
|
+
yield File.join(dir, name)
|
92
110
|
end
|
93
111
|
end
|
94
112
|
|
95
|
-
|
113
|
+
# library names for searching in system library paths
|
114
|
+
lib_basenames.each do |name|
|
115
|
+
yield name
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def normalize_path(path, suffix, apple_p=apple?)
|
120
|
+
return nil if path.nil?
|
121
|
+
case
|
122
|
+
when path.nil?,
|
123
|
+
Pathname.new(path).relative?
|
124
|
+
nil
|
125
|
+
when File.exist?(path)
|
126
|
+
File.realpath(path)
|
127
|
+
when File.exist?(path + suffix)
|
128
|
+
File.realpath(path + suffix)
|
129
|
+
when apple_p
|
130
|
+
normalize_path(remove_suffix_apple(path), ".so", false)
|
131
|
+
else
|
132
|
+
nil
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Strip off .so or .dylib
|
137
|
+
def remove_suffix_apple(path)
|
138
|
+
path.sub(/\.(?:dylib|so)\z/, '')
|
96
139
|
end
|
97
140
|
|
98
141
|
def investigate_python_config(python)
|
@@ -119,47 +162,25 @@ module PyCall
|
|
119
162
|
File.expand_path('../../python/investigator.py', __FILE__)
|
120
163
|
end
|
121
164
|
|
122
|
-
def
|
123
|
-
|
124
|
-
case RUBY_PLATFORM
|
125
|
-
when /mingw32/, /cygwin/, /mswin/
|
126
|
-
ENV['PYTHONHOME'] = python_config[:exec_prefix]
|
127
|
-
else
|
128
|
-
ENV['PYTHONHOME'] = python_config.values_at(:prefix, :exec_prefix).join(':')
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
165
|
+
def make_libpaths(python_config)
|
166
|
+
libpaths = python_config.values_at(:LIBPL, :srcdir, :LIBDIR)
|
132
167
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
lib
|
137
|
-
libs << lib << File.basename(lib) if lib
|
138
|
-
end
|
139
|
-
if (lib = python_config[:LIBRARY])
|
140
|
-
libs << File.basename(lib, File.extname(lib))
|
168
|
+
if windows?
|
169
|
+
libpaths << File.dirname(python_config[:executable])
|
170
|
+
else
|
171
|
+
libpaths << File.expand_path('../../lib', python_config[:executable])
|
141
172
|
end
|
142
173
|
|
143
|
-
|
144
|
-
|
145
|
-
libs.uniq!
|
146
|
-
|
147
|
-
debug_report "libs: #{libs.inspect}"
|
148
|
-
return libs
|
149
|
-
end
|
150
|
-
|
151
|
-
def make_libpaths(python_config)
|
152
|
-
executable = python_config[:executable]
|
153
|
-
libpaths = [ python_config[:LIBDIR] ]
|
154
|
-
if Fiddle::WINDOWS
|
155
|
-
libpaths << File.dirname(executable)
|
156
|
-
else
|
157
|
-
libpaths << File.expand_path('../../lib', executable)
|
174
|
+
if apple?
|
175
|
+
libpaths << python_config[:PYTHONFRAMEWORKPREFIX]
|
158
176
|
end
|
159
|
-
|
177
|
+
|
160
178
|
exec_prefix = python_config[:exec_prefix]
|
161
|
-
libpaths << exec_prefix
|
179
|
+
libpaths << exec_prefix
|
180
|
+
libpaths << File.join(exec_prefix, 'lib')
|
181
|
+
|
162
182
|
libpaths.compact!
|
183
|
+
libpaths.uniq!
|
163
184
|
|
164
185
|
debug_report "libpaths: #{libpaths.inspect}"
|
165
186
|
return libpaths
|
@@ -167,6 +188,14 @@ module PyCall
|
|
167
188
|
|
168
189
|
private
|
169
190
|
|
191
|
+
def windows?
|
192
|
+
Fiddle::WINDOWS
|
193
|
+
end
|
194
|
+
|
195
|
+
def apple?
|
196
|
+
RUBY_PLATFORM.include?("darwin")
|
197
|
+
end
|
198
|
+
|
170
199
|
def dlopen(libname)
|
171
200
|
Fiddle.dlopen(libname).tap do |handle|
|
172
201
|
debug_report("dlopen(#{libname.inspect}) = #{handle.inspect}") if handle
|
@@ -185,3 +214,22 @@ module PyCall
|
|
185
214
|
end
|
186
215
|
end
|
187
216
|
end
|
217
|
+
|
218
|
+
if __FILE__ == $0
|
219
|
+
require "pp"
|
220
|
+
python, python_config = PyCall::LibPython::Finder.find_python_config
|
221
|
+
|
222
|
+
puts "python_config:"
|
223
|
+
pp python_config
|
224
|
+
|
225
|
+
puts "\ncandidate_names:"
|
226
|
+
p PyCall::LibPython::Finder.candidate_names(python_config)
|
227
|
+
|
228
|
+
puts "\nlib_dirs:"
|
229
|
+
p PyCall::LibPython::Finder.make_libpaths(python_config)
|
230
|
+
|
231
|
+
puts "\ncandidate_paths:"
|
232
|
+
PyCall::LibPython::Finder.candidate_paths(python_config) do |path|
|
233
|
+
puts "- #{path}"
|
234
|
+
end
|
235
|
+
end
|