pycall 1.2.0 → 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.
data/Rakefile CHANGED
@@ -1,31 +1,93 @@
1
- require "bundler"
2
- Bundler::GemHelper.install_tasks
1
+ require "bundler/gem_helper"
2
+ require "rake/clean"
3
3
 
4
- require "rake"
5
- require "rake/extensiontask"
6
- require "rspec/core/rake_task"
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
- gem_spec = eval(File.read('pycall.gemspec'))
11
- Rake::ExtensionTask.new('pycall', gem_spec) do |ext|
12
- ext.lib_dir = File.join(*['lib', ENV['FAT_DIR']].compact)
13
- ext.cross_compile = true
14
- ext.cross_platform = %w[x86-mingw32 x64-mingw32]
15
- ext.cross_compiling do |s|
16
- s.files.concat %w[lib/2.2/pycall.so lib/2.3/pycall.so lib/2.4/pycall.so]
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
- Rake::ExtensionTask.new('pycall/spec_helper')
73
+ require "rake/extensiontask"
74
+ Rake::ExtensionTask.new("pycall/spec_helper")
21
75
 
22
- desc "Compile binaries for mingw platform using rake-compiler-dock"
23
- task 'build:mingw' do
24
- require 'rake_compiler_dock'
25
- RakeCompilerDock.sh "bundle && rake cross native gem RUBY_CC_VERSION=2.1.6:2.2.2:2.3.0:2.4.0"
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
- RSpec::Core::RakeTask.new(:spec)
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 :default => :spec
92
+ task default: :spec
31
93
  task spec: :compile
data/ci/travis_install.sh CHANGED
@@ -10,19 +10,35 @@ if test -z "$PYENV_VERSION"; then
10
10
  exit 1
11
11
  fi
12
12
 
13
+ pyenv_root=$(pyenv root)
14
+
13
15
  if test -n "$LIBPYTHON"; then
14
- export LIBPYTHON=$(pyenv root)/$LIBPYTHON
16
+ if test ! -f $LIBPYTHON; then
17
+ if test -f ${pyenv_root}/$LIBPYTHON; then
18
+ export LIBPYTHON=${pyenv_root}/$LIBPYTHON
19
+ else
20
+ echo "Invalid value in LIBPYTHON: ${LIBPYTHON}" >&2
21
+ exit 1
22
+ fi
23
+ fi
15
24
  fi
16
25
 
17
- if test "$PYENV_VERSION" = "system"; then
18
- if test -z "$LIBPYTHON"; then
19
- echo "ERROR: LIBPYTHON is not provided for PYENV_VERSION=system" >2
20
- exit 1
26
+ (
27
+ cd $(pyenv root)
28
+ if [ -d .git ]; then
29
+ git fetch origin
30
+ git checkout master
31
+ git reset --hard origin/master
21
32
  fi
22
- # NOTE: PYENV_VERSION should be the version of LIBPYTHON during install script
23
- PYENV_VERSION=$(basename $(dirname $(dirname $LIBPYTHON)))
24
- fi
25
- PYTHON_CONFIGURE_OPTS="--enable-shared" pyenv install -f $PYENV_VERSION
33
+ )
34
+
35
+ case $PYENV_VERSION in
36
+ system)
37
+ ;;
38
+ *)
39
+ PYTHON_CONFIGURE_OPTS="--enable-shared" pyenv install -f $PYENV_VERSION
40
+ ;;
41
+ esac
26
42
 
27
43
  case "$PYENV_VERSION" in
28
44
  *conda*)
@@ -40,6 +56,11 @@ case "$PYENV_VERSION" in
40
56
  travis_retry conda create -q -n test-environment python=$python_version numpy
41
57
  source $(pyenv prefix)/bin/activate test-environment
42
58
  ;;
59
+ system)
60
+ travis_retry pip install --user numpy
61
+ sudo sh -c "apt-get update && apt-get install --no-install-recommends -y python3-pip"
62
+ travis_retry python3.6 -m pip install --user numpy
63
+ ;;
43
64
  *)
44
65
  travis_retry pip install --user numpy
45
66
  ;;
@@ -2,7 +2,7 @@ require 'pycall/import'
2
2
  include PyCall::Import
3
3
 
4
4
  pyimport 'numpy', as: :np
5
- pyfrom 'sklearn.cross_validation', import: :train_test_split
5
+ pyfrom 'sklearn.model_selection', import: :train_test_split
6
6
  pyfrom 'sklearn.preprocessing', import: :StandardScaler
7
7
  pyfrom 'sklearn.datasets', import: %i(make_moons make_circles make_classification)
8
8
  pyfrom 'sklearn.neighbors', import: :KNeighborsClassifier
data/examples/hist.rb CHANGED
@@ -20,7 +20,7 @@ num_bins = 50
20
20
 
21
21
  fig, ax = *plt.subplots
22
22
 
23
- n, bins, patches = *ax.hist(x, num_bins, normed: 1)
23
+ n, bins, patches = *ax.hist(x, num_bins, density: 1)
24
24
 
25
25
  y = mlab.normpdf(bins, mu, sigma)
26
26
  ax.plot(bins, y, '--')
@@ -38,7 +38,7 @@
38
38
  "include PyCall::Import\n",
39
39
  "\n",
40
40
  "pyimport 'numpy', as: :np\n",
41
- "pyfrom 'sklearn.cross_validation', import: :train_test_split\n",
41
+ "pyfrom 'sklearn.model_selection', import: :train_test_split\n",
42
42
  "pyfrom 'sklearn.preprocessing', import: :StandardScaler\n",
43
43
  "pyfrom 'sklearn.datasets', import: %i(make_moons make_circles make_classification)\n",
44
44
  "pyfrom 'sklearn.neighbors', import: :KNeighborsClassifier\n",
@@ -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
+ }
@@ -30,7 +30,13 @@ lookup_libpython_api(VALUE libpython_handle, char const *name)
30
30
  arg.libpython_handle = libpython_handle;
31
31
  arg.name = name;
32
32
  addr = rb_protect((VALUE (*)(VALUE))lookup_libpython_api_0, (VALUE)&arg, &state);
33
- return (state || NIL_P(addr)) ? NULL : NUM2PTR(addr);
33
+ if (state) {
34
+ rb_set_errinfo(Qnil);
35
+ return NULL;
36
+ }
37
+ else {
38
+ return NIL_P(addr) ? NULL : NUM2PTR(addr);
39
+ }
34
40
  }
35
41
 
36
42
  #define LOOKUP_API_ENTRY(api_name) lookup_libpython_api(libpython_handle, #api_name)
data/ext/pycall/pycall.c CHANGED
@@ -70,6 +70,52 @@ pycall_after_fork(VALUE mod)
70
70
  return Qnil;
71
71
  }
72
72
 
73
+ static volatile pycall_tls_key without_gvl_key;
74
+
75
+ int
76
+ pycall_without_gvl_p(void)
77
+ {
78
+ /*
79
+ * In pthread, the default value is NULL (== 0).
80
+ *
81
+ * In Win32 thread, the default value is 0 (initialized by TlsAlloc).
82
+ */
83
+ return (int)pycall_tls_get(without_gvl_key);
84
+ }
85
+
86
+ static inline int
87
+ pycall_set_without_gvl(void)
88
+ {
89
+ return pycall_tls_set(without_gvl_key, (void *)1);
90
+ }
91
+
92
+ static inline int
93
+ pycall_set_with_gvl(void)
94
+ {
95
+ return pycall_tls_set(without_gvl_key, (void *)0);
96
+ }
97
+
98
+ VALUE
99
+ pycall_without_gvl(VALUE (* func)(VALUE), VALUE arg)
100
+ {
101
+ int state;
102
+ VALUE result;
103
+
104
+ pycall_set_without_gvl();
105
+
106
+ result = rb_protect(func, arg, &state);
107
+
108
+ pycall_set_with_gvl();
109
+
110
+ return result;
111
+ }
112
+
113
+ static VALUE
114
+ pycall_m_without_gvl(VALUE mod)
115
+ {
116
+ return pycall_without_gvl(rb_yield, Qnil);
117
+ }
118
+
73
119
  /* ==== PyCall::PyPtr ==== */
74
120
 
75
121
  const rb_data_type_t pycall_pyptr_data_type = {
@@ -890,7 +936,7 @@ struct call_pyobject_call_params {
890
936
  PyObject *kwargs;
891
937
  };
892
938
 
893
- PyObject *
939
+ static inline PyObject *
894
940
  call_pyobject_call(struct call_pyobject_call_params *params)
895
941
  {
896
942
  PyObject *res;
@@ -899,7 +945,7 @@ call_pyobject_call(struct call_pyobject_call_params *params)
899
945
  }
900
946
 
901
947
  PyObject *
902
- pyobject_call_without_gvl(PyObject *pycallable, PyObject *args, PyObject *kwargs)
948
+ pyobject_call(PyObject *pycallable, PyObject *args, PyObject *kwargs)
903
949
  {
904
950
  PyObject *res;
905
951
  struct call_pyobject_call_params params;
@@ -907,9 +953,14 @@ pyobject_call_without_gvl(PyObject *pycallable, PyObject *args, PyObject *kwargs
907
953
  params.args = args;
908
954
  params.kwargs = kwargs;
909
955
 
910
- res = (PyObject *)rb_thread_call_without_gvl(
911
- (void * (*)(void *))call_pyobject_call, (void *)&params,
912
- (rb_unblock_function_t *)pycall_interrupt_python_thread, NULL);
956
+ if (pycall_without_gvl_p()) {
957
+ res = (PyObject *)rb_thread_call_without_gvl(
958
+ (void * (*)(void *))call_pyobject_call, (void *)&params,
959
+ (rb_unblock_function_t *)pycall_interrupt_python_thread, NULL);
960
+ }
961
+ else {
962
+ res = call_pyobject_call(&params);
963
+ }
913
964
 
914
965
  return res;
915
966
  }
@@ -961,7 +1012,7 @@ pycall_call_python_callable(PyObject *pycallable, int argc, VALUE *argv)
961
1012
  }
962
1013
  }
963
1014
 
964
- res = pyobject_call_without_gvl(pycallable, args, kwargs); /* New reference */
1015
+ res = pyobject_call(pycallable, args, kwargs); /* New reference */
965
1016
  if (!res) {
966
1017
  pycall_pyerror_fetch_and_raise("PyObject_Call in pycall_call_python_callable");
967
1018
  }
@@ -2185,6 +2236,9 @@ Init_pycall(void)
2185
2236
 
2186
2237
  rb_define_module_function(mPyCall, "after_fork", pycall_after_fork, 0);
2187
2238
 
2239
+ pycall_tls_create(&without_gvl_key);
2240
+ rb_define_module_function(mPyCall, "without_gvl", pycall_m_without_gvl, 0);
2241
+
2188
2242
  /* PyCall::PyPtr */
2189
2243
 
2190
2244
  cPyPtr = rb_define_class_under(mPyCall, "PyPtr", rb_cBasicObject);
@@ -11,10 +11,19 @@ extern "C" {
11
11
  #include <ruby.h>
12
12
  #include <ruby/encoding.h>
13
13
  #include <ruby/thread.h>
14
+
14
15
  #include <assert.h>
15
16
  #include <inttypes.h>
16
17
  #include <limits.h>
17
18
 
19
+ #if defined(_WIN32)
20
+ # define PYCALL_THREAD_WIN32
21
+ # include <ruby/win32.h>
22
+ #elif defined(HAVE_PTHREAD_H)
23
+ # define PYCALL_THREAD_PTHREAD
24
+ # include <pthread.h>
25
+ #endif
26
+
18
27
  #if SIZEOF_LONG == SIZEOF_VOIDP
19
28
  # define PTR2NUM(x) (LONG2NUM((long)(x)))
20
29
  # define NUM2PTR(x) ((void*)(NUM2ULONG(x)))
@@ -492,6 +501,23 @@ extern PyTypeObject PyRuby_Type;
492
501
 
493
502
  PyObject * PyRuby_New(VALUE ruby_object);
494
503
 
504
+ /* ==== thread support ==== */
505
+
506
+ #if defined(PYCALL_THREAD_WIN32)
507
+ typedef DWORD pycall_tls_key;
508
+ #elif defined(PYCALL_THREAD_PTHREAD)
509
+ typedef pthread_key_t pycall_tls_key;
510
+ #else
511
+ # error "unsupported thread type"
512
+ #endif
513
+
514
+ int pycall_tls_create(pycall_tls_key* tls_key);
515
+ void *pycall_tls_get(pycall_tls_key tls_key);
516
+ int pycall_tls_set(pycall_tls_key tls_key, void *ptr);
517
+
518
+ int pycall_without_gvl_p(void);
519
+ VALUE pycall_without_gvl(VALUE (* func)(VALUE), VALUE arg);
520
+
495
521
  /* ==== pycall ==== */
496
522
 
497
523
  typedef struct {
@@ -637,7 +663,7 @@ Py_ssize_t pycall_python_hexversion(void);
637
663
 
638
664
  void pycall_Py_DecRef(PyObject *);
639
665
 
640
- RUBY_EXTERN const rb_data_type_t pycall_pyptr_data_type;
666
+ extern const rb_data_type_t pycall_pyptr_data_type;
641
667
  size_t pycall_pyptr_memsize(void const *);
642
668
  void pycall_pyptr_free(void *);
643
669