pycall_thread 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.pdm-python +1 -0
- data/.rubocop.yml +13 -0
- data/.ruby-version +1 -0
- data/Gemfile +19 -0
- data/Gemfile.lock +56 -0
- data/README.md +50 -0
- data/Rakefile +16 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/examples/puma/app.rb +25 -0
- data/examples/puma/config.ru +3 -0
- data/examples/puma/start_puma.sh +4 -0
- data/lib/pycall_thread/version.rb +5 -0
- data/lib/pycall_thread.rb +99 -0
- data/pdm.lock +327 -0
- data/pycall_thread.gemspec +41 -0
- data/pyproject.toml +19 -0
- data/sig/pycall_thread.rbs +4 -0
- metadata +79 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 04ac373bef738633cd5d2746983182a216f6a5174a9b927709c5d133234d0a6c
|
4
|
+
data.tar.gz: 11e50545784a6d10755c489e1f95b2b23bcd9965356c3881a2dce7109d3bd6f2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b67602e6888e458a64cd2874a540d9e7bba78cd8ff38e7642c2c1b1ef74da2e854890f6ce0cb139b0ad4b84c0c28d84b6209dcd4ad8a15a8027dbd6d1651084b
|
7
|
+
data.tar.gz: d8d8955f9b99a51cc8f3a80c81c39604945a3beafc4930dfa38f715144d7ca207aab778fc95ffcfa000bf6447224bce143463cbfd033481567cb86e477ad8a4a
|
data/.pdm-python
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
/Users/seth/src/tester/justpuma/.venv/bin/python
|
data/.rubocop.yml
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.0.6
|
data/Gemfile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
# Specify your gem's dependencies in pycall_thread.gemspec
|
6
|
+
gemspec
|
7
|
+
|
8
|
+
gem "rake", "~> 13.0"
|
9
|
+
gem "minitest", "~> 5.0"
|
10
|
+
gem "rubocop", "~> 1.21"
|
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:
|
17
|
+
# bundle exec ruby -I/Users/seth/src/pycall.rb/ext/pycall
|
18
|
+
#
|
19
|
+
# gem 'pycall', path: '/Users/seth/src/pycall.rb', require: false
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
pycall_thread (0.1.0)
|
5
|
+
pycall
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
ast (2.4.2)
|
11
|
+
json (2.7.2)
|
12
|
+
language_server-protocol (3.17.0.3)
|
13
|
+
minitest (5.24.1)
|
14
|
+
nio4r (2.7.3)
|
15
|
+
parallel (1.26.0)
|
16
|
+
parser (3.3.4.2)
|
17
|
+
ast (~> 2.4.1)
|
18
|
+
racc
|
19
|
+
puma (6.4.2)
|
20
|
+
nio4r (~> 2.0)
|
21
|
+
pycall (1.5.2)
|
22
|
+
racc (1.8.1)
|
23
|
+
rainbow (3.1.1)
|
24
|
+
rake (13.2.1)
|
25
|
+
regexp_parser (2.9.2)
|
26
|
+
rexml (3.3.4)
|
27
|
+
strscan
|
28
|
+
rubocop (1.65.1)
|
29
|
+
json (~> 2.3)
|
30
|
+
language_server-protocol (>= 3.17.0)
|
31
|
+
parallel (~> 1.10)
|
32
|
+
parser (>= 3.3.0.2)
|
33
|
+
rainbow (>= 2.2.2, < 4.0)
|
34
|
+
regexp_parser (>= 2.4, < 3.0)
|
35
|
+
rexml (>= 3.2.5, < 4.0)
|
36
|
+
rubocop-ast (>= 1.31.1, < 2.0)
|
37
|
+
ruby-progressbar (~> 1.7)
|
38
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
39
|
+
rubocop-ast (1.32.0)
|
40
|
+
parser (>= 3.3.1.0)
|
41
|
+
ruby-progressbar (1.13.0)
|
42
|
+
strscan (3.1.0)
|
43
|
+
unicode-display_width (2.5.0)
|
44
|
+
|
45
|
+
PLATFORMS
|
46
|
+
arm64-darwin-23
|
47
|
+
|
48
|
+
DEPENDENCIES
|
49
|
+
minitest (~> 5.0)
|
50
|
+
puma (~> 6.4.1)
|
51
|
+
pycall_thread!
|
52
|
+
rake (~> 13.0)
|
53
|
+
rubocop (~> 1.21)
|
54
|
+
|
55
|
+
BUNDLED WITH
|
56
|
+
2.2.33
|
data/README.md
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# PyCallThread
|
2
|
+
|
3
|
+
PyCallThread provides `PyCallThread.run(&block)`, which lets you safely run a block of
|
4
|
+
[pycall.rb](https://github.com/mrkn/pycall.rb) code: even if you're in a thread.
|
5
|
+
|
6
|
+
This makes PyCall easier to use from Ruby on Rails, Puma and other threaded web servers.
|
7
|
+
|
8
|
+
## Usage
|
9
|
+
|
10
|
+
```
|
11
|
+
require 'pycall_thread'
|
12
|
+
|
13
|
+
# Initialization is optional but gives you a few config settings
|
14
|
+
PyCallThread.init do
|
15
|
+
# If you need to do anything to setup you venv, you can do it here
|
16
|
+
require 'pycall'
|
17
|
+
end
|
18
|
+
|
19
|
+
# We can safely call PyCall, even from a thread (or web request) using `PyCallThread.run`:
|
20
|
+
Thread.new do
|
21
|
+
data_table = PyCallThread.run do
|
22
|
+
pandas = PyCall.import('pandas')
|
23
|
+
data = pandas.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv', sep: ';')
|
24
|
+
data.head().to_string()
|
25
|
+
end
|
26
|
+
puts "Data is:"
|
27
|
+
puts data_table
|
28
|
+
end
|
29
|
+
```
|
30
|
+
|
31
|
+
Examples of using PyCall with webservers:
|
32
|
+
|
33
|
+
- [Puma](./examples/puma)
|
34
|
+
- Ruby on Rails: todo
|
35
|
+
|
36
|
+
## Installation
|
37
|
+
|
38
|
+
Gemfile:
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
gem 'pycall_thread'
|
42
|
+
```
|
43
|
+
|
44
|
+
## Development
|
45
|
+
|
46
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
47
|
+
|
48
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
49
|
+
|
50
|
+
TODO: make run `rake test` run the tests
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
require "rake/testtask"
|
5
|
+
|
6
|
+
Rake::TestTask.new(:test) do |t|
|
7
|
+
t.libs << "test"
|
8
|
+
t.libs << "lib"
|
9
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
10
|
+
end
|
11
|
+
|
12
|
+
require "rubocop/rake_task"
|
13
|
+
|
14
|
+
RuboCop::RakeTask.new
|
15
|
+
|
16
|
+
task default: %i[test rubocop]
|
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "pycall_thread"
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require "irb"
|
15
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative '../../lib/pycall_thread/pycall_thread'
|
2
|
+
|
3
|
+
PyCallThread.init do
|
4
|
+
# Setup our local venv (using pdm, in .venv)
|
5
|
+
ENV['PYTHON'] = `pdm run which python`.strip
|
6
|
+
site_dir = `pdm run python -c 'import site; print(site.getsitepackages()[0])'`.strip
|
7
|
+
|
8
|
+
require 'pycall'
|
9
|
+
|
10
|
+
# This is to setup our local venv
|
11
|
+
site = PyCall.import_module('site')
|
12
|
+
site.addsitedir(site_dir)
|
13
|
+
end
|
14
|
+
|
15
|
+
class App
|
16
|
+
def call(env)
|
17
|
+
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()
|
21
|
+
end
|
22
|
+
|
23
|
+
[200, { 'Content-Type' => 'text/html' }, [winequality]]
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module PyCallThread
|
2
|
+
@queue = Queue.new
|
3
|
+
|
4
|
+
VALID_UNSAFE_RETURN_VALUES = [:allow, :error, :warn]
|
5
|
+
|
6
|
+
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
|
+
|
10
|
+
@initialized = true
|
11
|
+
|
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
|
+
# Start the thread we will use to run code invoked with PyCallThread.run
|
19
|
+
# If we've been passed a require_pycall_block, use that to require 'pycall'
|
20
|
+
# instead of doing it directly.
|
21
|
+
@py_thread = Thread.new { pycall_thread_loop(&require_pycall_block) }
|
22
|
+
|
23
|
+
at_exit do
|
24
|
+
stop_pycall_thread
|
25
|
+
end
|
26
|
+
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
|
30
|
+
# Runs &block on the PyCall thread, and returns the result
|
31
|
+
def self.run(&block)
|
32
|
+
init unless @initialized
|
33
|
+
|
34
|
+
result_queue = Queue.new
|
35
|
+
@queue << -> do
|
36
|
+
begin
|
37
|
+
result_queue << { retval: block.call }
|
38
|
+
rescue => e
|
39
|
+
result_queue << { exception: e }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
result = result_queue.pop
|
44
|
+
|
45
|
+
if result[:exception]
|
46
|
+
raise result[:exception]
|
47
|
+
elsif python_object?(result[:retval])
|
48
|
+
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."
|
49
|
+
case @unsafe_return
|
50
|
+
when :error
|
51
|
+
raise msg
|
52
|
+
when :warn
|
53
|
+
warn "Warning: #{msg}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
result[:retval]
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.stop_pycall_thread
|
61
|
+
@queue << :stop
|
62
|
+
@py_thread.join
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.pycall_thread_loop(&require_pycall_block)
|
66
|
+
Thread.current.name = "pycall"
|
67
|
+
|
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
|
+
loop do
|
76
|
+
block = @queue.pop
|
77
|
+
break if block == :stop
|
78
|
+
block.call
|
79
|
+
rescue => e
|
80
|
+
puts "pycall_thread_loop(): exception in pycall_thread_loop #{e}"
|
81
|
+
puts e.backtrace.join("\n")
|
82
|
+
end
|
83
|
+
|
84
|
+
# If PyCall.finalize is not present, the main proces will hang at exit
|
85
|
+
# See: https://github.com/mrkn/pycall.rb/pull/187
|
86
|
+
PyCall.finalize if PyCall.respond_to?(:finalize)
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.python_object?(obj)
|
90
|
+
[
|
91
|
+
PyCall::IterableWrapper,
|
92
|
+
PyCall::PyObjectWrapper,
|
93
|
+
PyCall::PyModuleWrapper,
|
94
|
+
PyCall::PyObjectWrapper,
|
95
|
+
PyCall::PyTypeObjectWrapper,
|
96
|
+
PyCall::PyPtr,
|
97
|
+
].any? { |kind| obj.is_a?(kind) }
|
98
|
+
end
|
99
|
+
end
|
data/pdm.lock
ADDED
@@ -0,0 +1,327 @@
|
|
1
|
+
# This file is @generated by PDM.
|
2
|
+
# It is not intended for manual editing.
|
3
|
+
|
4
|
+
[metadata]
|
5
|
+
groups = ["default", "dev"]
|
6
|
+
strategy = ["inherit_metadata"]
|
7
|
+
lock_version = "4.5.0"
|
8
|
+
content_hash = "sha256:1a7142df64b7d1c563d85942f53f3f3e7b5b37423c14d97a925cc35b3cdeb731"
|
9
|
+
|
10
|
+
[[metadata.targets]]
|
11
|
+
requires_python = "==3.12.*"
|
12
|
+
|
13
|
+
[[package]]
|
14
|
+
name = "asttokens"
|
15
|
+
version = "2.4.1"
|
16
|
+
summary = "Annotate AST trees with source code positions"
|
17
|
+
groups = ["dev"]
|
18
|
+
dependencies = [
|
19
|
+
"six>=1.12.0",
|
20
|
+
"typing; python_version < \"3.5\"",
|
21
|
+
]
|
22
|
+
files = [
|
23
|
+
{file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"},
|
24
|
+
{file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"},
|
25
|
+
]
|
26
|
+
|
27
|
+
[[package]]
|
28
|
+
name = "colorama"
|
29
|
+
version = "0.4.6"
|
30
|
+
requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
31
|
+
summary = "Cross-platform colored terminal text."
|
32
|
+
groups = ["dev"]
|
33
|
+
marker = "sys_platform == \"win32\""
|
34
|
+
files = [
|
35
|
+
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
36
|
+
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
37
|
+
]
|
38
|
+
|
39
|
+
[[package]]
|
40
|
+
name = "decorator"
|
41
|
+
version = "5.1.1"
|
42
|
+
requires_python = ">=3.5"
|
43
|
+
summary = "Decorators for Humans"
|
44
|
+
groups = ["dev"]
|
45
|
+
files = [
|
46
|
+
{file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"},
|
47
|
+
{file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
|
48
|
+
]
|
49
|
+
|
50
|
+
[[package]]
|
51
|
+
name = "executing"
|
52
|
+
version = "2.0.1"
|
53
|
+
requires_python = ">=3.5"
|
54
|
+
summary = "Get the currently executing AST node of a frame, and other information"
|
55
|
+
groups = ["dev"]
|
56
|
+
files = [
|
57
|
+
{file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"},
|
58
|
+
{file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"},
|
59
|
+
]
|
60
|
+
|
61
|
+
[[package]]
|
62
|
+
name = "ipython"
|
63
|
+
version = "8.26.0"
|
64
|
+
requires_python = ">=3.10"
|
65
|
+
summary = "IPython: Productive Interactive Computing"
|
66
|
+
groups = ["dev"]
|
67
|
+
dependencies = [
|
68
|
+
"colorama; sys_platform == \"win32\"",
|
69
|
+
"decorator",
|
70
|
+
"exceptiongroup; python_version < \"3.11\"",
|
71
|
+
"jedi>=0.16",
|
72
|
+
"matplotlib-inline",
|
73
|
+
"pexpect>4.3; sys_platform != \"win32\" and sys_platform != \"emscripten\"",
|
74
|
+
"prompt-toolkit<3.1.0,>=3.0.41",
|
75
|
+
"pygments>=2.4.0",
|
76
|
+
"stack-data",
|
77
|
+
"traitlets>=5.13.0",
|
78
|
+
"typing-extensions>=4.6; python_version < \"3.12\"",
|
79
|
+
]
|
80
|
+
files = [
|
81
|
+
{file = "ipython-8.26.0-py3-none-any.whl", hash = "sha256:e6b347c27bdf9c32ee9d31ae85defc525755a1869f14057e900675b9e8d6e6ff"},
|
82
|
+
{file = "ipython-8.26.0.tar.gz", hash = "sha256:1cec0fbba8404af13facebe83d04436a7434c7400e59f47acf467c64abd0956c"},
|
83
|
+
]
|
84
|
+
|
85
|
+
[[package]]
|
86
|
+
name = "jedi"
|
87
|
+
version = "0.19.1"
|
88
|
+
requires_python = ">=3.6"
|
89
|
+
summary = "An autocompletion tool for Python that can be used for text editors."
|
90
|
+
groups = ["dev"]
|
91
|
+
dependencies = [
|
92
|
+
"parso<0.9.0,>=0.8.3",
|
93
|
+
]
|
94
|
+
files = [
|
95
|
+
{file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"},
|
96
|
+
{file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"},
|
97
|
+
]
|
98
|
+
|
99
|
+
[[package]]
|
100
|
+
name = "matplotlib-inline"
|
101
|
+
version = "0.1.7"
|
102
|
+
requires_python = ">=3.8"
|
103
|
+
summary = "Inline Matplotlib backend for Jupyter"
|
104
|
+
groups = ["dev"]
|
105
|
+
dependencies = [
|
106
|
+
"traitlets",
|
107
|
+
]
|
108
|
+
files = [
|
109
|
+
{file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"},
|
110
|
+
{file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"},
|
111
|
+
]
|
112
|
+
|
113
|
+
[[package]]
|
114
|
+
name = "numpy"
|
115
|
+
version = "1.26.4"
|
116
|
+
requires_python = ">=3.9"
|
117
|
+
summary = "Fundamental package for array computing in Python"
|
118
|
+
groups = ["default"]
|
119
|
+
files = [
|
120
|
+
{file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"},
|
121
|
+
{file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"},
|
122
|
+
{file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"},
|
123
|
+
{file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"},
|
124
|
+
{file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"},
|
125
|
+
{file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"},
|
126
|
+
{file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"},
|
127
|
+
{file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"},
|
128
|
+
{file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"},
|
129
|
+
]
|
130
|
+
|
131
|
+
[[package]]
|
132
|
+
name = "pandas"
|
133
|
+
version = "2.2.2"
|
134
|
+
requires_python = ">=3.9"
|
135
|
+
summary = "Powerful data structures for data analysis, time series, and statistics"
|
136
|
+
groups = ["default"]
|
137
|
+
dependencies = [
|
138
|
+
"numpy>=1.22.4; python_version < \"3.11\"",
|
139
|
+
"numpy>=1.23.2; python_version == \"3.11\"",
|
140
|
+
"numpy>=1.26.0; python_version >= \"3.12\"",
|
141
|
+
"python-dateutil>=2.8.2",
|
142
|
+
"pytz>=2020.1",
|
143
|
+
"tzdata>=2022.7",
|
144
|
+
]
|
145
|
+
files = [
|
146
|
+
{file = "pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef"},
|
147
|
+
{file = "pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce"},
|
148
|
+
{file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad"},
|
149
|
+
{file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad"},
|
150
|
+
{file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76"},
|
151
|
+
{file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32"},
|
152
|
+
{file = "pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23"},
|
153
|
+
{file = "pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54"},
|
154
|
+
]
|
155
|
+
|
156
|
+
[[package]]
|
157
|
+
name = "parso"
|
158
|
+
version = "0.8.4"
|
159
|
+
requires_python = ">=3.6"
|
160
|
+
summary = "A Python Parser"
|
161
|
+
groups = ["dev"]
|
162
|
+
files = [
|
163
|
+
{file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"},
|
164
|
+
{file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"},
|
165
|
+
]
|
166
|
+
|
167
|
+
[[package]]
|
168
|
+
name = "pexpect"
|
169
|
+
version = "4.9.0"
|
170
|
+
summary = "Pexpect allows easy control of interactive console applications."
|
171
|
+
groups = ["dev"]
|
172
|
+
marker = "sys_platform != \"win32\" and sys_platform != \"emscripten\""
|
173
|
+
dependencies = [
|
174
|
+
"ptyprocess>=0.5",
|
175
|
+
]
|
176
|
+
files = [
|
177
|
+
{file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"},
|
178
|
+
{file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"},
|
179
|
+
]
|
180
|
+
|
181
|
+
[[package]]
|
182
|
+
name = "prompt-toolkit"
|
183
|
+
version = "3.0.47"
|
184
|
+
requires_python = ">=3.7.0"
|
185
|
+
summary = "Library for building powerful interactive command lines in Python"
|
186
|
+
groups = ["dev"]
|
187
|
+
dependencies = [
|
188
|
+
"wcwidth",
|
189
|
+
]
|
190
|
+
files = [
|
191
|
+
{file = "prompt_toolkit-3.0.47-py3-none-any.whl", hash = "sha256:0d7bfa67001d5e39d02c224b663abc33687405033a8c422d0d675a5a13361d10"},
|
192
|
+
{file = "prompt_toolkit-3.0.47.tar.gz", hash = "sha256:1e1b29cb58080b1e69f207c893a1a7bf16d127a5c30c9d17a25a5d77792e5360"},
|
193
|
+
]
|
194
|
+
|
195
|
+
[[package]]
|
196
|
+
name = "psutil"
|
197
|
+
version = "6.0.0"
|
198
|
+
requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
|
199
|
+
summary = "Cross-platform lib for process and system monitoring in Python."
|
200
|
+
groups = ["default"]
|
201
|
+
files = [
|
202
|
+
{file = "psutil-6.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c588a7e9b1173b6e866756dde596fd4cad94f9399daf99ad8c3258b3cb2b47a0"},
|
203
|
+
{file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ed2440ada7ef7d0d608f20ad89a04ec47d2d3ab7190896cd62ca5fc4fe08bf0"},
|
204
|
+
{file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd9a97c8e94059b0ef54a7d4baf13b405011176c3b6ff257c247cae0d560ecd"},
|
205
|
+
{file = "psutil-6.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e8d0054fc88153ca0544f5c4d554d42e33df2e009c4ff42284ac9ebdef4132"},
|
206
|
+
{file = "psutil-6.0.0-cp37-abi3-win32.whl", hash = "sha256:a495580d6bae27291324fe60cea0b5a7c23fa36a7cd35035a16d93bdcf076b9d"},
|
207
|
+
{file = "psutil-6.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:33ea5e1c975250a720b3a6609c490db40dae5d83a4eb315170c4fe0d8b1f34b3"},
|
208
|
+
{file = "psutil-6.0.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:ffe7fc9b6b36beadc8c322f84e1caff51e8703b88eee1da46d1e3a6ae11b4fd0"},
|
209
|
+
{file = "psutil-6.0.0.tar.gz", hash = "sha256:8faae4f310b6d969fa26ca0545338b21f73c6b15db7c4a8d934a5482faa818f2"},
|
210
|
+
]
|
211
|
+
|
212
|
+
[[package]]
|
213
|
+
name = "ptyprocess"
|
214
|
+
version = "0.7.0"
|
215
|
+
summary = "Run a subprocess in a pseudo terminal"
|
216
|
+
groups = ["dev"]
|
217
|
+
marker = "sys_platform != \"win32\" and sys_platform != \"emscripten\""
|
218
|
+
files = [
|
219
|
+
{file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
|
220
|
+
{file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
|
221
|
+
]
|
222
|
+
|
223
|
+
[[package]]
|
224
|
+
name = "pure-eval"
|
225
|
+
version = "0.2.3"
|
226
|
+
summary = "Safely evaluate AST nodes without side effects"
|
227
|
+
groups = ["dev"]
|
228
|
+
files = [
|
229
|
+
{file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"},
|
230
|
+
{file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"},
|
231
|
+
]
|
232
|
+
|
233
|
+
[[package]]
|
234
|
+
name = "pygments"
|
235
|
+
version = "2.18.0"
|
236
|
+
requires_python = ">=3.8"
|
237
|
+
summary = "Pygments is a syntax highlighting package written in Python."
|
238
|
+
groups = ["dev"]
|
239
|
+
files = [
|
240
|
+
{file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"},
|
241
|
+
{file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"},
|
242
|
+
]
|
243
|
+
|
244
|
+
[[package]]
|
245
|
+
name = "python-dateutil"
|
246
|
+
version = "2.9.0.post0"
|
247
|
+
requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
|
248
|
+
summary = "Extensions to the standard Python datetime module"
|
249
|
+
groups = ["default"]
|
250
|
+
dependencies = [
|
251
|
+
"six>=1.5",
|
252
|
+
]
|
253
|
+
files = [
|
254
|
+
{file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
|
255
|
+
{file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
|
256
|
+
]
|
257
|
+
|
258
|
+
[[package]]
|
259
|
+
name = "pytz"
|
260
|
+
version = "2024.1"
|
261
|
+
summary = "World timezone definitions, modern and historical"
|
262
|
+
groups = ["default"]
|
263
|
+
files = [
|
264
|
+
{file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"},
|
265
|
+
{file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"},
|
266
|
+
]
|
267
|
+
|
268
|
+
[[package]]
|
269
|
+
name = "six"
|
270
|
+
version = "1.16.0"
|
271
|
+
requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
272
|
+
summary = "Python 2 and 3 compatibility utilities"
|
273
|
+
groups = ["default", "dev"]
|
274
|
+
files = [
|
275
|
+
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
276
|
+
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
277
|
+
]
|
278
|
+
|
279
|
+
[[package]]
|
280
|
+
name = "stack-data"
|
281
|
+
version = "0.6.3"
|
282
|
+
summary = "Extract data from python stack frames and tracebacks for informative displays"
|
283
|
+
groups = ["dev"]
|
284
|
+
dependencies = [
|
285
|
+
"asttokens>=2.1.0",
|
286
|
+
"executing>=1.2.0",
|
287
|
+
"pure-eval",
|
288
|
+
]
|
289
|
+
files = [
|
290
|
+
{file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"},
|
291
|
+
{file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"},
|
292
|
+
]
|
293
|
+
|
294
|
+
[[package]]
|
295
|
+
name = "traitlets"
|
296
|
+
version = "5.14.3"
|
297
|
+
requires_python = ">=3.8"
|
298
|
+
summary = "Traitlets Python configuration system"
|
299
|
+
groups = ["dev"]
|
300
|
+
files = [
|
301
|
+
{file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"},
|
302
|
+
{file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"},
|
303
|
+
]
|
304
|
+
|
305
|
+
[[package]]
|
306
|
+
name = "tzdata"
|
307
|
+
version = "2024.1"
|
308
|
+
requires_python = ">=2"
|
309
|
+
summary = "Provider of IANA time zone data"
|
310
|
+
groups = ["default"]
|
311
|
+
files = [
|
312
|
+
{file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"},
|
313
|
+
{file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"},
|
314
|
+
]
|
315
|
+
|
316
|
+
[[package]]
|
317
|
+
name = "wcwidth"
|
318
|
+
version = "0.2.13"
|
319
|
+
summary = "Measures the displayed width of unicode strings in a terminal"
|
320
|
+
groups = ["dev"]
|
321
|
+
dependencies = [
|
322
|
+
"backports-functools-lru-cache>=1.2.1; python_version < \"3.2\"",
|
323
|
+
]
|
324
|
+
files = [
|
325
|
+
{file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"},
|
326
|
+
{file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"},
|
327
|
+
]
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/pycall_thread/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "pycall_thread"
|
7
|
+
spec.version = PycallThread::VERSION
|
8
|
+
spec.authors = ["Seth Nickell"]
|
9
|
+
spec.email = ["snickell@gmail.com"]
|
10
|
+
|
11
|
+
spec.summary = "Use PyCall in a thread-safe way from Rails or Puma"
|
12
|
+
spec.description = "PyCall is not thread-safe, but this gem provides a way to run all PyCall code in the same thread."
|
13
|
+
spec.homepage = "https://github.com/snickell/pycall_thread"
|
14
|
+
spec.required_ruby_version = ">= 2.6.0"
|
15
|
+
|
16
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
17
|
+
|
18
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
19
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
20
|
+
|
21
|
+
spec.license = "MIT"
|
22
|
+
|
23
|
+
spec.add_dependency "pycall"
|
24
|
+
|
25
|
+
# Specify which files should be added to the gem when it is released.
|
26
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
27
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
28
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
29
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
30
|
+
end
|
31
|
+
end
|
32
|
+
spec.bindir = "exe"
|
33
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
34
|
+
spec.require_paths = ["lib"]
|
35
|
+
|
36
|
+
# Uncomment to register a new dependency of your gem
|
37
|
+
# spec.add_dependency "example-gem", "~> 1.0"
|
38
|
+
|
39
|
+
# For more information and examples about making a new gem, checkout our
|
40
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
41
|
+
end
|
data/pyproject.toml
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
[project]
|
2
|
+
requires-python = "==3.12.*"
|
3
|
+
|
4
|
+
dependencies = [
|
5
|
+
"pandas",
|
6
|
+
"psutil>=6.0.0",
|
7
|
+
]
|
8
|
+
|
9
|
+
[tool.pdm]
|
10
|
+
distribution = false
|
11
|
+
|
12
|
+
[tool.pdm.dev-dependencies]
|
13
|
+
dev = [
|
14
|
+
"ipython>=8.26.0",
|
15
|
+
]
|
16
|
+
[build-system]
|
17
|
+
requires = ["pdm-backend"]
|
18
|
+
build-backend = "pdm.backend"
|
19
|
+
|
metadata
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pycall_thread
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Seth Nickell
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-08-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: pycall
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: PyCall is not thread-safe, but this gem provides a way to run all PyCall
|
28
|
+
code in the same thread.
|
29
|
+
email:
|
30
|
+
- snickell@gmail.com
|
31
|
+
executables: []
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- ".pdm-python"
|
36
|
+
- ".rubocop.yml"
|
37
|
+
- ".ruby-version"
|
38
|
+
- Gemfile
|
39
|
+
- Gemfile.lock
|
40
|
+
- README.md
|
41
|
+
- Rakefile
|
42
|
+
- bin/console
|
43
|
+
- bin/setup
|
44
|
+
- examples/puma/app.rb
|
45
|
+
- examples/puma/config.ru
|
46
|
+
- examples/puma/start_puma.sh
|
47
|
+
- lib/pycall_thread.rb
|
48
|
+
- lib/pycall_thread/version.rb
|
49
|
+
- pdm.lock
|
50
|
+
- pycall_thread.gemspec
|
51
|
+
- pyproject.toml
|
52
|
+
- sig/pycall_thread.rbs
|
53
|
+
homepage: https://github.com/snickell/pycall_thread
|
54
|
+
licenses:
|
55
|
+
- MIT
|
56
|
+
metadata:
|
57
|
+
allowed_push_host: https://rubygems.org
|
58
|
+
homepage_uri: https://github.com/snickell/pycall_thread
|
59
|
+
source_code_uri: https://github.com/snickell/pycall_thread
|
60
|
+
post_install_message:
|
61
|
+
rdoc_options: []
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 2.6.0
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
requirements: []
|
75
|
+
rubygems_version: 3.2.33
|
76
|
+
signing_key:
|
77
|
+
specification_version: 4
|
78
|
+
summary: Use PyCall in a thread-safe way from Rails or Puma
|
79
|
+
test_files: []
|