pycall 1.3.1 → 1.4.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.
- 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
|