pycall_thread 0.1.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 04ac373bef738633cd5d2746983182a216f6a5174a9b927709c5d133234d0a6c
4
- data.tar.gz: 11e50545784a6d10755c489e1f95b2b23bcd9965356c3881a2dce7109d3bd6f2
3
+ metadata.gz: 5f06a9a4d8b09d14190d2001b9463bdd9101df77ced43e4cf898247718d69b1a
4
+ data.tar.gz: 0e0121315ee956037688ad7016621dc3f667c4ec99e05f7541a3a74b4ea61b2c
5
5
  SHA512:
6
- metadata.gz: b67602e6888e458a64cd2874a540d9e7bba78cd8ff38e7642c2c1b1ef74da2e854890f6ce0cb139b0ad4b84c0c28d84b6209dcd4ad8a15a8027dbd6d1651084b
7
- data.tar.gz: d8d8955f9b99a51cc8f3a80c81c39604945a3beafc4930dfa38f715144d7ca207aab778fc95ffcfa000bf6447224bce143463cbfd033481567cb86e477ad8a4a
6
+ metadata.gz: 168d6a9aa3c30ece2692ee3bb92502dbabd766097c3e2a90849f3d7d61384806b1e658cb8008c34714a2617c83ca416c395da55c44d2da4d62318d30062385a8
7
+ data.tar.gz: fc32925321e4ea1812ecde6416ef6d90130bbc6254a72beaeb265710ed18fc6f44fd1ebc0bf9fcdf7b7eb8d01829ab126cb2f0676c72b86039dda5e0c4c39683
data/.rubocop.yml CHANGED
@@ -10,4 +10,7 @@ Style/StringLiteralsInInterpolation:
10
10
  EnforcedStyle: double_quotes
11
11
 
12
12
  Layout/LineLength:
13
- Max: 120
13
+ Enabled: false
14
+
15
+ Metrics/MethodLength:
16
+ Enabled: false
data/Gemfile CHANGED
@@ -9,11 +9,7 @@ gem "rake", "~> 13.0"
9
9
  gem "minitest", "~> 5.0"
10
10
  gem "rubocop", "~> 1.21"
11
11
 
12
-
13
- # Include Puma for testing examples:
14
- gem 'puma', '~> 6.4.1'
15
-
16
- # To use the local path, you must also run things with:
12
+ # To use the local pycall, you must also run things with e.g.:
17
13
  # bundle exec ruby -I/Users/seth/src/pycall.rb/ext/pycall
18
14
  #
19
15
  # gem 'pycall', path: '/Users/seth/src/pycall.rb', require: false
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pycall_thread (0.1.0)
4
+ pycall_thread (0.1.2)
5
5
  pycall
6
6
 
7
7
  GEM
@@ -11,13 +11,10 @@ GEM
11
11
  json (2.7.2)
12
12
  language_server-protocol (3.17.0.3)
13
13
  minitest (5.24.1)
14
- nio4r (2.7.3)
15
14
  parallel (1.26.0)
16
15
  parser (3.3.4.2)
17
16
  ast (~> 2.4.1)
18
17
  racc
19
- puma (6.4.2)
20
- nio4r (~> 2.0)
21
18
  pycall (1.5.2)
22
19
  racc (1.8.1)
23
20
  rainbow (3.1.1)
@@ -47,7 +44,6 @@ PLATFORMS
47
44
 
48
45
  DEPENDENCIES
49
46
  minitest (~> 5.0)
50
- puma (~> 6.4.1)
51
47
  pycall_thread!
52
48
  rake (~> 13.0)
53
49
  rubocop (~> 1.21)
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "puma", "~> 6.4.2"
4
+ gem "pycall_thread", "~> 0.1.2"
@@ -0,0 +1,19 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ nio4r (2.7.3)
5
+ puma (6.4.2)
6
+ nio4r (~> 2.0)
7
+ pycall (1.5.2)
8
+ pycall_thread (0.1.0)
9
+ pycall
10
+
11
+ PLATFORMS
12
+ arm64-darwin-23
13
+
14
+ DEPENDENCIES
15
+ puma
16
+ pycall_thread
17
+
18
+ BUNDLED WITH
19
+ 2.2.33
@@ -0,0 +1,10 @@
1
+ # PyCallThread Puma Example
2
+
3
+ ```
4
+ bundle install
5
+ bundle exec ./start_puma.sh
6
+ ```
7
+
8
+ Open: [http://localhost:9292](http://localhost:9292)
9
+
10
+ All the interesting code is in [app.rb](./app.rb)
data/examples/puma/app.rb CHANGED
@@ -1,25 +1,26 @@
1
- require_relative '../../lib/pycall_thread/pycall_thread'
1
+ require "pycall_thread"
2
2
 
3
3
  PyCallThread.init do
4
4
  # Setup our local venv (using pdm, in .venv)
5
- ENV['PYTHON'] = `pdm run which python`.strip
5
+ ENV["PYTHON"] = `pdm run which python`.strip
6
6
  site_dir = `pdm run python -c 'import site; print(site.getsitepackages()[0])'`.strip
7
7
 
8
- require 'pycall'
8
+ require "pycall"
9
9
 
10
10
  # This is to setup our local venv
11
- site = PyCall.import_module('site')
11
+ site = PyCall.import_module("site")
12
12
  site.addsitedir(site_dir)
13
13
  end
14
14
 
15
+ # Simple Puma App that demonstrates PyCallThread
15
16
  class App
16
- def call(env)
17
+ def call(_)
17
18
  winequality = PyCallThread.run do
18
- pandas = PyCall.import_module('pandas')
19
- data = pandas.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv', sep: ';')
20
- data.to_html()
19
+ pandas = PyCall.import_module("pandas")
20
+ data = pandas.read_csv("https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv", sep: ";")
21
+ data.to_html
21
22
  end
22
23
 
23
- [200, { 'Content-Type' => 'text/html' }, [winequality]]
24
+ [200, { "Content-Type" => "text/html" }, [winequality]]
24
25
  end
25
- end
26
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PycallThread
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.2"
5
5
  end
data/lib/pycall_thread.rb CHANGED
@@ -1,30 +1,40 @@
1
+ # Provides a way to run PyCall code from multiple threads, see
2
+ # PyCallThread.init and PyCallThread.run for more information.
1
3
  module PyCallThread
2
4
  @queue = Queue.new
3
5
 
4
- VALID_UNSAFE_RETURN_VALUES = [:allow, :error, :warn]
6
+ VALID_UNSAFE_RETURN_VALUES = %i[allow error warn].freeze
5
7
 
6
8
  def self.init(unsafe_return: :error, &require_pycall_block)
7
- # Only safe to use PyCallThread if PyCall hasn't already been loaded
8
- raise "PyCall::LibPython already exists: PyCall can't have been initialized already" if defined?(PyCall::LibPython)
9
+ raise ArgumentError, "Invalid value for unsafe_return: #{unsafe_return}. Must be one of: #{VALID_UNSAFE_RETURN_VALUES.join(", ")}" unless VALID_UNSAFE_RETURN_VALUES.include?(unsafe_return)
9
10
 
10
- @initialized = true
11
+ @unsafe_return = unsafe_return
11
12
 
12
- if VALID_UNSAFE_RETURN_VALUES.include?(unsafe_return)
13
- @unsafe_return = unsafe_return
14
- else
15
- raise ArgumentError, "Invalid value for unsafe_return: #{unsafe_return}. Must be one of: #{VALID_UNSAFE_RETURN_VALUES.join(', ')}"
16
- end
17
-
18
13
  # Start the thread we will use to run code invoked with PyCallThread.run
14
+ @py_thread = Thread.new { pycall_thread_loop }
15
+
19
16
  # If we've been passed a require_pycall_block, use that to require 'pycall'
20
17
  # instead of doing it directly.
21
- @py_thread = Thread.new { pycall_thread_loop(&require_pycall_block) }
18
+ require_pycall(&require_pycall_block)
22
19
 
23
- at_exit do
24
- stop_pycall_thread
25
- end
20
+ at_exit { stop_pycall_thread }
26
21
 
27
- nil
22
+ @initialized = true
23
+ end
24
+
25
+ def self.require_pycall(&require_pycall_block)
26
+ # Only safe to use PyCallThread if PyCall hasn't already been loaded
27
+ raise "PyCall::LibPython already exists: PyCall can't have been initialized already" if defined?(PyCall::LibPython)
28
+
29
+ run do
30
+ # require 'pycall' or run a user-defined block that should do the same
31
+ if require_pycall_block
32
+ require_pycall_block.call
33
+ else
34
+ require "pycall"
35
+ end
36
+ nil
37
+ end
28
38
  end
29
39
 
30
40
  # Runs &block on the PyCall thread, and returns the result
@@ -32,16 +42,18 @@ module PyCallThread
32
42
  init unless @initialized
33
43
 
34
44
  result_queue = Queue.new
35
- @queue << -> do
45
+ @queue << lambda {
36
46
  begin
37
47
  result_queue << { retval: block.call }
38
- rescue => e
48
+ rescue StandardError => e
39
49
  result_queue << { exception: e }
40
50
  end
41
- end
51
+ }
42
52
 
43
- result = result_queue.pop
53
+ run_result(result_queue.pop)
54
+ end
44
55
 
56
+ def self.run_result
45
57
  if result[:exception]
46
58
  raise result[:exception]
47
59
  elsif python_object?(result[:retval])
@@ -62,21 +74,15 @@ module PyCallThread
62
74
  @py_thread.join
63
75
  end
64
76
 
65
- def self.pycall_thread_loop(&require_pycall_block)
77
+ def self.pycall_thread_loop
66
78
  Thread.current.name = "pycall"
67
79
 
68
- # require 'pycall' or run a user-defined block that should do the same
69
- if require_pycall_block
70
- require_pycall_block.call
71
- else
72
- require 'pycall'
73
- end
74
-
75
80
  loop do
76
81
  block = @queue.pop
77
82
  break if block == :stop
83
+
78
84
  block.call
79
- rescue => e
85
+ rescue StandardError => e
80
86
  puts "pycall_thread_loop(): exception in pycall_thread_loop #{e}"
81
87
  puts e.backtrace.join("\n")
82
88
  end
@@ -93,7 +99,7 @@ module PyCallThread
93
99
  PyCall::PyModuleWrapper,
94
100
  PyCall::PyObjectWrapper,
95
101
  PyCall::PyTypeObjectWrapper,
96
- PyCall::PyPtr,
102
+ PyCall::PyPtr
97
103
  ].any? { |kind| obj.is_a?(kind) }
98
104
  end
99
- end
105
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pycall_thread
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Seth Nickell
@@ -41,6 +41,9 @@ files:
41
41
  - Rakefile
42
42
  - bin/console
43
43
  - bin/setup
44
+ - examples/puma/Gemfile
45
+ - examples/puma/Gemfile.lock
46
+ - examples/puma/README.md
44
47
  - examples/puma/app.rb
45
48
  - examples/puma/config.ru
46
49
  - examples/puma/start_puma.sh