pycall 1.4.0 → 1.4.2
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 +4 -4
- data/.github/workflows/ci.yml +44 -21
- data/CHANGES.md +11 -0
- data/README.md +2 -9
- data/ext/pycall/gc.c +84 -5
- data/ext/pycall/libpython.c +1 -0
- data/ext/pycall/pycall.c +72 -11
- data/ext/pycall/pycall_internal.h +20 -1
- data/ext/pycall/ruby_wrapper.c +5 -5
- data/lib/pycall/iterable_wrapper.rb +32 -0
- data/lib/pycall/pyobject_wrapper.rb +6 -0
- data/lib/pycall/version.rb +1 -1
- data/lib/pycall/wrapper_object_cache.rb +6 -1
- data/lib/pycall.rb +13 -0
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9b2526ea3050985dc6d864e979804fb4ebaa9c36d8239b48809a9cd31dd01de5
|
|
4
|
+
data.tar.gz: 68df5fcac7fd5c574009ebbc33f0d9c1724ff8d5057d4dfaa69832b8509e4a92
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c52cd3ac7b709238a1b3ec95ed7eb45bcfaa5ef8a40b74f8de15cec928350152769c915250f27290e1b7f2bfb84032faa3a39837d0598c2927a114f6989f11dc
|
|
7
|
+
data.tar.gz: 6c45f609e09251815588b0a02095af9d221e2c715d4e614d16888326fc46c6158a631b6cc514d9b3e673468abf2834e003074a5e77f27b46098c5ab698328459
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -21,35 +21,58 @@ 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:
|
|
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
|
+
# The latest Python 2
|
|
45
|
+
- { os: ubuntu-20.04 , ruby: 3.1 , python: "2.x" , python_architecture: x64 , venv: "" }
|
|
46
|
+
- { os: ubuntu-20.04 , ruby: 3.0 , python: "2.x" , python_architecture: x64 , venv: "" }
|
|
47
|
+
- { os: ubuntu-20.04 , ruby: 2.7 , python: "2.x" , python_architecture: x64 , venv: "" }
|
|
48
|
+
- { os: ubuntu-20.04 , ruby: 2.6 , python: "2.x" , python_architecture: x64 , venv: "" }
|
|
49
|
+
- { os: ubuntu-20.04 , ruby: 2.5 , python: "2.x" , python_architecture: x64 , venv: "" }
|
|
50
|
+
- { os: ubuntu-20.04 , ruby: 2.4 , python: "2.x" , python_architecture: x64 , venv: "" }
|
|
51
|
+
|
|
52
|
+
# Ruby 2.7 with Each Python 3.x
|
|
53
|
+
- { os: ubuntu-20.04 , ruby: 2.7 , python: "3.11" , python_architecture: x64 , venv: "" }
|
|
54
|
+
- { os: ubuntu-20.04 , ruby: 2.7 , python: "3.10" , python_architecture: x64 , venv: "" }
|
|
55
|
+
- { os: ubuntu-20.04 , ruby: 2.7 , python: "3.9" , python_architecture: x64 , venv: "" }
|
|
56
|
+
- { os: ubuntu-20.04 , ruby: 2.7 , python: "3.8" , python_architecture: x64 , venv: "" }
|
|
57
|
+
- { os: ubuntu-20.04 , ruby: 2.7 , python: "3.7" , python_architecture: x64 , venv: "" }
|
|
58
|
+
- { os: ubuntu-20.04 , ruby: 2.7 , python: "3.6" , python_architecture: x64 , venv: "" }
|
|
59
|
+
- { os: ubuntu-18.04 , ruby: 2.7 , python: "3.8" , python_architecture: x64 , venv: "" }
|
|
60
|
+
|
|
61
|
+
# Ruby-debug with the latest Python 3
|
|
62
|
+
- { os: ubuntu-20.04 , ruby: debug , python: "3.x" , python_architecture: x64 , venv: "" }
|
|
63
|
+
|
|
64
|
+
# Ruby 3.1 with the latest Python 3 with venv
|
|
65
|
+
- { os: ubuntu-20.04 , ruby: "3.1" , python: "3.x" , python_architecture: x64 , venv: "venv:" }
|
|
66
|
+
- { os: ubuntu-18.04 , ruby: "3.1" , python: "3.x" , python_architecture: x64 , venv: "venv:" }
|
|
67
|
+
|
|
68
|
+
# Ubuntu 18.04 with venv
|
|
69
|
+
- { os: ubuntu-18.04 , ruby: "3.1" , python: "3.8" , python_architecture: x64 , venv: "venv:" }
|
|
70
|
+
|
|
71
|
+
# macOS with venv
|
|
72
|
+
- { os: macos-latest , ruby: "3.1" , python: "3.x" , python_architecture: x64 , venv: "venv:" }
|
|
73
|
+
- { os: macos-latest , ruby: "3.1" , python: "3.8" , python_architecture: x64 , venv: "venv:" }
|
|
74
|
+
|
|
75
|
+
#- { os: macos-latest , ruby: debug , python: "3.x" , python_architecture: x64 , venv: "" }
|
|
53
76
|
|
|
54
77
|
steps:
|
|
55
78
|
- uses: actions/checkout@v2
|
|
@@ -98,9 +121,9 @@ jobs:
|
|
|
98
121
|
- ubuntu-20.04
|
|
99
122
|
- macos-latest
|
|
100
123
|
ruby:
|
|
101
|
-
- "3.
|
|
124
|
+
- "3.1"
|
|
102
125
|
python:
|
|
103
|
-
- 3.
|
|
126
|
+
- "3.10"
|
|
104
127
|
|
|
105
128
|
steps:
|
|
106
129
|
- uses: actions/checkout@v2
|
data/CHANGES.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# The change history of PyCall
|
|
2
2
|
|
|
3
|
+
## 1.4.2
|
|
4
|
+
|
|
5
|
+
* Add supports of unary operators: `+@`, `-@`, `~`
|
|
6
|
+
* Fix `without_gvl` for exceptions occurred in the given block
|
|
7
|
+
* Add PyCall.setattr and PyCall.delattr
|
|
8
|
+
|
|
9
|
+
## 1.4.1
|
|
10
|
+
|
|
11
|
+
* Fix SEGV occurred on Windows
|
|
12
|
+
* Add PyCall.iterable
|
|
13
|
+
|
|
3
14
|
## 1.4.0
|
|
4
15
|
|
|
5
16
|
* Explicitly states that Windows is not supported yet in README
|
data/README.md
CHANGED
|
@@ -32,12 +32,6 @@ pyenv does not build the shared library in default, so you need to specify `--en
|
|
|
32
32
|
$ env PYTHON_CONFIGURE_OPTS='--enable-shared' pyenv install 3.7.2
|
|
33
33
|
```
|
|
34
34
|
|
|
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
35
|
## Installation
|
|
42
36
|
|
|
43
37
|
Add this line to your application's Gemfile:
|
|
@@ -59,9 +53,8 @@ Or install it yourself as:
|
|
|
59
53
|
Here is a simple example to call Python's `math.sin` function and compare it to
|
|
60
54
|
the `Math.sin` in Ruby:
|
|
61
55
|
|
|
62
|
-
require 'pycall
|
|
63
|
-
|
|
64
|
-
pyimport :math
|
|
56
|
+
require 'pycall'
|
|
57
|
+
math = PyCall.import_module("math")
|
|
65
58
|
math.sin(math.pi / 4) - Math.sin(Math::PI / 4) # => 0.0
|
|
66
59
|
|
|
67
60
|
Type conversions from Ruby to Python are automatically performed for numeric,
|
data/ext/pycall/gc.c
CHANGED
|
@@ -1,5 +1,74 @@
|
|
|
1
1
|
#include "pycall_internal.h"
|
|
2
2
|
|
|
3
|
+
struct gcguard {
|
|
4
|
+
st_table *guarded_objects;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
static int
|
|
8
|
+
gcguard_mark_i(st_data_t key, st_data_t val, st_data_t arg)
|
|
9
|
+
{
|
|
10
|
+
VALUE obj = (VALUE)val;
|
|
11
|
+
rb_gc_mark(obj);
|
|
12
|
+
return ST_CONTINUE;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static void
|
|
16
|
+
gcguard_mark(void* ptr)
|
|
17
|
+
{
|
|
18
|
+
struct gcguard *gg = (struct gcguard *)ptr;
|
|
19
|
+
st_foreach(gg->guarded_objects, gcguard_mark_i, 0);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static void
|
|
23
|
+
gcguard_free(void* ptr)
|
|
24
|
+
{
|
|
25
|
+
struct gcguard *gg = (struct gcguard *)ptr;
|
|
26
|
+
st_free_table(gg->guarded_objects);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static size_t
|
|
30
|
+
gcguard_memsize(const void* ptr)
|
|
31
|
+
{
|
|
32
|
+
const struct gcguard *gg = (const struct gcguard *)ptr;
|
|
33
|
+
return st_memsize(gg->guarded_objects);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static rb_data_type_t gcguard_data_type = {
|
|
37
|
+
"PyCall::gcguard",
|
|
38
|
+
{
|
|
39
|
+
gcguard_mark,
|
|
40
|
+
gcguard_free,
|
|
41
|
+
gcguard_memsize,
|
|
42
|
+
},
|
|
43
|
+
#ifdef RUBY_TYPED_FREE_IMMEDIATELY
|
|
44
|
+
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
|
45
|
+
#endif
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
static void
|
|
49
|
+
gcguard_aset(VALUE gcguard, PyObject *pyptr, VALUE rbobj)
|
|
50
|
+
{
|
|
51
|
+
struct gcguard *gg;
|
|
52
|
+
TypedData_Get_Struct(gcguard, struct gcguard, &gcguard_data_type, gg);
|
|
53
|
+
|
|
54
|
+
st_insert(gg->guarded_objects, (st_data_t)pyptr, (st_data_t)rbobj);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static void
|
|
58
|
+
gcguard_delete(VALUE gcguard, PyObject *pyptr)
|
|
59
|
+
{
|
|
60
|
+
if (rb_typeddata_is_kind_of(gcguard, &gcguard_data_type)) {
|
|
61
|
+
/* This check is necessary to avoid error on the process finalization phase */
|
|
62
|
+
struct gcguard *gg;
|
|
63
|
+
st_data_t key, val;
|
|
64
|
+
|
|
65
|
+
TypedData_Get_Struct(gcguard, struct gcguard, &gcguard_data_type, gg);
|
|
66
|
+
|
|
67
|
+
key = (st_data_t)pyptr;
|
|
68
|
+
st_delete(gg->guarded_objects, &key, &val);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
3
72
|
static ID id_gcguard_table;
|
|
4
73
|
static PyObject *weakref_callback_pyobj;
|
|
5
74
|
static PyObject *gcguard_weakref_destroyed(PyObject *self, PyObject *weakref);
|
|
@@ -21,15 +90,15 @@ gcguard_weakref_destroyed(PyObject *self, PyObject *weakref)
|
|
|
21
90
|
void
|
|
22
91
|
pycall_gcguard_aset(PyObject *pyobj, VALUE rbobj)
|
|
23
92
|
{
|
|
24
|
-
VALUE
|
|
25
|
-
|
|
93
|
+
VALUE gcguard = rb_ivar_get(mPyCall, id_gcguard_table);
|
|
94
|
+
gcguard_aset(gcguard, pyobj, rbobj);
|
|
26
95
|
}
|
|
27
96
|
|
|
28
97
|
void
|
|
29
98
|
pycall_gcguard_delete(PyObject *pyobj)
|
|
30
99
|
{
|
|
31
|
-
VALUE
|
|
32
|
-
|
|
100
|
+
VALUE gcguard = rb_ivar_get(mPyCall, id_gcguard_table);
|
|
101
|
+
gcguard_delete(gcguard, pyobj);
|
|
33
102
|
}
|
|
34
103
|
|
|
35
104
|
void
|
|
@@ -64,11 +133,21 @@ pycall_gcguard_register(PyObject *pyobj, VALUE obj)
|
|
|
64
133
|
pycall_gcguard_aset(wref, obj);
|
|
65
134
|
}
|
|
66
135
|
|
|
136
|
+
static VALUE
|
|
137
|
+
gcguard_new(void)
|
|
138
|
+
{
|
|
139
|
+
struct gcguard *gg;
|
|
140
|
+
VALUE obj = TypedData_Make_Struct(0, struct gcguard, &gcguard_data_type, gg);
|
|
141
|
+
gg->guarded_objects = st_init_numtable();
|
|
142
|
+
|
|
143
|
+
return obj;
|
|
144
|
+
}
|
|
145
|
+
|
|
67
146
|
void
|
|
68
147
|
pycall_init_gcguard(void)
|
|
69
148
|
{
|
|
70
149
|
id_gcguard_table = rb_intern("gcguard_table");
|
|
71
|
-
rb_ivar_set(mPyCall, id_gcguard_table,
|
|
150
|
+
rb_ivar_set(mPyCall, id_gcguard_table, gcguard_new());
|
|
72
151
|
|
|
73
152
|
weakref_callback_pyobj = Py_API(PyCFunction_NewEx)(&gcguard_weakref_callback_def, NULL, NULL);
|
|
74
153
|
}
|
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
|
{
|
|
@@ -1077,7 +1125,7 @@ pycall_pyobject_wrapper_wrapper_method(int argc, VALUE *argv, VALUE wrapper)
|
|
|
1077
1125
|
static VALUE
|
|
1078
1126
|
pycall_libpython_helpers_m_define_wrapper_method(VALUE mod, VALUE wrapper, VALUE name)
|
|
1079
1127
|
{
|
|
1080
|
-
VALUE pyptr
|
|
1128
|
+
VALUE pyptr;
|
|
1081
1129
|
PyObject *pyobj, *attr;
|
|
1082
1130
|
char *name_cstr;
|
|
1083
1131
|
|
|
@@ -1089,12 +1137,8 @@ pycall_libpython_helpers_m_define_wrapper_method(VALUE mod, VALUE wrapper, VALUE
|
|
|
1089
1137
|
pyobj = get_pyobj_ptr(pyptr);
|
|
1090
1138
|
|
|
1091
1139
|
if (RB_TYPE_P(name, T_SYMBOL)) {
|
|
1092
|
-
name_sym = name;
|
|
1093
1140
|
name = rb_sym_to_s(name);
|
|
1094
1141
|
}
|
|
1095
|
-
else if (RB_TYPE_P(name, T_STRING)) {
|
|
1096
|
-
name_sym = rb_str_intern(name);
|
|
1097
|
-
}
|
|
1098
1142
|
|
|
1099
1143
|
name_cstr = StringValueCStr(name);
|
|
1100
1144
|
if (name_cstr[RSTRING_LEN(name) - 1] == '=') {
|
|
@@ -1189,7 +1233,7 @@ pycall_libpython_helpers_m_setitem(VALUE mod, VALUE pyptr, VALUE key, VALUE v)
|
|
|
1189
1233
|
}
|
|
1190
1234
|
|
|
1191
1235
|
static VALUE
|
|
1192
|
-
pycall_libpython_helpers_m_delitem(VALUE mod, VALUE pyptr, VALUE key
|
|
1236
|
+
pycall_libpython_helpers_m_delitem(VALUE mod, VALUE pyptr, VALUE key)
|
|
1193
1237
|
{
|
|
1194
1238
|
PyObject *pyobj, *pyobj_key;
|
|
1195
1239
|
int res;
|
|
@@ -1202,7 +1246,7 @@ pycall_libpython_helpers_m_delitem(VALUE mod, VALUE pyptr, VALUE key, VALUE v)
|
|
|
1202
1246
|
pycall_pyerror_fetch_and_raise("PyObject_DelItem");
|
|
1203
1247
|
}
|
|
1204
1248
|
|
|
1205
|
-
return
|
|
1249
|
+
return Qnil;
|
|
1206
1250
|
}
|
|
1207
1251
|
|
|
1208
1252
|
static VALUE
|
|
@@ -2017,6 +2061,8 @@ pycall_pyerror_fetch_and_raise(char const *format, ...)
|
|
|
2017
2061
|
va_list args;
|
|
2018
2062
|
VALUE pyerror, msg;
|
|
2019
2063
|
|
|
2064
|
+
RBIMPL_NONNULL_ARG(format);
|
|
2065
|
+
|
|
2020
2066
|
pyerror = pycall_pyerror_fetch();
|
|
2021
2067
|
if (!NIL_P(pyerror))
|
|
2022
2068
|
rb_exc_raise(pyerror);
|
|
@@ -2074,11 +2120,24 @@ pycall_pystring_from_formatv(char const *format, va_list vargs)
|
|
|
2074
2120
|
|
|
2075
2121
|
/* ==== Python ==== */
|
|
2076
2122
|
|
|
2123
|
+
int
|
|
2124
|
+
pycall_PyObject_DelAttrString(PyObject *pyobj, const char *attr_name)
|
|
2125
|
+
{
|
|
2126
|
+
/* PyObject_DelAttrString is defined by using PyObject_SetAttrString in CPython's abstract.h */
|
|
2127
|
+
return Py_API(PyObject_SetAttrString)(pyobj, attr_name, NULL);
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2077
2130
|
static void
|
|
2078
2131
|
init_python(void)
|
|
2079
2132
|
{
|
|
2080
2133
|
static char const *argv[1] = { "" };
|
|
2081
2134
|
|
|
2135
|
+
/* optional functions */
|
|
2136
|
+
if (! Py_API(PyObject_DelAttrString)) {
|
|
2137
|
+
/* The case of PyObject_DelAttrString as a macro */
|
|
2138
|
+
Py_API(PyObject_DelAttrString) = pycall_PyObject_DelAttrString;
|
|
2139
|
+
}
|
|
2140
|
+
|
|
2082
2141
|
Py_API(Py_InitializeEx)(0);
|
|
2083
2142
|
Py_API(PySys_SetArgvEx)(0, (char **)argv, 0);
|
|
2084
2143
|
|
|
@@ -2236,7 +2295,7 @@ Init_pycall(void)
|
|
|
2236
2295
|
|
|
2237
2296
|
rb_define_module_function(mPyCall, "after_fork", pycall_after_fork, 0);
|
|
2238
2297
|
|
|
2239
|
-
pycall_tls_create(&without_gvl_key);
|
|
2298
|
+
pycall_tls_create((pycall_tls_key *)&without_gvl_key);
|
|
2240
2299
|
rb_define_module_function(mPyCall, "without_gvl", pycall_m_without_gvl, 0);
|
|
2241
2300
|
|
|
2242
2301
|
/* PyCall::PyPtr */
|
|
@@ -2301,6 +2360,8 @@ Init_pycall(void)
|
|
|
2301
2360
|
rb_define_module_function(mHelpers, "compare", pycall_libpython_helpers_m_compare, 3);
|
|
2302
2361
|
rb_define_module_function(mHelpers, "getattr", pycall_libpython_helpers_m_getattr, -1);
|
|
2303
2362
|
rb_define_module_function(mHelpers, "hasattr?", pycall_libpython_helpers_m_hasattr_p, 2);
|
|
2363
|
+
rb_define_module_function(mHelpers, "setattr", pycall_libpython_helpers_m_setattr, 3);
|
|
2364
|
+
rb_define_module_function(mHelpers, "delattr", pycall_libpython_helpers_m_delattr, 2);
|
|
2304
2365
|
rb_define_module_function(mHelpers, "callable?", pycall_libpython_helpers_m_callable_p, 1);
|
|
2305
2366
|
rb_define_module_function(mHelpers, "call_object", pycall_libpython_helpers_m_call_object, -1);
|
|
2306
2367
|
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
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module PyCall
|
|
2
|
+
class IterableWrapper
|
|
3
|
+
include Enumerable
|
|
4
|
+
|
|
5
|
+
def initialize(obj)
|
|
6
|
+
@obj = check_iterable(obj)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
private def check_iterable(obj)
|
|
10
|
+
unless PyCall.hasattr?(obj, :__iter__)
|
|
11
|
+
raise ArgumentError, "%p object is not iterable" % obj
|
|
12
|
+
end
|
|
13
|
+
obj
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def each
|
|
17
|
+
return enum_for(__method__) unless block_given?
|
|
18
|
+
iter = @obj.__iter__()
|
|
19
|
+
while true
|
|
20
|
+
begin
|
|
21
|
+
yield iter.__next__()
|
|
22
|
+
rescue PyCall::PyError => err
|
|
23
|
+
if err.type == PyCall.builtins.StopIteration
|
|
24
|
+
break
|
|
25
|
+
else
|
|
26
|
+
raise err
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
data/lib/pycall/version.rb
CHANGED
|
@@ -6,7 +6,12 @@ module PyCall
|
|
|
6
6
|
rescue
|
|
7
7
|
WMAP_SUPPORT_INT_KEY = false
|
|
8
8
|
else
|
|
9
|
-
|
|
9
|
+
case RUBY_PLATFORM
|
|
10
|
+
when /cygwin/, /mingw/, /mswin/
|
|
11
|
+
WMAP_SUPPORT_INT_KEY = false
|
|
12
|
+
else
|
|
13
|
+
WMAP_SUPPORT_INT_KEY = true
|
|
14
|
+
end
|
|
10
15
|
end
|
|
11
16
|
|
|
12
17
|
if WMAP_SUPPORT_INT_KEY
|
data/lib/pycall.rb
CHANGED
|
@@ -5,6 +5,7 @@ module PyCall
|
|
|
5
5
|
require 'pycall/pyobject_wrapper'
|
|
6
6
|
require 'pycall/pytypeobject_wrapper'
|
|
7
7
|
require 'pycall/pymodule_wrapper'
|
|
8
|
+
require 'pycall/iterable_wrapper'
|
|
8
9
|
require 'pycall/init'
|
|
9
10
|
|
|
10
11
|
module_function
|
|
@@ -58,6 +59,14 @@ module PyCall
|
|
|
58
59
|
LibPython::Helpers.hasattr?(obj.__pyptr__, name)
|
|
59
60
|
end
|
|
60
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
|
+
|
|
61
70
|
def same?(left, right)
|
|
62
71
|
case left
|
|
63
72
|
when PyObjectWrapper
|
|
@@ -73,6 +82,10 @@ module PyCall
|
|
|
73
82
|
LibPython::Helpers.import_module(name)
|
|
74
83
|
end
|
|
75
84
|
|
|
85
|
+
def iterable(obj)
|
|
86
|
+
IterableWrapper.new(obj)
|
|
87
|
+
end
|
|
88
|
+
|
|
76
89
|
def len(obj)
|
|
77
90
|
case obj
|
|
78
91
|
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.
|
|
4
|
+
version: 1.4.2
|
|
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-01-08 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -196,6 +196,7 @@ files:
|
|
|
196
196
|
- lib/pycall/import.rb
|
|
197
197
|
- lib/pycall/init.rb
|
|
198
198
|
- lib/pycall/iruby_helper.rb
|
|
199
|
+
- lib/pycall/iterable_wrapper.rb
|
|
199
200
|
- lib/pycall/libpython.rb
|
|
200
201
|
- lib/pycall/libpython/finder.rb
|
|
201
202
|
- lib/pycall/libpython/pyobject_struct.rb
|
|
@@ -235,7 +236,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
235
236
|
- !ruby/object:Gem::Version
|
|
236
237
|
version: '0'
|
|
237
238
|
requirements: []
|
|
238
|
-
rubygems_version: 3.
|
|
239
|
+
rubygems_version: 3.3.13
|
|
239
240
|
signing_key:
|
|
240
241
|
specification_version: 4
|
|
241
242
|
summary: pycall
|