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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b016b6c9b12867d113713238ecc171cc4fde8965197c79c59abf66d8bb3b252e
4
- data.tar.gz: abb36597376d37744972f7bb537ab61032387c73947d7846b9947bb622bf04ac
3
+ metadata.gz: 7b2f11d56347f0f7a8977916c73468aa165a23350ed271e7cdb064341211aabb
4
+ data.tar.gz: 0d4c68470567e1a2d58d2512f8ca883dfaca1329f4d89a255d0917f3b2c85f7b
5
5
  SHA512:
6
- metadata.gz: 18bc020ef0c1f1d82e3796ae023170ee287bbf966c3efd12dd48f730eec585b7b70e00352d316e8e524e608c70bff0348937c1afc434ba7d6ae634223f0e1fa6
7
- data.tar.gz: 9ea69bb090386a7a280be6a7c779b406f49a3de2da5b406c64529be6a462535cbe9eefaad404e3dd1dab6bc98a5840e2825708f8d1602e1404ab4dbd83ce43b2
6
+ metadata.gz: dd61f72199c6986c9bbe5260a7f43c92a6e8ef2036a4fb94c2d3ff14a74c1b21a306f601e6ccee73dd709fb76cc48b24af1002a3b95cbe18ecc7604015f8e028
7
+ data.tar.gz: 4040cec7aa67bb7edf1a8f07896e6c7b62b971b7e15a78171d8424465087dff5b25e8bedd0971f9c2060944712f1b14905605e9261933f480162a87f2bbd925b
@@ -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
- - { os: ubuntu-20.04 , ruby: 2.5 , python: 3.x , python_architecture: x64 , venv: "" }
39
- - { os: ubuntu-20.04 , ruby: 2.4 , python: 3.x , python_architecture: x64 , venv: "" }
40
- - { os: ubuntu-20.04 , ruby: 2.5 , python: 2.x , python_architecture: x64 , venv: "" }
41
- - { os: ubuntu-20.04 , ruby: 2.4 , python: 2.x , python_architecture: x64 , venv: "" }
42
- - { os: ubuntu-20.04 , ruby: 2.7 , python: 3.8 , python_architecture: x64 , venv: "" }
43
- - { os: ubuntu-20.04 , ruby: 2.7 , python: 3.7 , python_architecture: x64 , venv: "" }
44
- - { os: ubuntu-20.04 , ruby: 2.7 , python: 3.6 , python_architecture: x64 , venv: "" }
45
- - { os: ubuntu-18.04 , ruby: 2.7 , python: 3.8 , python_architecture: x64 , venv: "" }
46
- - { os: ubuntu-20.04 , ruby: debug , python: 3.x , python_architecture: x64 , venv: "" }
47
- - { os: ubuntu-20.04 , ruby: "3.0" , python: 3.x , python_architecture: x64 , venv: "venv:" }
48
- - { os: ubuntu-18.04 , ruby: "3.0" , python: 3.x , python_architecture: x64 , venv: "venv:" }
49
- - { os: ubuntu-18.04 , ruby: "3.0" , python: 3.8 , python_architecture: x64 , venv: "venv:" }
50
- - { os: macos-latest , ruby: "3.0" , python: 3.x , python_architecture: x64 , venv: "venv:" }
51
- - { os: macos-latest , ruby: "3.0" , python: 3.8 , python_architecture: x64 , venv: "venv:" }
52
- #- { os: macos-latest , ruby: debug , python: 3.x , python_architecture: x64 , venv: "" }
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@v2
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@v2
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
- - macos-latest
109
+ #- macos-latest
100
110
  ruby:
101
- - "3.0"
111
+ - "3.1"
102
112
  python:
103
- - 3.8
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@v2
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@v2
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 2.7 or higher.
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/import'
63
- include PyCall::Import
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 (and classobj
155
- in 2.7) objects, and `PyCall::PyRubyPtr` is for the objects that wraps pointers
156
- of Ruby objects.
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. Alternative [buildpacks](https://devcenter.heroku.com/articles/buildpacks) are available,
173
- including these that have been reported to work with PyCall:
174
-
175
- https://github.com/richgong/heroku-buildpack-python
176
- https://github.com/dsounded/heroku-buildpack-python
177
- https://github.com/ReforgeHQ/heroku-buildpack-python
178
-
179
- These community-developed buildpacks are not supported by Heroku, so it's
180
- worth examining the source to make sure the buildpack you use suits your
181
- needs. For instance, 'ReforgeHQ' works well with Python 3.8.1, but has not
182
- been configured to work with other versions and may not be as generally
183
- useful as the 'dsounded' or 'richgong' buildpacks.
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
@@ -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
- long pycall_hash_salt;
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 (int)pycall_tls_get(without_gvl_key);
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
- if (Py_API(PyDict_SetItemString)(kwargs, key_cstr, pyvalue) < 0) {
922
- return ST_STOP;
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
- return pycall_call_python_callable(attr, argc, argv);
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, name_sym;
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, VALUE v)
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 v;
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
- if (res == -1) {
1898
- return ST_STOP;
1899
- }
1900
- Py_API(Py_DecRef)(pyobj_key);
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 long pycall_hash_salt;
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;
@@ -136,7 +136,7 @@ static void *
136
136
  PyRuby_hash_long(PyRubyObject *pyro)
137
137
  {
138
138
  VALUE obj, rbhash;
139
- long h;
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 ? (Py_hash_t)pycall_hash_salt : h);
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
  }
@@ -13,6 +13,12 @@ module PyCall
13
13
  end
14
14
 
15
15
  OPERATOR_METHOD_NAMES = {
16
+ # Unary operators
17
+ :+@ => :__pos__,
18
+ :-@ => :__neg__,
19
+ :~ => :__invert__,
20
+
21
+ # Binary operators
16
22
  :+ => :__add__,
17
23
  :- => :__sub__,
18
24
  :* => :__mul__,
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python
2
2
 
3
3
  import ctypes.util
4
- from distutils.sysconfig import get_config_var, get_python_version
4
+ from sysconfig import get_config_var, get_python_version
5
5
  import os
6
6
  import sys
7
7
 
@@ -1,5 +1,5 @@
1
1
  module PyCall
2
- VERSION = "1.4.1"
2
+ VERSION = "1.5.0"
3
3
 
4
4
  module Version
5
5
  numbers, TAG = VERSION.split("-")
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.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: 2021-07-12 00:00:00.000000000 Z
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.2.21
239
+ rubygems_version: 3.3.13
240
240
  signing_key:
241
241
  specification_version: 4
242
242
  summary: pycall