Skip to content

Commit

Permalink
Implement PyCallThread as a library, and give a puma example
Browse files Browse the repository at this point in the history
  • Loading branch information
snickell committed Aug 8, 2024
1 parent 4132260 commit 90c90b3
Show file tree
Hide file tree
Showing 14 changed files with 75 additions and 596 deletions.
4 changes: 2 additions & 2 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ source 'https://rubygems.org'
# To use the local path, you must also run things with:
# bundle exec ruby -I/Users/seth/src/pycall.rb/ext/pycall
#
gem 'pycall', path: '/Users/seth/src/pycall.rb', require: false
# gem 'pycall', path: '/Users/seth/src/pycall.rb', require: false

# gem 'pycall'
gem 'pycall'
gem 'puma', '~> 6.4.1'
8 changes: 2 additions & 6 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
PATH
remote: /Users/seth/src/pycall.rb
specs:
pycall (1.5.2)

GEM
remote: https://rubygems.org/
specs:
nio4r (2.7.3)
puma (6.4.2)
nio4r (~> 2.0)
pycall (1.5.2)

PLATFORMS
arm64-darwin-23

DEPENDENCIES
puma (~> 6.4.1)
pycall!
pycall

BUNDLED WITH
2.3.22
518 changes: 0 additions & 518 deletions README.md

This file was deleted.

10 changes: 0 additions & 10 deletions app.rb

This file was deleted.

4 changes: 0 additions & 4 deletions config.ru

This file was deleted.

32 changes: 0 additions & 32 deletions crash_puma.rb

This file was deleted.

12 changes: 0 additions & 12 deletions crash_puma.sh

This file was deleted.

25 changes: 25 additions & 0 deletions examples/puma/app.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
require_relative '../../lib/pycall_thread/pycall_thread'

PyCallThread.init do
# Setup our local venv (using pdm, in .venv)
ENV['PYTHON'] = `pdm run which python`.strip
site_dir = `pdm run python -c 'import site; print(site.getsitepackages()[0])'`.strip

require 'pycall'

# This is to setup our local venv
site = PyCall.import_module('site')
site.addsitedir(site_dir)
end

class App
def call(env)
winequality = PyCallThread.run do
pandas = PyCall.import_module('pandas')
data = pandas.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv', sep: ';')
data.to_html()
end

[200, { 'Content-Type' => 'text/html' }, [winequality]]
end
end
3 changes: 3 additions & 0 deletions examples/puma/config.ru
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
require './app'

run App.new
4 changes: 4 additions & 0 deletions examples/puma/start_puma.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh

# Start Puma with 3 threads on port 9292
puma -t 3:3 -p 9292
29 changes: 20 additions & 9 deletions pycall_thread.rb → lib/pycall_thread/pycall_thread.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ def self.init(unsafe_return: :error, &require_pycall_block)
# instead of doing it directly.
@py_thread = Thread.new { pycall_thread_loop(&require_pycall_block) }

at_exit { stop_pycall_thread }
at_exit do
stop_pycall_thread
end

nil
end
Expand All @@ -30,26 +32,34 @@ def self.run(&block)
init unless @initialized

result_queue = Queue.new
@queue << -> { result_queue << block.call }
retval = result_queue.pop
@queue << -> do
begin
result_queue << { retval: block.call }
rescue => e
result_queue << { exception: e }
end
end

puts "pycall_thread.done, retval = #{retval.inspect}"
result = result_queue.pop

if python_object?(retval)
if result[:exception]
raise result[:exception]
elsif python_object?(result[:retval])
msg = "Trying to return a python object from a PyCallThread.run block is potentially not thread-safe. Please convert #{result.inspect} to a basic Ruby type (like string, array, number, boolean etc) before returning."
case @unsafe_return
when :error
raise "Trying to return a python object from PyCDO.lock_python_gil block is potentially not thread-safe. Please convert it to a basic Ruby type (like string, array, number, boolean etc) before returning."
raise msg
when :warn
warn "Warning: Returning a Python object from PyCDO.lock_python_gil block is potentially not thread-safe. Please convert it to a basic Ruby type (like string, array, number, boolean etc) before returning."
warn "Warning: #{msg}"
end
end

puts "returning from pycall.run"
retval
result[:retval]
end

def self.stop_pycall_thread
@queue << :stop
@py_thread.join
end

def self.pycall_thread_loop(&require_pycall_block)
Expand All @@ -68,6 +78,7 @@ def self.pycall_thread_loop(&require_pycall_block)
block.call
rescue => e
puts "pycall_thread_loop(): exception in pycall_thread_loop #{e}"
puts e.backtrace.join("\n")
end

# If PyCall.finalize is not present, the main proces will hang at exit
Expand Down
19 changes: 18 additions & 1 deletion pdm.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions puma.rb

This file was deleted.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ requires-python = "==3.12.*"

dependencies = [
"pandas",
"psutil>=6.0.0",
]

[tool.pdm]
Expand Down

0 comments on commit 90c90b3

Please sign in to comment.