pycall_thread 0.1.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 +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: []
|