pycall 1.4.1 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +34 -24
- data/.github/workflows/windows.yml +2 -3
- data/CHANGES.md +19 -0
- data/README.md +33 -27
- data/ext/pycall/libpython.c +1 -0
- data/ext/pycall/pycall.c +96 -25
- data/ext/pycall/pycall_internal.h +20 -1
- data/ext/pycall/ruby_wrapper.c +5 -5
- data/lib/pycall/pyobject_wrapper.rb +6 -0
- data/lib/pycall/python/investigator.py +1 -1
- data/lib/pycall/version.rb +1 -1
- data/lib/pycall.rb +8 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7b2f11d56347f0f7a8977916c73468aa165a23350ed271e7cdb064341211aabb
|
4
|
+
data.tar.gz: 0d4c68470567e1a2d58d2512f8ca883dfaca1329f4d89a255d0917f3b2c85f7b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dd61f72199c6986c9bbe5260a7f43c92a6e8ef2036a4fb94c2d3ff14a74c1b21a306f601e6ccee73dd709fb76cc48b24af1002a3b95cbe18ecc7604015f8e028
|
7
|
+
data.tar.gz: 4040cec7aa67bb7edf1a8f07896e6c7b62b971b7e15a78171d8424465087dff5b25e8bedd0971f9c2060944712f1b14905605e9261933f480162a87f2bbd925b
|
data/.github/workflows/ci.yml
CHANGED
@@ -21,38 +21,48 @@ jobs:
|
|
21
21
|
fail-fast: false
|
22
22
|
matrix:
|
23
23
|
os:
|
24
|
+
- ubuntu-latest
|
24
25
|
- ubuntu-20.04
|
25
26
|
- macos-latest
|
26
27
|
ruby:
|
28
|
+
- "3.2"
|
29
|
+
- "3.1"
|
27
30
|
- "3.0"
|
28
|
-
- 2.7
|
29
|
-
- 2.6
|
31
|
+
- "2.7"
|
30
32
|
python:
|
31
|
-
- 3.x
|
32
|
-
- 2.x
|
33
|
+
- "3.x"
|
33
34
|
python_architecture:
|
34
35
|
- x64
|
35
36
|
venv:
|
36
37
|
- ""
|
37
38
|
include:
|
38
|
-
|
39
|
-
- { os: ubuntu-20.04 , ruby: 2.
|
40
|
-
- { os: ubuntu-20.04 , ruby: 2.5 , python:
|
41
|
-
- { os: ubuntu-20.04 , ruby: 2.4 , python:
|
42
|
-
|
43
|
-
|
44
|
-
- { os: ubuntu-20.04 , ruby: 2.7 , python: 3.
|
45
|
-
- { os: ubuntu-
|
46
|
-
- { os: ubuntu-20.04 , ruby:
|
47
|
-
- { os: ubuntu-20.04 , ruby:
|
48
|
-
- { os: ubuntu-
|
49
|
-
- { os: ubuntu-
|
50
|
-
|
51
|
-
-
|
52
|
-
|
39
|
+
# Old ruby and the latest Python 3
|
40
|
+
- { os: ubuntu-20.04 , ruby: 2.6 , python: "3.x" , python_architecture: x64 , venv: "" }
|
41
|
+
- { os: ubuntu-20.04 , ruby: 2.5 , python: "3.x" , python_architecture: x64 , venv: "" }
|
42
|
+
- { os: ubuntu-20.04 , ruby: 2.4 , python: "3.x" , python_architecture: x64 , venv: "" }
|
43
|
+
|
44
|
+
# Ruby 2.7 with Each Python 3.x
|
45
|
+
- { os: ubuntu-20.04 , ruby: 2.7 , python: "3.11" , python_architecture: x64 , venv: "" }
|
46
|
+
- { os: ubuntu-20.04 , ruby: 2.7 , python: "3.10" , python_architecture: x64 , venv: "" }
|
47
|
+
- { os: ubuntu-20.04 , ruby: 2.7 , python: "3.9" , python_architecture: x64 , venv: "" }
|
48
|
+
- { os: ubuntu-20.04 , ruby: 2.7 , python: "3.8" , python_architecture: x64 , venv: "" }
|
49
|
+
- { os: ubuntu-20.04 , ruby: 2.7 , python: "3.7" , python_architecture: x64 , venv: "" }
|
50
|
+
- { os: ubuntu-20.04 , ruby: 2.7 , python: "3.6" , python_architecture: x64 , venv: "" }
|
51
|
+
|
52
|
+
# Ruby-debug with the latest Python 3
|
53
|
+
- { os: ubuntu-20.04 , ruby: debug , python: "3.x" , python_architecture: x64 , venv: "" }
|
54
|
+
|
55
|
+
# Ruby 3.1 with the latest Python 3 with venv
|
56
|
+
- { os: ubuntu-20.04 , ruby: "3.1" , python: "3.x" , python_architecture: x64 , venv: "venv:" }
|
57
|
+
|
58
|
+
# macOS with venv
|
59
|
+
- { os: macos-latest , ruby: "3.1" , python: "3.x" , python_architecture: x64 , venv: "venv:" }
|
60
|
+
- { os: macos-latest , ruby: "3.1" , python: "3.8" , python_architecture: x64 , venv: "venv:" }
|
61
|
+
|
62
|
+
#- { os: macos-latest , ruby: debug , python: "3.x" , python_architecture: x64 , venv: "" }
|
53
63
|
|
54
64
|
steps:
|
55
|
-
- uses: actions/checkout@
|
65
|
+
- uses: actions/checkout@v3
|
56
66
|
with:
|
57
67
|
fetch-depth: 1
|
58
68
|
|
@@ -61,7 +71,7 @@ jobs:
|
|
61
71
|
with:
|
62
72
|
ruby-version: ${{ matrix.ruby }}
|
63
73
|
|
64
|
-
- uses: actions/setup-python@
|
74
|
+
- uses: actions/setup-python@v4
|
65
75
|
with:
|
66
76
|
python-version: ${{ matrix.python }}
|
67
77
|
architecture: ${{ matrix.python_architecture }}
|
@@ -96,11 +106,11 @@ jobs:
|
|
96
106
|
matrix:
|
97
107
|
os:
|
98
108
|
- ubuntu-20.04
|
99
|
-
|
109
|
+
#- macos-latest
|
100
110
|
ruby:
|
101
|
-
- "3.
|
111
|
+
- "3.1"
|
102
112
|
python:
|
103
|
-
- 3.
|
113
|
+
- "3.10"
|
104
114
|
|
105
115
|
steps:
|
106
116
|
- uses: actions/checkout@v2
|
@@ -26,7 +26,6 @@ jobs:
|
|
26
26
|
- 2.6
|
27
27
|
python:
|
28
28
|
- 3.x
|
29
|
-
- 2.x
|
30
29
|
python_architecture:
|
31
30
|
- x64
|
32
31
|
include:
|
@@ -34,7 +33,7 @@ jobs:
|
|
34
33
|
#- { os: windows-latest , ruby: mswin , python: 3.x , python_architecture: x64 }
|
35
34
|
|
36
35
|
steps:
|
37
|
-
- uses: actions/checkout@
|
36
|
+
- uses: actions/checkout@v3
|
38
37
|
with:
|
39
38
|
fetch-depth: 1
|
40
39
|
|
@@ -43,7 +42,7 @@ jobs:
|
|
43
42
|
with:
|
44
43
|
ruby-version: ${{ matrix.ruby }}
|
45
44
|
|
46
|
-
- uses: actions/setup-python@
|
45
|
+
- uses: actions/setup-python@v4
|
47
46
|
with:
|
48
47
|
python-version: ${{ matrix.python }}
|
49
48
|
architecture: ${{ matrix.python_architecture }}
|
data/CHANGES.md
CHANGED
@@ -1,5 +1,24 @@
|
|
1
1
|
# The change history of PyCall
|
2
2
|
|
3
|
+
## 1.5.0
|
4
|
+
|
5
|
+
* Fix to prevent distutils deprecation warning (#159)
|
6
|
+
|
7
|
+
*Christopher Dilks*
|
8
|
+
|
9
|
+
* Drop Python 2.7 support
|
10
|
+
|
11
|
+
* Fix memory leak of Python objects. (#129)
|
12
|
+
This should fixes #128 and maybe also fixes #164.
|
13
|
+
|
14
|
+
*mknkmyza*
|
15
|
+
|
16
|
+
## 1.4.2
|
17
|
+
|
18
|
+
* Add supports of unary operators: `+@`, `-@`, `~`
|
19
|
+
* Fix `without_gvl` for exceptions occurred in the given block
|
20
|
+
* Add PyCall.setattr and PyCall.delattr
|
21
|
+
|
3
22
|
## 1.4.1
|
4
23
|
|
5
24
|
* Fix SEGV occurred on Windows
|
data/README.md
CHANGED
@@ -19,9 +19,7 @@ pycall.rb supports Ruby version 2.4 or higher.
|
|
19
19
|
|
20
20
|
## Supported Python versions
|
21
21
|
|
22
|
-
pycall.rb supports Python version
|
23
|
-
|
24
|
-
Note that in Python 2.7 old-style class, that is defined without a super class, is not fully supported in pycall.rb.
|
22
|
+
pycall.rb supports Python version 3.7 or higher.
|
25
23
|
|
26
24
|
## Note for pyenv users
|
27
25
|
|
@@ -32,12 +30,6 @@ pyenv does not build the shared library in default, so you need to specify `--en
|
|
32
30
|
$ env PYTHON_CONFIGURE_OPTS='--enable-shared' pyenv install 3.7.2
|
33
31
|
```
|
34
32
|
|
35
|
-
## Note for Windows users
|
36
|
-
|
37
|
-
Currently, pycall.rb does not support Windows. Please try to use pycall.rb on WSL2 environment.
|
38
|
-
|
39
|
-
On Windows, the error "[BUG] object allocation during garbage collection phase" is occurred at unpredictable timings.
|
40
|
-
|
41
33
|
## Installation
|
42
34
|
|
43
35
|
Add this line to your application's Gemfile:
|
@@ -59,9 +51,8 @@ Or install it yourself as:
|
|
59
51
|
Here is a simple example to call Python's `math.sin` function and compare it to
|
60
52
|
the `Math.sin` in Ruby:
|
61
53
|
|
62
|
-
require 'pycall
|
63
|
-
|
64
|
-
pyimport :math
|
54
|
+
require 'pycall'
|
55
|
+
math = PyCall.import_module("math")
|
65
56
|
math.sin(math.pi / 4) - Math.sin(Math::PI / 4) # => 0.0
|
66
57
|
|
67
58
|
Type conversions from Ruby to Python are automatically performed for numeric,
|
@@ -151,9 +142,9 @@ Use [mrkn/pandas.rb](https://github.com/mrkn/pandas.rb) instead of just importin
|
|
151
142
|
|
152
143
|
PyCall wraps pointers of Python objects in `PyCall::PyPtr` objects.
|
153
144
|
`PyCall::PyPtr` class has two subclasses, `PyCall::PyTypePtr` and
|
154
|
-
`PyCall::PyRubyPtr`. `PyCall::PyTypePtr` is specialized for type
|
155
|
-
|
156
|
-
|
145
|
+
`PyCall::PyRubyPtr`. `PyCall::PyTypePtr` is specialized for type objects,
|
146
|
+
and `PyCall::PyRubyPtr` is for the objects that wraps pointers of
|
147
|
+
Ruby objects.
|
157
148
|
|
158
149
|
These `PyCall::PyPtr` objects are used mainly in PyCall infrastructure.
|
159
150
|
Instead, we usually treats the instances of `Object`, `Class`, `Module`, or
|
@@ -169,18 +160,33 @@ translates Ruby's coerce system into Python's swapped operation protocol.
|
|
169
160
|
## Deploying on Heroku
|
170
161
|
|
171
162
|
Heroku's default version of Python is not compiled with the `--enabled-shared`
|
172
|
-
option and can't be accessed by PyCall.
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
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
|
173
|
+
|
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
|
+
|
184
190
|
|
185
191
|
The buildpack will expect to find both a `runtime.txt` and a `requirements.txt`
|
186
192
|
file in the root of your project. You will need to add these to specify the
|
data/ext/pycall/libpython.c
CHANGED
@@ -110,6 +110,7 @@ pycall_init_libpython_api_table(VALUE libpython_handle)
|
|
110
110
|
INIT_API_TABLE_ENTRY(PyObject_GetAttrString, required);
|
111
111
|
INIT_API_TABLE_ENTRY(PyObject_SetAttrString, required);
|
112
112
|
INIT_API_TABLE_ENTRY(PyObject_HasAttrString, required);
|
113
|
+
INIT_API_TABLE_ENTRY(PyObject_DelAttrString, optional);
|
113
114
|
INIT_API_TABLE_ENTRY(PyObject_GetItem, required);
|
114
115
|
INIT_API_TABLE_ENTRY(PyObject_SetItem, required);
|
115
116
|
INIT_API_TABLE_ENTRY(PyObject_DelItem, required);
|
data/ext/pycall/pycall.c
CHANGED
@@ -52,7 +52,7 @@ pycall_python_hexversion(void)
|
|
52
52
|
|
53
53
|
#define python_is_unicode_literals (python_major_version >= 3)
|
54
54
|
|
55
|
-
|
55
|
+
intptr_t pycall_hash_salt;
|
56
56
|
|
57
57
|
static VALUE pycall_call_python_callable(PyObject *pycallable, int argc, VALUE *argv);
|
58
58
|
|
@@ -80,7 +80,7 @@ pycall_without_gvl_p(void)
|
|
80
80
|
*
|
81
81
|
* In Win32 thread, the default value is 0 (initialized by TlsAlloc).
|
82
82
|
*/
|
83
|
-
return
|
83
|
+
return pycall_tls_get(without_gvl_key) != (void*)0;
|
84
84
|
}
|
85
85
|
|
86
86
|
static inline int
|
@@ -107,6 +107,10 @@ pycall_without_gvl(VALUE (* func)(VALUE), VALUE arg)
|
|
107
107
|
|
108
108
|
pycall_set_with_gvl();
|
109
109
|
|
110
|
+
if (state) {
|
111
|
+
rb_jump_tag(state);
|
112
|
+
}
|
113
|
+
|
110
114
|
return result;
|
111
115
|
}
|
112
116
|
|
@@ -410,7 +414,6 @@ pycall_pyptr_inspect(VALUE obj)
|
|
410
414
|
|
411
415
|
cname = rb_class_name(CLASS_OF(obj));
|
412
416
|
str = rb_sprintf("#<%"PRIsVALUE":%p type=%s addr=%p>", cname, (void*)obj, Py_TYPE(pyobj)->tp_name, pyobj);
|
413
|
-
OBJ_INFECT(str, obj);
|
414
417
|
|
415
418
|
return str;
|
416
419
|
}
|
@@ -861,6 +864,51 @@ pycall_libpython_helpers_m_hasattr_p(VALUE mod, VALUE pyptr, VALUE name)
|
|
861
864
|
return res ? Qtrue : Qfalse;
|
862
865
|
}
|
863
866
|
|
867
|
+
static VALUE
|
868
|
+
pycall_libpython_helpers_m_setattr(VALUE mod, VALUE pyptr, VALUE name, VALUE val)
|
869
|
+
{
|
870
|
+
PyObject *pyobj, *pyval;
|
871
|
+
|
872
|
+
if (!is_pycall_pyptr(pyptr)) {
|
873
|
+
rb_raise(rb_eTypeError, "PyCall::PyPtr is required");
|
874
|
+
}
|
875
|
+
|
876
|
+
pyobj = get_pyobj_ptr(pyptr);
|
877
|
+
|
878
|
+
if (RB_TYPE_P(name, T_SYMBOL)) {
|
879
|
+
name = rb_sym_to_s(name);
|
880
|
+
}
|
881
|
+
|
882
|
+
pyval = pycall_pyobject_from_ruby(val);
|
883
|
+
if (Py_API(PyObject_SetAttrString)(pyobj, StringValueCStr(name), pyval) == -1) {
|
884
|
+
pycall_pyerror_fetch_and_raise("PyObject_SetAttrString");
|
885
|
+
}
|
886
|
+
|
887
|
+
return Qnil;
|
888
|
+
}
|
889
|
+
|
890
|
+
static VALUE
|
891
|
+
pycall_libpython_helpers_m_delattr(VALUE mod, VALUE pyptr, VALUE name)
|
892
|
+
{
|
893
|
+
PyObject *pyobj;
|
894
|
+
|
895
|
+
if (!is_pycall_pyptr(pyptr)) {
|
896
|
+
rb_raise(rb_eTypeError, "PyCall::PyPtr is required");
|
897
|
+
}
|
898
|
+
|
899
|
+
pyobj = get_pyobj_ptr(pyptr);
|
900
|
+
|
901
|
+
if (RB_TYPE_P(name, T_SYMBOL)) {
|
902
|
+
name = rb_sym_to_s(name);
|
903
|
+
}
|
904
|
+
|
905
|
+
if (Py_API(PyObject_DelAttrString)(pyobj, StringValueCStr(name)) == -1) {
|
906
|
+
pycall_pyerror_fetch_and_raise("PyObject_DelAttrString");
|
907
|
+
}
|
908
|
+
|
909
|
+
return Qnil;
|
910
|
+
}
|
911
|
+
|
864
912
|
static VALUE
|
865
913
|
pycall_libpython_helpers_m_callable_p(VALUE mod, VALUE pyptr)
|
866
914
|
{
|
@@ -918,10 +966,10 @@ pycall_extract_kwargs_from_ruby_hash(VALUE key, VALUE value, VALUE arg)
|
|
918
966
|
key_cstr = StringValueCStr(key);
|
919
967
|
pyvalue = pycall_pyobject_from_ruby(value);
|
920
968
|
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
return ST_CONTINUE;
|
969
|
+
int res = Py_API(PyDict_SetItemString)(kwargs, key_cstr, pyvalue);
|
970
|
+
pycall_Py_DecRef(pyvalue);
|
971
|
+
|
972
|
+
return (res < 0) ? ST_STOP : ST_CONTINUE;
|
925
973
|
}
|
926
974
|
|
927
975
|
static void
|
@@ -1013,6 +1061,10 @@ pycall_call_python_callable(PyObject *pycallable, int argc, VALUE *argv)
|
|
1013
1061
|
}
|
1014
1062
|
|
1015
1063
|
res = pyobject_call(pycallable, args, kwargs); /* New reference */
|
1064
|
+
pycall_Py_DecRef(args);
|
1065
|
+
if (kwargs) {
|
1066
|
+
pycall_Py_DecRef(kwargs);
|
1067
|
+
}
|
1016
1068
|
if (!res) {
|
1017
1069
|
pycall_pyerror_fetch_and_raise("PyObject_Call in pycall_call_python_callable");
|
1018
1070
|
}
|
@@ -1051,9 +1103,9 @@ pycall_pyobject_wrapper_wrapper_method(int argc, VALUE *argv, VALUE wrapper)
|
|
1051
1103
|
|
1052
1104
|
name_cstr[RSTRING_LEN(name) - 1] = '\0';
|
1053
1105
|
res = Py_API(PyObject_SetAttrString)(pyobj, name_cstr, attr);
|
1106
|
+
pycall_Py_DecRef(attr);
|
1054
1107
|
name_cstr[RSTRING_LEN(name) - 1] = '=';
|
1055
1108
|
if (res == -1) {
|
1056
|
-
pycall_Py_DecRef(attr);
|
1057
1109
|
pycall_pyerror_fetch_and_raise("PyObject_SetAttrString in pycall_pyobject_wrapper_wrapper_method");
|
1058
1110
|
}
|
1059
1111
|
|
@@ -1071,13 +1123,15 @@ pycall_pyobject_wrapper_wrapper_method(int argc, VALUE *argv, VALUE wrapper)
|
|
1071
1123
|
if (PyType_Check(attr) || PyClass_Check(attr))
|
1072
1124
|
return pycall_pyobject_to_ruby(attr);
|
1073
1125
|
|
1074
|
-
|
1126
|
+
VALUE obj = pycall_call_python_callable(attr, argc, argv);
|
1127
|
+
pycall_Py_DecRef(attr);
|
1128
|
+
return obj;
|
1075
1129
|
}
|
1076
1130
|
|
1077
1131
|
static VALUE
|
1078
1132
|
pycall_libpython_helpers_m_define_wrapper_method(VALUE mod, VALUE wrapper, VALUE name)
|
1079
1133
|
{
|
1080
|
-
VALUE pyptr
|
1134
|
+
VALUE pyptr;
|
1081
1135
|
PyObject *pyobj, *attr;
|
1082
1136
|
char *name_cstr;
|
1083
1137
|
|
@@ -1089,12 +1143,8 @@ pycall_libpython_helpers_m_define_wrapper_method(VALUE mod, VALUE wrapper, VALUE
|
|
1089
1143
|
pyobj = get_pyobj_ptr(pyptr);
|
1090
1144
|
|
1091
1145
|
if (RB_TYPE_P(name, T_SYMBOL)) {
|
1092
|
-
name_sym = name;
|
1093
1146
|
name = rb_sym_to_s(name);
|
1094
1147
|
}
|
1095
|
-
else if (RB_TYPE_P(name, T_STRING)) {
|
1096
|
-
name_sym = rb_str_intern(name);
|
1097
|
-
}
|
1098
1148
|
|
1099
1149
|
name_cstr = StringValueCStr(name);
|
1100
1150
|
if (name_cstr[RSTRING_LEN(name) - 1] == '=') {
|
@@ -1159,6 +1209,7 @@ pycall_libpython_helpers_m_getitem(VALUE mod, VALUE pyptr, VALUE key)
|
|
1159
1209
|
pyobj_key = pycall_convert_index(key);
|
1160
1210
|
|
1161
1211
|
pyobj_v = Py_API(PyObject_GetItem)(pyobj, pyobj_key);
|
1212
|
+
pycall_Py_DecRef(pyobj_key);
|
1162
1213
|
if (!pyobj_v) {
|
1163
1214
|
pycall_pyerror_fetch_and_raise("PyObject_GetItem in pycall_libpython_helpers_m_getitem");
|
1164
1215
|
}
|
@@ -1179,17 +1230,17 @@ pycall_libpython_helpers_m_setitem(VALUE mod, VALUE pyptr, VALUE key, VALUE v)
|
|
1179
1230
|
pyobj_value = pycall_pyobject_from_ruby(v);
|
1180
1231
|
|
1181
1232
|
res = Py_API(PyObject_SetItem)(pyobj, pyobj_key, pyobj_value);
|
1233
|
+
pycall_Py_DecRef(pyobj_key);
|
1234
|
+
pycall_Py_DecRef(pyobj_value);
|
1182
1235
|
if (res == -1) {
|
1183
1236
|
pycall_pyerror_fetch_and_raise("PyObject_SetItem in pycall_libpython_helpers_m_setitem");
|
1184
1237
|
}
|
1185
|
-
Py_API(Py_DecRef(pyobj_key));
|
1186
|
-
Py_API(Py_DecRef(pyobj_value));
|
1187
1238
|
|
1188
1239
|
return v;
|
1189
1240
|
}
|
1190
1241
|
|
1191
1242
|
static VALUE
|
1192
|
-
pycall_libpython_helpers_m_delitem(VALUE mod, VALUE pyptr, VALUE key
|
1243
|
+
pycall_libpython_helpers_m_delitem(VALUE mod, VALUE pyptr, VALUE key)
|
1193
1244
|
{
|
1194
1245
|
PyObject *pyobj, *pyobj_key;
|
1195
1246
|
int res;
|
@@ -1198,11 +1249,12 @@ pycall_libpython_helpers_m_delitem(VALUE mod, VALUE pyptr, VALUE key, VALUE v)
|
|
1198
1249
|
pyobj_key = pycall_convert_index(key);
|
1199
1250
|
|
1200
1251
|
res = Py_API(PyObject_DelItem)(pyobj, pyobj_key);
|
1252
|
+
pycall_Py_DecRef(pyobj_key);
|
1201
1253
|
if (res == -1) {
|
1202
1254
|
pycall_pyerror_fetch_and_raise("PyObject_DelItem");
|
1203
1255
|
}
|
1204
1256
|
|
1205
|
-
return
|
1257
|
+
return Qnil;
|
1206
1258
|
}
|
1207
1259
|
|
1208
1260
|
static VALUE
|
@@ -1229,6 +1281,7 @@ pycall_libpython_helpers_m_dict_contains(VALUE mod, VALUE pyptr, VALUE key)
|
|
1229
1281
|
pyobj = check_get_pyobj_ptr(pyptr, Py_API(PyDict_Type));
|
1230
1282
|
pyobj_key = pycall_pyobject_from_ruby(key);
|
1231
1283
|
res = Py_API(PyDict_Contains)(pyobj, pyobj_key);
|
1284
|
+
pycall_Py_DecRef(pyobj_key);
|
1232
1285
|
if (res == -1) {
|
1233
1286
|
pycall_pyerror_fetch_and_raise("PyDict_Contains");
|
1234
1287
|
}
|
@@ -1267,6 +1320,7 @@ pycall_libpython_helpers_m_sequence_contains(VALUE mod, VALUE pyptr, VALUE key)
|
|
1267
1320
|
|
1268
1321
|
pyobj_key = pycall_pyobject_from_ruby(key);
|
1269
1322
|
res = Py_API(PySequence_Contains)(pyobj, pyobj_key);
|
1323
|
+
pycall_Py_DecRef(pyobj_key);
|
1270
1324
|
if (res == -1) {
|
1271
1325
|
pycall_pyerror_fetch_and_raise("PySequence_Contains");
|
1272
1326
|
}
|
@@ -1664,6 +1718,7 @@ pycall_pytuple_to_a(PyObject *pyobj)
|
|
1664
1718
|
PyObject *pytem = Py_API(PyTuple_GetItem)(pyobj, i);
|
1665
1719
|
Py_API(Py_IncRef)(pytem);
|
1666
1720
|
rb_ary_push(ary, pycall_pyobject_to_ruby(pytem));
|
1721
|
+
pycall_Py_DecRef(pytem);
|
1667
1722
|
}
|
1668
1723
|
|
1669
1724
|
return ary;
|
@@ -1682,6 +1737,7 @@ pycall_pysequence_to_a(PyObject *pyobj)
|
|
1682
1737
|
for (i = 0; i < n; ++i) {
|
1683
1738
|
PyObject *pytem = Py_API(PySequence_GetItem)(pyobj, i);
|
1684
1739
|
rb_ary_push(ary, pycall_pyobject_to_ruby(pytem));
|
1740
|
+
pycall_Py_DecRef(pytem);
|
1685
1741
|
}
|
1686
1742
|
|
1687
1743
|
return ary;
|
@@ -1894,12 +1950,10 @@ pycall_pydict_from_ruby_iter(VALUE key, VALUE value, VALUE arg)
|
|
1894
1950
|
pyobj_key = pycall_pyobject_from_ruby(key);
|
1895
1951
|
pyobj_value = pycall_pyobject_from_ruby(value);
|
1896
1952
|
res = Py_API(PyObject_SetItem)(pydictobj, pyobj_key, pyobj_value);
|
1897
|
-
|
1898
|
-
|
1899
|
-
|
1900
|
-
|
1901
|
-
Py_API(Py_DecRef)(pyobj_value);
|
1902
|
-
return ST_CONTINUE;
|
1953
|
+
pycall_Py_DecRef(pyobj_key);
|
1954
|
+
pycall_Py_DecRef(pyobj_value);
|
1955
|
+
|
1956
|
+
return (res == -1) ? ST_STOP : ST_CONTINUE;
|
1903
1957
|
}
|
1904
1958
|
|
1905
1959
|
PyObject *
|
@@ -2017,6 +2071,8 @@ pycall_pyerror_fetch_and_raise(char const *format, ...)
|
|
2017
2071
|
va_list args;
|
2018
2072
|
VALUE pyerror, msg;
|
2019
2073
|
|
2074
|
+
RBIMPL_NONNULL_ARG(format);
|
2075
|
+
|
2020
2076
|
pyerror = pycall_pyerror_fetch();
|
2021
2077
|
if (!NIL_P(pyerror))
|
2022
2078
|
rb_exc_raise(pyerror);
|
@@ -2074,11 +2130,24 @@ pycall_pystring_from_formatv(char const *format, va_list vargs)
|
|
2074
2130
|
|
2075
2131
|
/* ==== Python ==== */
|
2076
2132
|
|
2133
|
+
int
|
2134
|
+
pycall_PyObject_DelAttrString(PyObject *pyobj, const char *attr_name)
|
2135
|
+
{
|
2136
|
+
/* PyObject_DelAttrString is defined by using PyObject_SetAttrString in CPython's abstract.h */
|
2137
|
+
return Py_API(PyObject_SetAttrString)(pyobj, attr_name, NULL);
|
2138
|
+
}
|
2139
|
+
|
2077
2140
|
static void
|
2078
2141
|
init_python(void)
|
2079
2142
|
{
|
2080
2143
|
static char const *argv[1] = { "" };
|
2081
2144
|
|
2145
|
+
/* optional functions */
|
2146
|
+
if (! Py_API(PyObject_DelAttrString)) {
|
2147
|
+
/* The case of PyObject_DelAttrString as a macro */
|
2148
|
+
Py_API(PyObject_DelAttrString) = pycall_PyObject_DelAttrString;
|
2149
|
+
}
|
2150
|
+
|
2082
2151
|
Py_API(Py_InitializeEx)(0);
|
2083
2152
|
Py_API(PySys_SetArgvEx)(0, (char **)argv, 0);
|
2084
2153
|
|
@@ -2236,7 +2305,7 @@ Init_pycall(void)
|
|
2236
2305
|
|
2237
2306
|
rb_define_module_function(mPyCall, "after_fork", pycall_after_fork, 0);
|
2238
2307
|
|
2239
|
-
pycall_tls_create(&without_gvl_key);
|
2308
|
+
pycall_tls_create((pycall_tls_key *)&without_gvl_key);
|
2240
2309
|
rb_define_module_function(mPyCall, "without_gvl", pycall_m_without_gvl, 0);
|
2241
2310
|
|
2242
2311
|
/* PyCall::PyPtr */
|
@@ -2301,6 +2370,8 @@ Init_pycall(void)
|
|
2301
2370
|
rb_define_module_function(mHelpers, "compare", pycall_libpython_helpers_m_compare, 3);
|
2302
2371
|
rb_define_module_function(mHelpers, "getattr", pycall_libpython_helpers_m_getattr, -1);
|
2303
2372
|
rb_define_module_function(mHelpers, "hasattr?", pycall_libpython_helpers_m_hasattr_p, 2);
|
2373
|
+
rb_define_module_function(mHelpers, "setattr", pycall_libpython_helpers_m_setattr, 3);
|
2374
|
+
rb_define_module_function(mHelpers, "delattr", pycall_libpython_helpers_m_delattr, 2);
|
2304
2375
|
rb_define_module_function(mHelpers, "callable?", pycall_libpython_helpers_m_callable_p, 1);
|
2305
2376
|
rb_define_module_function(mHelpers, "call_object", pycall_libpython_helpers_m_call_object, -1);
|
2306
2377
|
rb_define_module_function(mHelpers, "getitem", pycall_libpython_helpers_m_getitem, 2);
|
@@ -34,6 +34,22 @@ extern "C" {
|
|
34
34
|
# error ---->> ruby requires sizeof(void*) == sizeof(long) or sizeof(LONG_LONG) to be compiled. <<----
|
35
35
|
#endif
|
36
36
|
|
37
|
+
#ifndef RUBY_ASSERT
|
38
|
+
# define RUBY_ASSERT(expr) assert(expr)
|
39
|
+
#endif
|
40
|
+
|
41
|
+
#ifndef RBIMPL_ATTR_NONNULL
|
42
|
+
# define RBIMPL_ATTR_NONNULL(list) /* void */
|
43
|
+
#endif
|
44
|
+
|
45
|
+
#ifndef RBIMPL_NONNULL_ARG
|
46
|
+
# define RBIMPL_NONNULL_ARG(arg) RUBY_ASSERT(arg)
|
47
|
+
#endif
|
48
|
+
|
49
|
+
#ifndef RBIMPL_ATTR_FORMAT
|
50
|
+
# define RBIMPL_ATTR_FORMAT(x, y, z) /* void */
|
51
|
+
#endif
|
52
|
+
|
37
53
|
#ifndef RB_INTEGER_TYPE_P
|
38
54
|
# define RB_INTEGER_TYPE_P(obj) pycall_integer_type_p(obj)
|
39
55
|
static inline int
|
@@ -568,6 +584,7 @@ typedef struct {
|
|
568
584
|
PyObject * (* PyObject_GetAttrString)(PyObject *, char const *);
|
569
585
|
int (* PyObject_SetAttrString)(PyObject *, char const *, PyObject *);
|
570
586
|
int (* PyObject_HasAttrString)(PyObject *, char const *);
|
587
|
+
int (* PyObject_DelAttrString)(PyObject *, char const *);
|
571
588
|
PyObject * (* PyObject_GetItem)(PyObject *, PyObject *);
|
572
589
|
int (* PyObject_SetItem)(PyObject *obj, PyObject *key, PyObject *value);
|
573
590
|
int (* PyObject_DelItem)(PyObject *, PyObject *);
|
@@ -692,6 +709,8 @@ PyObject *pycall_pylist_from_ruby(VALUE);
|
|
692
709
|
PyObject *pycall_pydict_from_ruby(VALUE);
|
693
710
|
PyObject *pycall_pyslice_from_ruby(VALUE);
|
694
711
|
|
712
|
+
RBIMPL_ATTR_NONNULL((1))
|
713
|
+
RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 1, 0)
|
695
714
|
NORETURN(void pycall_pyerror_fetch_and_raise(char const *format, ...));
|
696
715
|
|
697
716
|
unsigned long pycall_default_tp_flags(void);
|
@@ -714,7 +733,7 @@ void pycall_init_gcguard(void);
|
|
714
733
|
void pycall_init_ruby_wrapper(void);
|
715
734
|
|
716
735
|
#define pycall_hash_salt_32 0xb592cd9b
|
717
|
-
extern
|
736
|
+
extern intptr_t pycall_hash_salt;
|
718
737
|
extern VALUE pycall_mPyCall;
|
719
738
|
extern VALUE pycall_cPyPtr;
|
720
739
|
extern VALUE pycall_eError;
|
data/ext/pycall/ruby_wrapper.c
CHANGED
@@ -136,7 +136,7 @@ static void *
|
|
136
136
|
PyRuby_hash_long(PyRubyObject *pyro)
|
137
137
|
{
|
138
138
|
VALUE obj, rbhash;
|
139
|
-
|
139
|
+
intptr_t h;
|
140
140
|
|
141
141
|
obj = PyRuby_get_ruby_object_and_set_pyerr((PyObject *)pyro);
|
142
142
|
if (obj == Qundef)
|
@@ -152,9 +152,9 @@ static long
|
|
152
152
|
PyRuby_hash_long_with_gvl(PyRubyObject *pyro)
|
153
153
|
{
|
154
154
|
if (!ruby_thread_has_gvl_p()) {
|
155
|
-
return (long)CALL_WITH_GVL(PyRuby_hash_long, pyro);
|
155
|
+
return (long)(intptr_t)CALL_WITH_GVL(PyRuby_hash_long, pyro);
|
156
156
|
}
|
157
|
-
return (long)PyRuby_hash_long(pyro);
|
157
|
+
return (long)(intptr_t)PyRuby_hash_long(pyro);
|
158
158
|
}
|
159
159
|
|
160
160
|
static void *
|
@@ -171,10 +171,10 @@ PyRuby_hash_hash_t(PyRubyObject *pyro)
|
|
171
171
|
#if SIZEOF_PY_HASH_T == SIZEOF_LONG
|
172
172
|
/* In this case, we can assume sizeof(Py_hash_t) == sizeof(long) */
|
173
173
|
h = NUM2SSIZET(rbhash);
|
174
|
-
return (void *)(h == -1 ?
|
174
|
+
return (void *)(h == -1 ? pycall_hash_salt : h);
|
175
175
|
#else
|
176
176
|
/* In this case, we can assume sizeof(long) == 4 and sizeof(Py_hash_t) == 8 */
|
177
|
-
h = (pycall_hash_salt_32 << 32) | FIX2LONG(rbhash);
|
177
|
+
h = ((Py_hash_t)pycall_hash_salt_32 << 32) | FIX2LONG(rbhash);
|
178
178
|
return (void *)(h == -1 ? ((pycall_hash_salt << 32) | pycall_hash_salt) : h);
|
179
179
|
#endif
|
180
180
|
}
|
data/lib/pycall/version.rb
CHANGED
data/lib/pycall.rb
CHANGED
@@ -59,6 +59,14 @@ module PyCall
|
|
59
59
|
LibPython::Helpers.hasattr?(obj.__pyptr__, name)
|
60
60
|
end
|
61
61
|
|
62
|
+
def setattr(obj, name, val)
|
63
|
+
LibPython::Helpers.setattr(obj.__pyptr__, name, val)
|
64
|
+
end
|
65
|
+
|
66
|
+
def delattr(obj, name)
|
67
|
+
LibPython::Helpers.delattr(obj.__pyptr__, name)
|
68
|
+
end
|
69
|
+
|
62
70
|
def same?(left, right)
|
63
71
|
case left
|
64
72
|
when PyObjectWrapper
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pycall
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kenta Murata
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-08-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -236,7 +236,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
236
236
|
- !ruby/object:Gem::Version
|
237
237
|
version: '0'
|
238
238
|
requirements: []
|
239
|
-
rubygems_version: 3.
|
239
|
+
rubygems_version: 3.3.13
|
240
240
|
signing_key:
|
241
241
|
specification_version: 4
|
242
242
|
summary: pycall
|