pycall 1.5.2 → 1.5.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c0793d5cd5f93c4eb9cd5938b7999799649db72f2abd35b4ae3247c450aec7ce
4
- data.tar.gz: 404e3c06167a752f94cfc9051734a259725366f7c981c654e4f8744a3613c907
3
+ metadata.gz: cbe8667d161e27770570029b6e2da005db45d5f2e61482ba7ffa96117fa82a61
4
+ data.tar.gz: 530c510c0accfc3b1af1daf69ad530855ecf943ffaf6dd9d47975bf7f89132a7
5
5
  SHA512:
6
- metadata.gz: 2c8f666bb616b01f69bcd33130991cd22f9f2bfea3b67946e87550c846327032bd14d69d2522d8b83ee18db5551cea3bf0fa51f7043d7cfcfd8a8490c6bf04df
7
- data.tar.gz: 041600f566b4d8158dc4772830ab1a8eb00479a82b118f0f135e20130d05d9047dba53b8ab32701c2bee20ec770a48cd540cd259d369163e64f4da775e7e26ae
6
+ metadata.gz: 179af9a8a76e9f4528990468e8eb776c9031390cea2f28219c3da8aaf5dbc017df1cf2dfdf254b549a9313800ec640f82d4ca5302d6b74d3b192cc79daaf896b
7
+ data.tar.gz: 71a248181b886b513a650aef95a583563d5bb34e125e8f0bc1dc838e47c8ab6d38480bfe77d017065c73c47177dccab6d5a27b28d666b401e325fb118e95ea30
@@ -1,11 +1,10 @@
1
- # To get started with Dependabot version updates, you'll need to specify which
2
- # package ecosystems to update and where the package manifests are located.
3
- # Please see the documentation for all configuration options:
4
- # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5
-
6
1
  version: 2
7
2
  updates:
8
- - package-ecosystem: "bundler" # See documentation for possible values
9
- directory: "/" # Location of package manifests
3
+ - package-ecosystem: "github-actions"
4
+ directory: "/"
10
5
  schedule:
11
- interval: "daily"
6
+ interval: "weekly"
7
+ - package-ecosystem: "bundler"
8
+ directory: "/"
9
+ schedule:
10
+ interval: "weekly"
@@ -3,14 +3,9 @@ name: CI
3
3
  on:
4
4
  push:
5
5
  branches:
6
- - master
7
- - "check/ci/**"
8
- - "check/unix/**"
6
+ - "**"
7
+ - '!dependabot/**'
9
8
  pull_request:
10
- types:
11
- - opened
12
- - synchronize
13
- - reopened
14
9
 
15
10
  jobs:
16
11
  test:
@@ -22,9 +17,10 @@ jobs:
22
17
  matrix:
23
18
  os:
24
19
  - ubuntu-latest
25
- - ubuntu-20.04
20
+ - ubuntu-22.04
26
21
  - macos-latest
27
22
  ruby:
23
+ - "3.4"
28
24
  - "3.3"
29
25
  - "3.2"
30
26
  - "3.1"
@@ -37,25 +33,27 @@ jobs:
37
33
  venv:
38
34
  - ""
39
35
  include:
36
+ # Python 3.11 with the latest Ruby
37
+ - { os: ubuntu-22.04 , ruby: 3.3 , python: "3.11" , python_architecture: x64 , venv: "" }
38
+
40
39
  # Old ruby and the latest Python 3
41
- - { os: ubuntu-20.04 , ruby: 2.6 , python: "3.x" , python_architecture: x64 , venv: "" }
42
- - { os: ubuntu-20.04 , ruby: 2.5 , python: "3.x" , python_architecture: x64 , venv: "" }
43
- - { os: ubuntu-20.04 , ruby: 2.4 , python: "3.x" , python_architecture: x64 , venv: "" }
40
+ - { os: ubuntu-22.04 , ruby: 2.6 , python: "3.x" , python_architecture: x64 , venv: "" }
41
+ - { os: ubuntu-22.04 , ruby: 2.5 , python: "3.x" , python_architecture: x64 , venv: "" }
42
+ - { os: ubuntu-22.04 , ruby: 2.4 , python: "3.x" , python_architecture: x64 , venv: "" }
44
43
 
45
44
  # Ruby 2.7 with Each Python 3.x
46
- - { os: ubuntu-20.04 , ruby: 2.7 , python: "3.12" , python_architecture: x64 , venv: "" }
47
- - { os: ubuntu-20.04 , ruby: 2.7 , python: "3.11" , python_architecture: x64 , venv: "" }
48
- - { os: ubuntu-20.04 , ruby: 2.7 , python: "3.10" , python_architecture: x64 , venv: "" }
49
- - { os: ubuntu-20.04 , ruby: 2.7 , python: "3.9" , python_architecture: x64 , venv: "" }
50
- - { os: ubuntu-20.04 , ruby: 2.7 , python: "3.8" , python_architecture: x64 , venv: "" }
51
- - { os: ubuntu-20.04 , ruby: 2.7 , python: "3.7" , python_architecture: x64 , venv: "" }
52
- - { os: ubuntu-20.04 , ruby: 2.7 , python: "3.6" , python_architecture: x64 , venv: "" }
45
+ - { os: ubuntu-22.04 , ruby: 2.7 , python: "3.12" , python_architecture: x64 , venv: "" }
46
+ - { os: ubuntu-22.04 , ruby: 2.7 , python: "3.11" , python_architecture: x64 , venv: "" }
47
+ - { os: ubuntu-22.04 , ruby: 2.7 , python: "3.10" , python_architecture: x64 , venv: "" }
48
+ - { os: ubuntu-22.04 , ruby: 2.7 , python: "3.9" , python_architecture: x64 , venv: "" }
49
+ - { os: ubuntu-22.04 , ruby: 2.7 , python: "3.8" , python_architecture: x64 , venv: "" }
50
+ - { os: ubuntu-22.04 , ruby: 2.7 , python: "3.7" , python_architecture: x64 , venv: "" }
53
51
 
54
52
  # Ruby-debug with the latest Python 3
55
- - { os: ubuntu-20.04 , ruby: debug , python: "3.x" , python_architecture: x64 , venv: "" }
53
+ - { os: ubuntu-22.04 , ruby: debug , python: "3.x" , python_architecture: x64 , venv: "" }
56
54
 
57
55
  # The latest stable Ruby with the latest Python 3 with venv
58
- - { os: ubuntu-20.04 , ruby: "3.3" , python: "3.x" , python_architecture: x64 , venv: "venv:" }
56
+ - { os: ubuntu-22.04 , ruby: "3.3" , python: "3.x" , python_architecture: x64 , venv: "venv:" }
59
57
 
60
58
  # macOS with venv
61
59
  - { os: macos-latest , ruby: "3.3" , python: "3.x" , python_architecture: x64 , venv: "venv:" }
@@ -63,7 +61,7 @@ jobs:
63
61
  #- { os: macos-latest , ruby: debug , python: "3.x" , python_architecture: x64 , venv: "" }
64
62
 
65
63
  steps:
66
- - uses: actions/checkout@v3
64
+ - uses: actions/checkout@v7
67
65
  with:
68
66
  fetch-depth: 1
69
67
 
@@ -72,7 +70,7 @@ jobs:
72
70
  with:
73
71
  ruby-version: ${{ matrix.ruby }}
74
72
 
75
- - uses: actions/setup-python@v4
73
+ - uses: actions/setup-python@v7
76
74
  with:
77
75
  python-version: ${{ matrix.python }}
78
76
  architecture: ${{ matrix.python_architecture }}
@@ -106,7 +104,7 @@ jobs:
106
104
  fail-fast: false
107
105
  matrix:
108
106
  os:
109
- - ubuntu-20.04
107
+ - ubuntu-22.04
110
108
  #- macos-latest
111
109
  ruby:
112
110
  - "3.1"
@@ -114,11 +112,11 @@ jobs:
114
112
  - "3.10"
115
113
 
116
114
  steps:
117
- - uses: actions/checkout@v2
115
+ - uses: actions/checkout@v7
118
116
  with:
119
117
  fetch-depth: 1
120
118
 
121
- - uses: conda-incubator/setup-miniconda@v2
119
+ - uses: conda-incubator/setup-miniconda@v4
122
120
  with:
123
121
  activate-environment: test
124
122
  python-version: ${{ matrix.python }}
@@ -0,0 +1,48 @@
1
+ name: Release
2
+ on:
3
+ push:
4
+ tags:
5
+ - "*"
6
+ jobs:
7
+ github:
8
+ name: GitHub
9
+ runs-on: ubuntu-latest
10
+ timeout-minutes: 10
11
+ permissions:
12
+ contents: write
13
+ discussions: write
14
+ steps:
15
+ - uses: actions/checkout@v7
16
+ - name: Extract release note
17
+ run: |
18
+ ruby \
19
+ -e 'print("## PyCall "); \
20
+ puts(ARGF.read.split(/^## /)[1])' \
21
+ CHANGES.md > release-note.md
22
+ - name: Upload to release
23
+ env:
24
+ GH_TOKEN: ${{ github.token }}
25
+ run: |
26
+ title=$(head -n1 release-note.md | sed -e 's/^## //')
27
+ tail -n +2 release-note.md > release-note-without-version.md
28
+ gh release create ${GITHUB_REF_NAME} \
29
+ --discussion-category Announcements \
30
+ --notes-file release-note-without-version.md \
31
+ --title "${title}"
32
+ rubygems:
33
+ name: RubyGems
34
+ runs-on: ubuntu-latest
35
+ timeout-minutes: 10
36
+ permissions:
37
+ id-token: write
38
+ environment: release
39
+ steps:
40
+ - uses: actions/checkout@v7
41
+ - uses: ruby/setup-ruby@v1
42
+ with:
43
+ ruby-version: ruby
44
+ bundler-cache: true
45
+ - uses: rubygems/configure-rubygems-credentials@v2.1.0
46
+ - name: Push gems
47
+ run: |
48
+ bundle exec rake release:rubygem_push
@@ -3,14 +3,9 @@ name: Windows
3
3
  on:
4
4
  push:
5
5
  branches:
6
- - master
7
- - "check/ci/**"
8
- - "check/windows/**"
6
+ - "**"
7
+ - '!dependabot/**'
9
8
  pull_request:
10
- types:
11
- - opened
12
- - synchronize
13
- - reopened
14
9
 
15
10
  jobs:
16
11
  test:
@@ -21,19 +16,24 @@ jobs:
21
16
  fail-fast: false
22
17
  matrix:
23
18
  ruby:
19
+ - "3.3"
20
+ - "3.2"
21
+ - "3.1"
24
22
  - "3.0"
25
- - 2.7
26
- - 2.6
23
+ - "2.7"
27
24
  python:
28
25
  - 3.x
29
26
  python_architecture:
30
27
  - x64
31
28
  include:
32
- - { os: windows-latest , ruby: mingw , python: 3.x , python_architecture: x64 }
33
- #- { os: windows-latest , ruby: mswin , python: 3.x , python_architecture: x64 }
29
+ # Python 3.11 with the latest Ruby
30
+ - { ruby: "3.3" , python: 3.11 , python_architecture: x64 }
31
+ # The development version of Ruby
32
+ - { ruby: mingw , python: 3.x , python_architecture: x64 }
33
+ #- { ruby: mswin , python: 3.x , python_architecture: x64 }
34
34
 
35
35
  steps:
36
- - uses: actions/checkout@v3
36
+ - uses: actions/checkout@v7
37
37
  with:
38
38
  fetch-depth: 1
39
39
 
@@ -42,7 +42,7 @@ jobs:
42
42
  with:
43
43
  ruby-version: ${{ matrix.ruby }}
44
44
 
45
- - uses: actions/setup-python@v4
45
+ - uses: actions/setup-python@v7
46
46
  with:
47
47
  python-version: ${{ matrix.python }}
48
48
  architecture: ${{ matrix.python_architecture }}
@@ -67,20 +67,21 @@ jobs:
67
67
  fail-fast: false
68
68
  matrix:
69
69
  ruby:
70
- - "3.0"
70
+ - "3.3"
71
71
  python:
72
- - 3.8
72
+ - 3.12
73
+ - 3.11
73
74
 
74
75
  defaults:
75
76
  run:
76
77
  shell: pwsh
77
78
 
78
79
  steps:
79
- - uses: actions/checkout@v2
80
+ - uses: actions/checkout@v7
80
81
  with:
81
82
  fetch-depth: 1
82
83
 
83
- - uses: conda-incubator/setup-miniconda@v2
84
+ - uses: conda-incubator/setup-miniconda@v4
84
85
  with:
85
86
  activate-environment: test
86
87
  python-version: ${{ matrix.python }}
data/CHANGES.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # The change history of PyCall
2
2
 
3
+ ## 1.5.3
4
+
5
+ * Add support for Ruby 4.1 (#225).
6
+
7
+ *andreyzhelnin-st*
8
+
9
+ * Add support for multi-threading (#223, #224).
10
+
11
+ *andreyzhelnin-st*
12
+
3
13
  ## 1.5.2
4
14
 
5
15
  * Fix memory leak with str(list) (#169).
data/README.md CHANGED
@@ -5,8 +5,7 @@
5
5
 
6
6
  # PyCall: Calling Python functions from the Ruby language
7
7
 
8
- [![Build Status](https://github.com/mrkn/pycall.rb/workflows/CI/badge.svg)](https://github.com/mrkn/pycall.rb/actions?query=workflow%3ACI)
9
- [![Build status](https://ci.appveyor.com/api/projects/status/0fad23u4qj1yr49e/branch/master?svg=true)](https://ci.appveyor.com/project/mrkn/pycall-rb/branch/master)
8
+ [![Build Status](https://github.com/red-data-tools/pycall.rb/workflows/CI/badge.svg)](https://github.com/red-data-tools/pycall.rb/actions?query=workflow%3ACI)
10
9
 
11
10
  This library provides the features to directly call and partially interoperate
12
11
  with Python from the Ruby language. You can import arbitrary Python modules
@@ -21,6 +20,10 @@ pycall.rb supports Ruby version 2.4 or higher.
21
20
 
22
21
  pycall.rb supports Python version 3.7 or higher.
23
22
 
23
+ ## PyCall does not support multi-threaded use officially
24
+
25
+ CPython's C-API has GIL acquiring/releasing functions such as `PyGILState_Ensure` and `PyGILState_Release`. Programmers can call CPython's C-APIs from outside of Python threads if they manage GIL's state by these functions. However, we do not want to officially support the multi-threaded use of PyCall because creating the feature enabling stable multi-threaded use in any situation is too difficult. We want to avoid incurring the costs to support such use cases.
26
+
24
27
  ## Note for pyenv users
25
28
 
26
29
  pycall.rb requires Python's shared library (e.g. `libpython3.7m.so`).
@@ -128,15 +131,15 @@ DEBUG(find_libpython) dlopen("/opt/brew/opt/python/Frameworks/Python.framework/V
128
131
 
129
132
  ### matplotlib
130
133
 
131
- Use [mrkn/matplotlib.rb](https://github.com/mrkn/matplotlib.rb) instead of just importing it by `PyCall.import_module("matplotlib")`.
134
+ Use [red-data-tools/matplotlib.rb](https://github.com/red-data-tools/matplotlib.rb) instead of just importing it by `PyCall.import_module("matplotlib")`.
132
135
 
133
136
  ### numpy
134
137
 
135
- Use [mrkn/numpy.rb](https://github.com/mrkn/numpy.rb) instead of just importing it by `PyCall.import_module("numpy")`.
138
+ Use [red-data-tools/numpy.rb](https://github.com/red-data-tools/numpy.rb) instead of just importing it by `PyCall.import_module("numpy")`.
136
139
 
137
140
  ### pandas
138
141
 
139
- Use [mrkn/pandas.rb](https://github.com/mrkn/pandas.rb) instead of just importing it by `PyCall.import_module("pandas")`.
142
+ Use [red-data-tools/pandas.rb](https://github.com/red-data-tools/pandas.rb) instead of just importing it by `PyCall.import_module("pandas")`.
140
143
 
141
144
  ## PyCall object system
142
145
 
@@ -159,41 +162,16 @@ translates Ruby's coerce system into Python's swapped operation protocol.
159
162
 
160
163
  ## Deploying on Heroku
161
164
 
162
- Heroku's default version of Python is not compiled with the `--enabled-shared`
163
- option and can't be accessed by PyCall.
164
-
165
- There are many ways to make our heroku use Python that is compiled with the `--enabled-shared` option:
166
-
167
- - use Heroku's official Python buildpacks `post_compile` hooks to recompile the python if the `--enabled-shared` option is not enabled.
168
- example script of `post_compile` in ruby on rails app `bin/post_compile`.
169
-
170
- set -e
171
- buildpack_url=https://github.com/heroku/heroku-buildpack-python
172
- buildpack_vsn=v197 # adjust version accordingly https://github.com/heroku/heroku-buildpack-python/tags
165
+ Heroku's default builds of Python are now compiled with the `--enabled-shared` option
166
+ (when using Python 3.10 and newer) and so work out of the box with PyCall without
167
+ the need for a custom buildpack.
173
168
 
174
- # rebuild python if it's missing enable-shared
175
- if ! python3 -msysconfig | grep enable-shared \
176
- > /dev/null; then
177
- PYTHON_VERSION="$(< runtime.txt)"
178
- git clone -b "$buildpack_vsn" "$buildpack_url" _buildpack
179
- export WORKSPACE_DIR="$PWD/_buildpack/builds"
180
- rm -f .heroku/python/bin/python # prevent failing ln after build
181
- sed -i 's!figure --pre!figure --enable-shared --pre!' \
182
- "$WORKSPACE_DIR"/runtimes/python3
183
- "$WORKSPACE_DIR/runtimes/$PYTHON_VERSION" /app/.heroku/python/
184
- rm -fr _buildpack
185
- fi
186
-
187
- - use your own precompiled python with `--enabled-shared` options then fork the official heroku [python buildspacks](https://github.com/heroku/heroku-buildpack-python) and change the `BUILDPACK_S3_BASE_URL` with your own uploaded precompiled python in Amazon's S3.
188
- - use 3rd party buildpacks from the [markets](https://elements.heroku.com/buildpacks) that have python with `--enabled-shared` option.
189
-
190
-
191
- The buildpack will expect to find both a `runtime.txt` and a `requirements.txt`
169
+ The Python buildpack will expect to find both a `.python-version` and a `requirements.txt`
192
170
  file in the root of your project. You will need to add these to specify the
193
171
  version of Python and any packages to be installed via `pip`, _e.g_ to use
194
- version Python 3.8.1 and version 2.5 of the 'networkx' package:
172
+ the latest patch version Python 3.12 and version 2.5 of the 'networkx' package:
195
173
 
196
- $ echo "python-3.8.1" >> runtime.txt
174
+ $ echo "3.12" > .python-version
197
175
  $ echo "networkx==2.5" >> requirements.txt
198
176
 
199
177
  Commit these two files into project's repository. You'll use these to manage
@@ -212,12 +190,11 @@ For a Ruby/Rails application this will typically report the stock `heroku/ruby`
212
190
  buildpack, or possibly both `heroku/ruby` and `heroku/nodejs`.
213
191
 
214
192
  Clear the list and progressively add back your buildpacks, starting with the Python
215
- community-developed buildpack. For example, if `ruby` and `nodejs` buildpacks were
216
- previously installed, and chosing the 'ReforgeHQ' buildback, your setup process will
217
- be similar to this:
193
+ buildpack. For example, if `ruby` and `nodejs` buildpacks were previously installed,
194
+ your setup process will be similar to this:
218
195
 
219
196
  $ heroku buildpacks:clear
220
- $ heroku buildpacks:add https://github.com/ReforgeHQ/heroku-buildpack-python -i 1
197
+ $ heroku buildpacks:add heroku/python -i 1
221
198
  $ heroku buildpacks:add heroku/nodejs -i 2
222
199
  # heroku buildpacks:add heroku/ruby -i 3
223
200
 
@@ -229,15 +206,12 @@ specify the order Heroku will load runtimes and execute bootstrapping code. It's
229
206
  important for the Python environment to be engaged first, as PyCall will need to
230
207
  be able to find it when Ruby-based processes start.
231
208
 
232
- Once you have set up your buildpacks, and have commited both `requirements.txt` and
233
- `runtime.txt` files to git, deploy your Heroku application as your normally would.
209
+ Once you have set up your buildpacks, and have committed both `requirements.txt` and
210
+ `.python-version` files to git, deploy your Heroku application as your normally would.
234
211
  The Python bootstrapping process will appear in the log first, followed by the Ruby
235
212
  and so on. PyCall should now be able to successfully call Python functions from
236
213
  within the Heroku environment.
237
214
 
238
- NB It is also possible to specify buildpacks within Docker images on Heroku.
239
- See Heroku's [documentation on using Docker Images](https://devcenter.heroku.com/articles/build-docker-images-heroku-yml).
240
-
241
215
  ## Development
242
216
 
243
217
  After checking out the repo, run `bin/setup` to install dependencies.
@@ -253,7 +227,7 @@ version, push git commits and tags, and push the `.gem` file to
253
227
  ## Contributing
254
228
 
255
229
  Bug reports and pull requests are welcome on GitHub at
256
- https://github.com/mrkn/pycall.rb.
230
+ https://github.com/red-data-tools/pycall.rb.
257
231
 
258
232
 
259
233
  ## Acknowledgement
data/Rakefile CHANGED
@@ -91,3 +91,14 @@ end
91
91
 
92
92
  task default: :spec
93
93
  task spec: :compile
94
+
95
+
96
+ release_task = Rake.application["release"]
97
+ # We use Trusted Publishing.
98
+ release_task.prerequisites.delete("build")
99
+ release_task.prerequisites.delete("release:rubygem_push")
100
+ release_task_comment = release_task.comment
101
+ if release_task_comment
102
+ release_task.clear_comments
103
+ release_task.comment = release_task_comment.gsub(/ and build.*$/, "")
104
+ end
data/docker/Dockerfile CHANGED
@@ -183,7 +183,7 @@ RUN bundle exec iruby register
183
183
 
184
184
  # Deploy matplotlib's examples
185
185
  RUN mkdir -p /tmp \
186
- && curl -fsSL https://github.com/mrkn/matplotlib.rb/archive/master.tar.gz | tar -xzf - -C /tmp \
186
+ && curl -fsSL https://github.com/red-data-tools/matplotlib.rb/archive/master.tar.gz | tar -xzf - -C /tmp \
187
187
  && mv /tmp/matplotlib.rb-master/examples /notebooks/examples/matplotlib \
188
188
  && rm -rf /tmp/matplotlib.rb-master
189
189
 
data/ext/pycall/pycall.c CHANGED
@@ -29,7 +29,6 @@ static int python_major_version;
29
29
  static int python_has_stackless_extension;
30
30
  static PyObject *python_builtins_module;
31
31
  static VALUE python_type_mapping;
32
- static VALUE python_type_mapping;
33
32
  static ID id_python_type_mapping;
34
33
 
35
34
  int
@@ -122,6 +121,58 @@ pycall_m_without_gvl(VALUE mod)
122
121
 
123
122
  /* ==== PyCall::PyPtr ==== */
124
123
 
124
+ struct pending_decref_list {
125
+ rb_nativethread_lock_t lock;
126
+ PyObject **pyptrs;
127
+ size_t count;
128
+ size_t capacity;
129
+ };
130
+
131
+ static struct pending_decref_list pending_decrefs;
132
+
133
+ static void
134
+ pycall_pending_decref_add(PyObject *pyobj)
135
+ {
136
+ rb_nativethread_lock_lock(&pending_decrefs.lock);
137
+ if (pending_decrefs.count == pending_decrefs.capacity) {
138
+ size_t new_capacity = pending_decrefs.capacity ? 2 * pending_decrefs.capacity : 64;
139
+ /* Not ruby_xrealloc: this function runs during GC, where xmalloc/xrealloc
140
+ * are prohibited (rb_bug "Cannot realloc during GC" since Ruby 3.3). */
141
+ PyObject **new_pyptrs = realloc(pending_decrefs.pyptrs, new_capacity * sizeof(PyObject *));
142
+ if (new_pyptrs == NULL) {
143
+ /* Leaking the object is preferable to blocking or crashing inside GC */
144
+ rb_nativethread_lock_unlock(&pending_decrefs.lock);
145
+ return;
146
+ }
147
+ pending_decrefs.pyptrs = new_pyptrs;
148
+ pending_decrefs.capacity = new_capacity;
149
+ }
150
+ pending_decrefs.pyptrs[pending_decrefs.count++] = pyobj;
151
+ rb_nativethread_lock_unlock(&pending_decrefs.lock);
152
+ }
153
+
154
+ static void
155
+ pycall_drain_pending_decrefs(void)
156
+ {
157
+ PyObject **pyptrs;
158
+ size_t i, count;
159
+
160
+ if (pending_decrefs.count == 0) return;
161
+
162
+ rb_nativethread_lock_lock(&pending_decrefs.lock);
163
+ pyptrs = pending_decrefs.pyptrs;
164
+ count = pending_decrefs.count;
165
+ pending_decrefs.pyptrs = NULL;
166
+ pending_decrefs.count = 0;
167
+ pending_decrefs.capacity = 0;
168
+ rb_nativethread_lock_unlock(&pending_decrefs.lock);
169
+
170
+ for (i = 0; i < count; ++i) {
171
+ pycall_Py_DecRef(pyptrs[i]);
172
+ }
173
+ free(pyptrs);
174
+ }
175
+
125
176
  const rb_data_type_t pycall_pyptr_data_type = {
126
177
  "PyCall::PyPtr",
127
178
  { 0, pycall_pyptr_free, pycall_pyptr_memsize, },
@@ -130,6 +181,11 @@ const rb_data_type_t pycall_pyptr_data_type = {
130
181
  #endif
131
182
  };
132
183
 
184
+ /* pycall_pyptr_free may be called by the GC sweeper on a thread that does
185
+ * not hold the GIL. PyCall keeps the GIL held by the thread that
186
+ * initialized Python for the entire lifetime of the process, so waiting
187
+ * for the GIL there (PyGILState_Ensure) blocks forever while the sweeper
188
+ * holds the GVL, freezing every Ruby thread in the process. */
133
189
  void
134
190
  pycall_pyptr_free(void *ptr)
135
191
  {
@@ -144,9 +200,9 @@ pycall_pyptr_free(void *ptr)
144
200
  pycall_Py_DecRef(pyobj);
145
201
  }
146
202
  else {
147
- PyGILState_STATE gstate = Py_API(PyGILState_Ensure)();
148
- pycall_Py_DecRef(pyobj);
149
- Py_API(PyGILState_Release)(gstate);
203
+ /* Instead of taking the GIL here, enqueue the pointer; the list is
204
+ * drained on the next Python call. */
205
+ pycall_pending_decref_add(pyobj);
150
206
  }
151
207
  }
152
208
 
@@ -628,6 +684,8 @@ pycall_libpython_api_PyObject_Dir(VALUE mod, VALUE pyptr)
628
684
  PyObject *dir;
629
685
  PyObject *pyobj;
630
686
 
687
+ pycall_drain_pending_decrefs();
688
+
631
689
  if (!is_pycall_pyptr(pyptr)) {
632
690
  rb_raise(rb_eTypeError, "PyCall::PyPtr is required");
633
691
  }
@@ -647,6 +705,8 @@ pycall_libpython_api_PyList_Size(VALUE mod, VALUE pyptr)
647
705
  PyObject *pyobj;
648
706
  Py_ssize_t size;
649
707
 
708
+ pycall_drain_pending_decrefs();
709
+
650
710
  if (!is_pycall_pyptr(pyptr)) {
651
711
  rb_raise(rb_eTypeError, "PyCall::PyPtr is required");
652
712
  }
@@ -667,6 +727,8 @@ pycall_libpython_api_PyList_GetItem(VALUE mod, VALUE pyptr, VALUE idx)
667
727
  PyObject *pyobj_item;
668
728
  Py_ssize_t i;
669
729
 
730
+ pycall_drain_pending_decrefs();
731
+
670
732
  if (!is_pycall_pyptr(pyptr)) {
671
733
  rb_raise(rb_eTypeError, "PyCall::PyPtr is required");
672
734
  }
@@ -732,6 +794,8 @@ pycall_libpython_helpers_m_import_module(int argc, VALUE *argv, VALUE mod)
732
794
  VALUE name, globals, locals, fromlist, level;
733
795
  char const *name_cstr;
734
796
 
797
+ pycall_drain_pending_decrefs();
798
+
735
799
  rb_scan_args(argc, argv, "14", &name, &globals, &locals, &fromlist, &level);
736
800
 
737
801
  if (RB_TYPE_P(name, T_SYMBOL)) {
@@ -784,6 +848,8 @@ pycall_libpython_helpers_m_compare(VALUE mod, VALUE op, VALUE pyptr_a, VALUE pyp
784
848
  PyObject *pyobj_a, *pyobj_b, *res;
785
849
  int opid;
786
850
 
851
+ pycall_drain_pending_decrefs();
852
+
787
853
  opid = pycall_rich_compare_opid(op);
788
854
 
789
855
  if (!is_pycall_pyptr(pyptr_a)) {
@@ -838,6 +904,8 @@ pycall_libpython_helpers_m_getattr(int argc, VALUE *argv, VALUE mod)
838
904
  {
839
905
  VALUE pyptr, name, default_value;
840
906
 
907
+ pycall_drain_pending_decrefs();
908
+
841
909
  if (rb_scan_args(argc, argv, "21", &pyptr, &name, &default_value) == 2) {
842
910
  default_value = Qundef;
843
911
  }
@@ -859,6 +927,8 @@ pycall_libpython_helpers_m_hasattr_p(VALUE mod, VALUE pyptr, VALUE name)
859
927
  PyObject *pyobj;
860
928
  int res;
861
929
 
930
+ pycall_drain_pending_decrefs();
931
+
862
932
  if (!is_pycall_pyptr(pyptr)) {
863
933
  rb_raise(rb_eTypeError, "PyCall::PyPtr is required");
864
934
  }
@@ -878,6 +948,8 @@ pycall_libpython_helpers_m_setattr(VALUE mod, VALUE pyptr, VALUE name, VALUE val
878
948
  {
879
949
  PyObject *pyobj, *pyval;
880
950
 
951
+ pycall_drain_pending_decrefs();
952
+
881
953
  if (!is_pycall_pyptr(pyptr)) {
882
954
  rb_raise(rb_eTypeError, "PyCall::PyPtr is required");
883
955
  }
@@ -901,6 +973,8 @@ pycall_libpython_helpers_m_delattr(VALUE mod, VALUE pyptr, VALUE name)
901
973
  {
902
974
  PyObject *pyobj;
903
975
 
976
+ pycall_drain_pending_decrefs();
977
+
904
978
  if (!is_pycall_pyptr(pyptr)) {
905
979
  rb_raise(rb_eTypeError, "PyCall::PyPtr is required");
906
980
  }
@@ -924,6 +998,8 @@ pycall_libpython_helpers_m_callable_p(VALUE mod, VALUE pyptr)
924
998
  PyObject *pyobj;
925
999
  int res;
926
1000
 
1001
+ pycall_drain_pending_decrefs();
1002
+
927
1003
  if (!is_pycall_pyptr(pyptr)) {
928
1004
  rb_raise(rb_eTypeError, "PyCall::PyPtr is required");
929
1005
  }
@@ -940,6 +1016,8 @@ pycall_libpython_helpers_m_call_object(int argc, VALUE *argv, VALUE mod)
940
1016
  VALUE pyptr;
941
1017
  PyObject *pyobj;
942
1018
 
1019
+ pycall_drain_pending_decrefs();
1020
+
943
1021
  if (argc < 1) {
944
1022
  rb_raise(rb_eArgError, "too few arguments (%d for >=1)", argc);
945
1023
  }
@@ -1089,6 +1167,8 @@ pycall_pyobject_wrapper_wrapper_method(int argc, VALUE *argv, VALUE wrapper)
1089
1167
  char *name_cstr;
1090
1168
  PyObject *pyobj, *attr;
1091
1169
 
1170
+ pycall_drain_pending_decrefs();
1171
+
1092
1172
  pyptr = rb_attr_get(wrapper, rb_intern("@__pyptr__"));
1093
1173
  if (NIL_P(pyptr) || !is_pycall_pyptr(pyptr)) {
1094
1174
  rb_raise(rb_eTypeError, "Wrong wrapper object is given");
@@ -1144,6 +1224,8 @@ pycall_libpython_helpers_m_define_wrapper_method(VALUE mod, VALUE wrapper, VALUE
1144
1224
  PyObject *pyobj, *attr;
1145
1225
  char *name_cstr;
1146
1226
 
1227
+ pycall_drain_pending_decrefs();
1228
+
1147
1229
  pyptr = rb_attr_get(wrapper, rb_intern("@__pyptr__"));
1148
1230
  if (NIL_P(pyptr) || !is_pycall_pyptr(pyptr)) {
1149
1231
  rb_raise(rb_eTypeError, "Wrong wrapper object is given");
@@ -1191,10 +1273,10 @@ pycall_convert_index(VALUE index)
1191
1273
  }
1192
1274
  }
1193
1275
  else if (rb_obj_is_kind_of(index, rb_cRange)) {
1194
- pyobj = pycall_pyslice_from_ruby(index); /* New refrence */
1276
+ pyobj = pycall_pyslice_from_ruby(index); /* New reference */
1195
1277
  }
1196
1278
  else if (pycall_obj_is_step_range(index)) {
1197
- pyobj = pycall_pyslice_from_ruby(index); /* New refrence */
1279
+ pyobj = pycall_pyslice_from_ruby(index); /* New reference */
1198
1280
  }
1199
1281
  else {
1200
1282
  pyobj = pycall_pyobject_from_ruby(index); /* New reference */
@@ -1209,6 +1291,8 @@ pycall_libpython_helpers_m_getitem(VALUE mod, VALUE pyptr, VALUE key)
1209
1291
  PyObject *pyobj, *pyobj_key, *pyobj_v;
1210
1292
  VALUE obj;
1211
1293
 
1294
+ pycall_drain_pending_decrefs();
1295
+
1212
1296
  if (!is_pycall_pyptr(pyptr)) {
1213
1297
  rb_raise(rb_eTypeError, "PyCall::PyPtr is required");
1214
1298
  }
@@ -1234,6 +1318,8 @@ pycall_libpython_helpers_m_setitem(VALUE mod, VALUE pyptr, VALUE key, VALUE v)
1234
1318
  PyObject *pyobj, *pyobj_key, *pyobj_value;
1235
1319
  int res;
1236
1320
 
1321
+ pycall_drain_pending_decrefs();
1322
+
1237
1323
  pyobj = check_get_pyobj_ptr(pyptr, NULL);
1238
1324
  pyobj_key = pycall_convert_index(key);
1239
1325
  pyobj_value = pycall_pyobject_from_ruby(v);
@@ -1254,6 +1340,8 @@ pycall_libpython_helpers_m_delitem(VALUE mod, VALUE pyptr, VALUE key)
1254
1340
  PyObject *pyobj, *pyobj_key;
1255
1341
  int res;
1256
1342
 
1343
+ pycall_drain_pending_decrefs();
1344
+
1257
1345
  pyobj = check_get_pyobj_ptr(pyptr, NULL);
1258
1346
  pyobj_key = pycall_convert_index(key);
1259
1347
 
@@ -1271,6 +1359,8 @@ pycall_libpython_helpers_m_str(VALUE mod, VALUE pyptr)
1271
1359
  {
1272
1360
  PyObject *pyobj, *pyobj_str;
1273
1361
 
1362
+ pycall_drain_pending_decrefs();
1363
+
1274
1364
  pyobj = check_get_pyobj_ptr(pyptr, NULL);
1275
1365
 
1276
1366
  pyobj_str = Py_API(PyObject_Str)(pyobj);
@@ -1289,6 +1379,8 @@ pycall_libpython_helpers_m_dict_contains(VALUE mod, VALUE pyptr, VALUE key)
1289
1379
  PyObject *pyobj, *pyobj_key;
1290
1380
  int res;
1291
1381
 
1382
+ pycall_drain_pending_decrefs();
1383
+
1292
1384
  pyobj = check_get_pyobj_ptr(pyptr, Py_API(PyDict_Type));
1293
1385
  pyobj_key = pycall_pyobject_from_ruby(key);
1294
1386
  res = Py_API(PyDict_Contains)(pyobj, pyobj_key);
@@ -1306,6 +1398,8 @@ pycall_libpython_helpers_m_dict_each(VALUE mod, VALUE pyptr)
1306
1398
  PyObject *pyobj, *pyobj_key, *pyobj_value;
1307
1399
  Py_ssize_t pos;
1308
1400
 
1401
+ pycall_drain_pending_decrefs();
1402
+
1309
1403
  pyobj = check_get_pyobj_ptr(pyptr, Py_API(PyDict_Type));
1310
1404
 
1311
1405
  pos = 0;
@@ -1325,6 +1419,8 @@ pycall_libpython_helpers_m_sequence_contains(VALUE mod, VALUE pyptr, VALUE key)
1325
1419
  PyObject *pyobj, *pyobj_key;
1326
1420
  int res;
1327
1421
 
1422
+ pycall_drain_pending_decrefs();
1423
+
1328
1424
  pyobj = check_get_pyobj_ptr(pyptr, NULL);
1329
1425
  if (!Py_API(PySequence_Check)(pyobj))
1330
1426
  rb_raise(rb_eTypeError, "unexpected Python type %s (expected a Python sequence object)", Py_TYPE(pyobj)->tp_name);
@@ -1344,6 +1440,8 @@ pycall_libpython_helpers_m_sequence_each(VALUE mod, VALUE pyptr)
1344
1440
  {
1345
1441
  PyObject *pyobj, *pyobj_iter, *pyobj_item;
1346
1442
 
1443
+ pycall_drain_pending_decrefs();
1444
+
1347
1445
  pyobj = check_get_pyobj_ptr(pyptr, NULL);
1348
1446
  if (!Py_API(PySequence_Check)(pyobj))
1349
1447
  rb_raise(rb_eTypeError, "unexpected Python type %s (expected a Python sequence object)", Py_TYPE(pyobj)->tp_name);
@@ -1528,7 +1626,7 @@ pycall_pyobject_to_ruby(PyObject *pyobj)
1528
1626
  Py_API(Py_IncRef)((PyObject *)pyobj->ob_type);
1529
1627
  cls = pycall_python_type_mapping_get_mapped_class(pycall_pytypeptr_new((PyObject *)pyobj->ob_type));
1530
1628
  if (NIL_P(cls)) {
1531
- rb_warning("Currentry do not support to convert %s to Ruby object", Py_TYPE(pyobj)->tp_name);
1629
+ rb_warning("Currently do not support to convert %s to Ruby object", Py_TYPE(pyobj)->tp_name);
1532
1630
  return pycall_pyobject_wrapper_object_new(rb_cObject, pyobj);
1533
1631
  }
1534
1632
 
@@ -1663,7 +1761,7 @@ pycall_pylong_to_ruby(PyObject *pyobj)
1663
1761
  }
1664
1762
  #endif
1665
1763
 
1666
- rb_warning("Currentry do not support to convert multi-precision PyLong number to Ruby object");
1764
+ rb_warning("Currently do not support to convert multi-precision PyLong number to Ruby object");
1667
1765
 
1668
1766
  return Qnil;
1669
1767
  }
@@ -1787,7 +1885,11 @@ pycall_conv_m_unregister_python_type_mapping(VALUE mod, VALUE pytypeptr)
1787
1885
  static VALUE
1788
1886
  pycall_conv_m_from_ruby(VALUE mod, VALUE obj)
1789
1887
  {
1790
- PyObject *pyobj = pycall_pyobject_from_ruby(obj);
1888
+ PyObject *pyobj;
1889
+
1890
+ pycall_drain_pending_decrefs();
1891
+
1892
+ pyobj = pycall_pyobject_from_ruby(obj);
1791
1893
  if (PyType_Check(pyobj) || PyClass_Check(pyobj))
1792
1894
  return pycall_pytypeptr_new(pyobj);
1793
1895
  if (PyRuby_Check(pyobj))
@@ -1799,7 +1901,11 @@ static VALUE
1799
1901
  pycall_conv_m_to_ruby(VALUE mod, VALUE pyptr)
1800
1902
  {
1801
1903
  VALUE obj, obj_pyptr;
1802
- PyObject *pyobj = check_get_pyobj_ptr(pyptr, NULL);
1904
+ PyObject *pyobj;
1905
+
1906
+ pycall_drain_pending_decrefs();
1907
+
1908
+ pyobj = check_get_pyobj_ptr(pyptr, NULL);
1803
1909
  obj = obj_pyptr = pycall_pyobject_to_ruby(pyobj);
1804
1910
  if (is_pyobject_wrapper(obj)) {
1805
1911
  obj_pyptr = pycall_pyobject_wrapper_get_pyptr(obj);
@@ -2321,6 +2427,8 @@ Init_pycall(void)
2321
2427
  pycall_tls_create((pycall_tls_key *)&without_gvl_key);
2322
2428
  rb_define_module_function(mPyCall, "without_gvl", pycall_m_without_gvl, 0);
2323
2429
 
2430
+ rb_nativethread_lock_initialize(&pending_decrefs.lock);
2431
+
2324
2432
  /* PyCall::PyPtr */
2325
2433
 
2326
2434
  cPyPtr = rb_define_class_under(mPyCall, "PyPtr", rb_cBasicObject);
@@ -11,6 +11,7 @@ extern "C" {
11
11
  #include <ruby.h>
12
12
  #include <ruby/encoding.h>
13
13
  #include <ruby/thread.h>
14
+ #include <ruby/thread_native.h>
14
15
 
15
16
  #include <assert.h>
16
17
  #include <inttypes.h>
@@ -71,7 +72,7 @@ typedef intptr_t Py_intptr_t;
71
72
  #ifdef HAVE_SSIZE_T
72
73
  typedef ssize_t Py_ssize_t;
73
74
  #elif SIZEOF_SIZE_T == SIZEOF_VOIDP
74
- typedef Py_intptr_tr Py_ssize_t;
75
+ typedef Py_intptr_t Py_ssize_t;
75
76
  #elif SIZEOF_SIZE_T == SIZEOF_INT
76
77
  typedef int Py_ssize_t;
77
78
  #elif SIZEOF_SIZE_T == SIZEOF_LONG
@@ -6,7 +6,6 @@ static PyMemberDef PyRuby_members[] = {
6
6
  {NULL} /* sentinel */
7
7
  };
8
8
 
9
- static VALUE PyRuby_get_ruby_object_and_set_pyerr(PyObject *pyobj);
10
9
  static void PyRuby_dealloc_with_gvl(PyRubyObject *);
11
10
  static PyObject * PyRuby_repr_with_gvl(PyRubyObject *);
12
11
  static PyObject * PyRuby_call_with_gvl(PyRubyObject *, PyObject *, PyObject *);
@@ -102,9 +101,7 @@ PyRuby_repr(PyRubyObject *pyro)
102
101
  VALUE obj, str;
103
102
  PyObject *res;
104
103
 
105
- obj = PyRuby_get_ruby_object_and_set_pyerr((PyObject *)pyro);
106
- if (obj == Qundef)
107
- return NULL;
104
+ obj = PyRuby_get_ruby_object((PyObject *)pyro);
108
105
 
109
106
  str = rb_inspect(obj);
110
107
  res = pycall_pystring_from_format("<PyCall.ruby_object %s>", StringValueCStr(str));
@@ -143,9 +140,7 @@ PyRuby_hash_long(PyRubyObject *pyro)
143
140
  VALUE obj, rbhash;
144
141
  intptr_t h;
145
142
 
146
- obj = PyRuby_get_ruby_object_and_set_pyerr((PyObject *)pyro);
147
- if (obj == Qundef)
148
- return (void *)-1;
143
+ obj = PyRuby_get_ruby_object((PyObject *)pyro);
149
144
 
150
145
  rbhash = rb_hash(obj);
151
146
  h = FIX2LONG(rbhash); /* Ruby's hash value is a Fixnum */
@@ -170,9 +165,7 @@ PyRuby_hash_hash_t(PyRubyObject *pyro)
170
165
  VALUE obj, rbhash;
171
166
  Py_hash_t h;
172
167
 
173
- obj = PyRuby_get_ruby_object_and_set_pyerr((PyObject *)pyro);
174
- if (obj == Qundef)
175
- return (void *)-1;
168
+ obj = PyRuby_get_ruby_object((PyObject *)pyro);
176
169
 
177
170
  rbhash = rb_hash(obj);
178
171
  #if SIZEOF_PY_HASH_T == SIZEOF_LONG
@@ -242,9 +235,7 @@ PyRuby_call(struct PyRuby_call_params *params)
242
235
  PyObject *pyobj_res;
243
236
  int state;
244
237
 
245
- obj = PyRuby_get_ruby_object_and_set_pyerr((PyObject *)params->pyro);
246
- if (obj == Qundef)
247
- return NULL;
238
+ obj = PyRuby_get_ruby_object((PyObject *)params->pyro);
248
239
 
249
240
  id_call = rb_intern("call");
250
241
  if (!rb_respond_to(obj, id_call)) {
@@ -296,9 +287,7 @@ PyRuby_getattro(struct PyRuby_getattro_params *params)
296
287
  ID name_id;
297
288
  PyObject *pyobj_res;
298
289
 
299
- obj = PyRuby_get_ruby_object_and_set_pyerr((PyObject *)params->pyro);
300
- if (obj == Qundef)
301
- return NULL;
290
+ obj = PyRuby_get_ruby_object((PyObject *)params->pyro);
302
291
 
303
292
  name = pycall_pyobject_to_ruby(params->pyobj_name);
304
293
  name_cstr = StringValueCStr(name);
@@ -489,56 +478,3 @@ pycall_init_ruby_wrapper(void)
489
478
  rb_define_module_function(mPyCall, "wrap_ruby_object", pycall_m_wrap_ruby_object, 1);
490
479
  }
491
480
 
492
- /* --- File internal utilities --- */
493
-
494
- static VALUE
495
- funcall_id2ref(VALUE object_id)
496
- {
497
- VALUE rb_mObjSpace;
498
- object_id = rb_check_to_integer(object_id, "to_int");
499
- rb_mObjSpace = rb_const_get(rb_cObject, rb_intern("ObjectSpace"));
500
- return rb_funcall(rb_mObjSpace, rb_intern("_id2ref"), 1, object_id);
501
- }
502
-
503
- static VALUE
504
- protect_id2ref(VALUE object_id)
505
- {
506
- VALUE obj;
507
- int state;
508
-
509
- obj = rb_protect((VALUE (*)(VALUE))funcall_id2ref, object_id, &state);
510
- if (state)
511
- return Qundef;
512
-
513
- return obj;
514
- }
515
-
516
- static VALUE
517
- protect_id2ref_and_set_pyerr(VALUE object_id)
518
- {
519
- VALUE obj = protect_id2ref(object_id);
520
- if (obj != Qundef)
521
- return obj;
522
-
523
- obj = rb_errinfo();
524
- if (RTEST(rb_obj_is_kind_of(obj, rb_eRangeError))) {
525
- Py_API(PyErr_SetString)(Py_API(PyExc_RuntimeError), "[BUG] referenced object was garbage-collected");
526
- }
527
- else {
528
- VALUE emesg = rb_check_funcall(obj, rb_intern("message"), 0, 0);
529
- Py_API(PyErr_Format)(Py_API(PyExc_RuntimeError),
530
- "[BUG] Unable to obtain ruby object from ID: %s (%s)",
531
- StringValueCStr(emesg), rb_class2name(CLASS_OF(obj)));
532
- }
533
- return Qundef;
534
- }
535
-
536
- static VALUE
537
- PyRuby_get_ruby_object_and_set_pyerr(PyObject *pyobj)
538
- {
539
- VALUE obj_id;
540
- if (!PyRuby_Check(pyobj))
541
- return Qundef;
542
- obj_id = rb_obj_id(PyRuby_get_ruby_object(pyobj));
543
- return protect_id2ref_and_set_pyerr(obj_id);
544
- }
@@ -231,7 +231,7 @@ module PyCall
231
231
  else
232
232
  name, basic_size = *args
233
233
  new.tap do |t|
234
- # NOTE: Disable autorelease for avoiding SEGV occurrance in Python's GC collect function
234
+ # NOTE: Disable autorelease for avoiding SEGV occurrence in Python's GC collect function
235
235
  # at which the __new__ method object of this type object is freed.
236
236
  t.pointer.autorelease = false
237
237
 
@@ -33,8 +33,6 @@ module PyCall
33
33
  }.freeze
34
34
 
35
35
  def method_missing(name, *args)
36
- name_str = name.to_s if name.kind_of?(Symbol)
37
- name_str.chop! if name_str.end_with?('=')
38
36
  case name
39
37
  when *OPERATOR_METHOD_NAMES.keys
40
38
  op_name = OPERATOR_METHOD_NAMES[name]
@@ -44,7 +42,8 @@ module PyCall
44
42
  return self.__send__(name, *args)
45
43
  end
46
44
  else
47
- if LibPython::Helpers.hasattr?(__pyptr__, name_str)
45
+ name_without_last_equal = name.to_s.chomp('=')
46
+ if LibPython::Helpers.hasattr?(__pyptr__, name_without_last_equal)
48
47
  LibPython::Helpers.define_wrapper_method(self, name)
49
48
  return self.__send__(name, *args)
50
49
  end
@@ -1,5 +1,5 @@
1
1
  module PyCall
2
- VERSION = "1.5.2"
2
+ VERSION = "1.5.3"
3
3
 
4
4
  module Version
5
5
  numbers, TAG = VERSION.split("-")
data/lib/pycall.rb CHANGED
@@ -116,7 +116,7 @@ module PyCall
116
116
  rescue PyError => err
117
117
  raise err unless ctx.__exit__(err.type, err.value, err.traceback)
118
118
  rescue Exception => err
119
- # TODO: support telling what exception has been catched
119
+ # TODO: support telling what exception has been caught
120
120
  raise err unless ctx.__exit__(err.class, err, err.backtrace_locations)
121
121
  else
122
122
  ctx.__exit__(nil, nil, nil)
data/pycall.gemspec CHANGED
@@ -1,29 +1,21 @@
1
1
  # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'pycall/version'
2
+
3
+ require_relative 'lib/pycall/version'
5
4
 
6
5
  Gem::Specification.new do |spec|
7
6
  spec.name = "pycall"
8
- version_components = [
9
- PyCall::Version::MAJOR.to_s,
10
- PyCall::Version::MINOR.to_s,
11
- PyCall::Version::MICRO.to_s,
12
- PyCall::Version::TAG,
13
- ]
14
- spec.version = version_components.compact.join(".")
7
+ spec.version = PyCall::VERSION
15
8
  spec.authors = ["Kenta Murata"]
16
9
  spec.email = ["mrkn@mrkn.jp"]
17
10
 
18
11
  spec.summary = %q{pycall}
19
12
  spec.description = %q{pycall}
20
- spec.homepage = "https://github.com/mrkn/pycall"
13
+ spec.homepage = "https://github.com/red-data-tools/pycall.rb"
21
14
  spec.license = "MIT"
22
15
 
23
16
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
24
17
  case f
25
- when %r{^Guardfile}, # NOTE: Skip symlink for Windows
26
- %r{^ext/pycall/spec_helper/},
18
+ when %r{^ext/pycall/spec_helper/},
27
19
  %r{^(test|spec|features)/}
28
20
  true
29
21
  else
@@ -36,13 +28,13 @@ Gem::Specification.new do |spec|
36
28
  spec.require_paths = ["lib"]
37
29
  spec.extensions = ["ext/pycall/extconf.rb"]
38
30
 
31
+ spec.add_dependency 'fiddle', '>= 1.0.0'
32
+
39
33
  spec.add_development_dependency "bundler"
40
34
  spec.add_development_dependency "rake"
41
35
  spec.add_development_dependency "rake-compiler"
42
36
  spec.add_development_dependency "rake-compiler-dock"
43
37
  spec.add_development_dependency "rspec"
44
38
  spec.add_development_dependency "launchy"
45
- spec.add_development_dependency "pry"
46
- spec.add_development_dependency "pry-byebug"
47
39
  spec.add_development_dependency "test-unit"
48
40
  end
metadata CHANGED
@@ -1,45 +1,30 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pycall
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.2
4
+ version: 1.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kenta Murata
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2024-05-24 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
- name: bundler
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: rake
13
+ name: fiddle
29
14
  requirement: !ruby/object:Gem::Requirement
30
15
  requirements:
31
16
  - - ">="
32
17
  - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
18
+ version: 1.0.0
19
+ type: :runtime
35
20
  prerelease: false
36
21
  version_requirements: !ruby/object:Gem::Requirement
37
22
  requirements:
38
23
  - - ">="
39
24
  - !ruby/object:Gem::Version
40
- version: '0'
25
+ version: 1.0.0
41
26
  - !ruby/object:Gem::Dependency
42
- name: rake-compiler
27
+ name: bundler
43
28
  requirement: !ruby/object:Gem::Requirement
44
29
  requirements:
45
30
  - - ">="
@@ -53,7 +38,7 @@ dependencies:
53
38
  - !ruby/object:Gem::Version
54
39
  version: '0'
55
40
  - !ruby/object:Gem::Dependency
56
- name: rake-compiler-dock
41
+ name: rake
57
42
  requirement: !ruby/object:Gem::Requirement
58
43
  requirements:
59
44
  - - ">="
@@ -67,7 +52,7 @@ dependencies:
67
52
  - !ruby/object:Gem::Version
68
53
  version: '0'
69
54
  - !ruby/object:Gem::Dependency
70
- name: rspec
55
+ name: rake-compiler
71
56
  requirement: !ruby/object:Gem::Requirement
72
57
  requirements:
73
58
  - - ">="
@@ -81,7 +66,7 @@ dependencies:
81
66
  - !ruby/object:Gem::Version
82
67
  version: '0'
83
68
  - !ruby/object:Gem::Dependency
84
- name: launchy
69
+ name: rake-compiler-dock
85
70
  requirement: !ruby/object:Gem::Requirement
86
71
  requirements:
87
72
  - - ">="
@@ -95,7 +80,7 @@ dependencies:
95
80
  - !ruby/object:Gem::Version
96
81
  version: '0'
97
82
  - !ruby/object:Gem::Dependency
98
- name: pry
83
+ name: rspec
99
84
  requirement: !ruby/object:Gem::Requirement
100
85
  requirements:
101
86
  - - ">="
@@ -109,7 +94,7 @@ dependencies:
109
94
  - !ruby/object:Gem::Version
110
95
  version: '0'
111
96
  - !ruby/object:Gem::Dependency
112
- name: pry-byebug
97
+ name: launchy
113
98
  requirement: !ruby/object:Gem::Requirement
114
99
  requirements:
115
100
  - - ">="
@@ -146,6 +131,7 @@ extra_rdoc_files: []
146
131
  files:
147
132
  - ".github/dependabot.yml"
148
133
  - ".github/workflows/ci.yml"
134
+ - ".github/workflows/release.yml"
149
135
  - ".github/workflows/windows.yml"
150
136
  - ".gitignore"
151
137
  - ".rspec"
@@ -155,14 +141,9 @@ files:
155
141
  - README.md
156
142
  - Rakefile
157
143
  - bin/console
158
- - bin/guard
159
144
  - bin/rspec
160
145
  - bin/runner
161
146
  - bin/setup
162
- - ci/travis_before_script.sh
163
- - ci/travis_install.sh
164
- - ci/travis_retry.sh
165
- - config/Guardfile
166
147
  - docker/Dockerfile
167
148
  - docker/Gemfile
168
149
  - docker/README.md
@@ -218,11 +199,10 @@ files:
218
199
  - pycall.gemspec
219
200
  - tasks/docker.rake
220
201
  - tasks/pycall.rake
221
- homepage: https://github.com/mrkn/pycall
202
+ homepage: https://github.com/red-data-tools/pycall.rb
222
203
  licenses:
223
204
  - MIT
224
205
  metadata: {}
225
- post_install_message:
226
206
  rdoc_options: []
227
207
  require_paths:
228
208
  - lib
@@ -237,8 +217,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
237
217
  - !ruby/object:Gem::Version
238
218
  version: '0'
239
219
  requirements: []
240
- rubygems_version: 3.5.10
241
- signing_key:
220
+ rubygems_version: 4.0.16
242
221
  specification_version: 4
243
222
  summary: pycall
244
223
  test_files: []
data/bin/guard DELETED
@@ -1,17 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
- #
4
- # This file was generated by Bundler.
5
- #
6
- # The application 'guard' is installed as part of a gem, and
7
- # this file is here to facilitate running it.
8
- #
9
-
10
- require "pathname"
11
- ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
12
- Pathname.new(__FILE__).realpath)
13
-
14
- require "rubygems"
15
- require "bundler/setup"
16
-
17
- load Gem.bin_path("guard", "guard")
@@ -1,21 +0,0 @@
1
- __DIR__=$(cd $(dirname $BASH_SOURCE); pwd)
2
- . $__DIR__/travis_retry.sh
3
-
4
- set -ex
5
-
6
- if test -z "$PYENV_VERSION"; then
7
- echo "ERROR: PYENV_VERSION is not provided" >2
8
- exit 1
9
- fi
10
-
11
- if test -n "$LIBPYTHON"; then
12
- export LIBPYTHON=$(pyenv root)/$LIBPYTHON
13
- fi
14
-
15
- case "$PYENV_VERSION" in
16
- *conda*)
17
- source $(pyenv prefix)/bin/activate test-environment
18
- ;;
19
- esac
20
-
21
- set +ex
data/ci/travis_install.sh DELETED
@@ -1,69 +0,0 @@
1
- #! /bin/bash
2
-
3
- __DIR__=$(cd $(dirname $BASH_SOURCE); pwd)
4
- . $__DIR__/travis_retry.sh
5
-
6
- set -ex
7
-
8
- if test -z "$PYENV_VERSION"; then
9
- echo "ERROR: PYENV_VERSION is not provided" >2
10
- exit 1
11
- fi
12
-
13
- pyenv_root=$(pyenv root)
14
-
15
- if test -n "$LIBPYTHON"; then
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
24
- fi
25
-
26
- (
27
- cd $(pyenv root)
28
- if [ -d .git ]; then
29
- git fetch origin
30
- git checkout master
31
- git reset --hard origin/master
32
- fi
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
42
-
43
- case "$PYENV_VERSION" in
44
- *conda*)
45
- case "$PYENV_VERSION" in
46
- *conda2*)
47
- python_version=2.7
48
- ;;
49
- *)
50
- python_version=3.6
51
- ;;
52
- esac
53
- conda config --set always_yes yes --set changeps1 no
54
- travis_retry conda update -q conda
55
- conda info -a
56
- travis_retry conda create -q -n test-environment python=$python_version numpy
57
- source $(pyenv prefix)/bin/activate test-environment
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
- ;;
64
- *)
65
- travis_retry pip install --user numpy
66
- ;;
67
- esac
68
-
69
- bundle install
data/ci/travis_retry.sh DELETED
@@ -1,19 +0,0 @@
1
- travis_retry ()
2
- {
3
- local result=0
4
- local count=1
5
- while [ $count -le 3 ]; do
6
- [ $result -ne 0 ] && {
7
- echo -e "\n${ANSI_RED}The command \"$@\" failed. Retrying, $count of 3.${ANSI_RESET}\n" 1>&2
8
- }
9
- "$@"
10
- result=$?
11
- [ $result -eq 0 ] && break
12
- count=$(($count + 1))
13
- sleep 1
14
- done
15
- [ $count -gt 3 ] && {
16
- echo -e "\n${ANSI_RED}The command \"$@\" failed 3 times.${ANSI_RESET}\n" 1>&2
17
- }
18
- return $result
19
- }
data/config/Guardfile DELETED
@@ -1,30 +0,0 @@
1
- # More info at https://github.com/guard/guard#readme
2
-
3
- if RUBY_PLATFORM =~ /darwin/
4
- notification :terminal_notifier, app_name: 'pycall.gem ::', activate: 'com.googlecode.iTerm2'
5
- end
6
-
7
- directories %w(config lib spec).select { |d|
8
- Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")
9
- }
10
-
11
- watch('config/Guardfile') do
12
- UI.info "Exiting guard because config changed"
13
- exit 0
14
- end
15
-
16
- guard :rspec, cmd: 'bin/rspec' do
17
- require "guard/rspec/dsl"
18
- dsl = Guard::RSpec::Dsl.new(self)
19
-
20
- # RSpec files
21
- rspec = dsl.rspec
22
- watch(rspec.spec_helper) { rspec.spec_dir }
23
- watch(rspec.spec_support) { rspec.spec_dir }
24
- watch(rspec.spec_files)
25
-
26
- # Ruby files
27
- ruby = dsl.ruby
28
- dsl.watch_spec_files_for(ruby.lib_files)
29
- watch('lib/pycall/libpython.rb') { 'spec' }
30
- end