On 2024/10/14 16:37, Yasuhito FUTATSUKI wrote:
> On 2024/10/14 12:37, Yasuhito FUTATSUKI wrote:
>> It seems it is caused by backward incompatible change of SWIG,
>> even it may be a bug.
>
> It turned out this is not a bug of SWIG, but intentional, for
> the purpose to fix another problem, incompatible change of
> SWIG_Ruby_AppendOutput, SWIG_Python_AppendOutput (and it affect
> the process how we construct result objects).
>
> Thanks,
SWIG 4.3.0-beta1 doesn't work with swig-rb and swig-py bindings, for our use
case that the svn_error_t * return value is not used as a return value on
success and is raised as an exception on error. Also, our %typemap(out) which
uses %append_output for swig-rb has issues.
I created patches and tested with SWIG 3.0.12, 4.2.1, 4.3.0-beta1.
- swig-4.3.0-swig-rb.patch.txt (swig-rb for trunk and 1.14.x)
- swig-4.3.0-swig...@trunk.patch.txt (swig-py for trunk)
- swig-4.3.0-swig...@1.14.x.patch.txt (swig-py for 1.14.x)
Could anybody please test the patches?
--
Jun Omae <jun6...@gmail.com> (大前 潤)
From 002741a54b88661268e2cc901a31835ebd696af4 Mon Sep 17 00:00:00 2001
From: Jun Omae <jun6...@gmail.com>
Date: Tue, 15 Oct 2024 03:46:54 +0900
Subject: [PATCH] Make swig-rb compatible with SWIG 4.3.0.
* subversion/bindings/swig/include/svn_containers.swg
(%typemap(out) apr_hash_t *PROPHASH,
%typemap(out) apr_hash_t *CHANGED_PATH_HASH,
%typemap(out) apr_array_header_t *PROP_LIST,
%typemap(out) apr_array_header_t *PROP_LIST_MAY_BE_NULL):
Set the return value to `$result` rather than using `%append_output` in
`%typemap(out)` for Ruby.
* subversion/bindings/swig/include/svn_types.swg
(%typemap(out) svn_error_t *):
Initialize `$result` with an empty array for the workaround to
`%append_output` incorrectly handling for nil and a list in Ruby.
(%typemap(ret) svn_error_t *):
Use first entry for `$result` when the size of the `$result` list is 1.
* subversion/bindings/swig/ruby/svn/core.rb
(Svn::Core::RangeList.diff):
Revised because multiple values are correctly retrieved from SWIG methods
now.
* subversion/bindings/swig/svn_wc.i
(%typemap(ret) svn_error_t *err):
Added because `%typemap(out) svn_error_t *err` is defined.
---
.../bindings/swig/include/svn_containers.swg | 10 ++++------
.../bindings/swig/include/svn_types.swg | 19 +++++++++++++++++--
subversion/bindings/swig/ruby/svn/core.rb | 2 +-
subversion/bindings/swig/svn_wc.i | 2 ++
4 files changed, 24 insertions(+), 9 deletions(-)
diff --git a/subversion/bindings/swig/include/svn_containers.swg
b/subversion/bindings/swig/include/svn_containers.swg
index 47bc50a92bb8b..7dc3b0dbedea1 100644
--- a/subversion/bindings/swig/include/svn_containers.swg
+++ b/subversion/bindings/swig/include/svn_containers.swg
@@ -310,7 +310,7 @@
%typemap(out) apr_hash_t *PROPHASH
{
- %append_output(svn_swig_rb_apr_hash_to_hash_svn_string($1));
+ $result = svn_swig_rb_apr_hash_to_hash_svn_string($1);
}
#endif
@@ -326,10 +326,8 @@
#ifdef SWIGRUBY
%typemap(out) apr_hash_t *CHANGED_PATH_HASH
{
- VALUE rb_changed_path_hash;
- rb_changed_path_hash =
+ $result =
svn_swig_rb_apr_hash_to_hash_swig_type($1, "svn_log_changed_path_t *");
- %append_output(rb_changed_path_hash);
}
%apply apr_hash_t *CHANGED_PATH_HASH {
@@ -760,7 +758,7 @@
%typemap(out) apr_array_header_t *PROP_LIST
{
- %append_output(svn_swig_rb_prop_apr_array_to_hash_prop($1));
+ $result = svn_swig_rb_prop_apr_array_to_hash_prop($1);
}
%typemap(in) apr_array_header_t *PROP_LIST_MAY_BE_NULL
@@ -778,7 +776,7 @@
%typemap(out) apr_array_header_t *PROP_LIST_MAY_BE_NULL
{
- %append_output($1 ? svn_swig_rb_prop_apr_array_to_hash_prop($1) : Qnil);
+ $result = $1 ? svn_swig_rb_prop_apr_array_to_hash_prop($1) : Qnil;
}
%apply apr_array_header_t *PROP_LIST {
diff --git a/subversion/bindings/swig/include/svn_types.swg
b/subversion/bindings/swig/include/svn_types.swg
index d251e34129f57..07d4c85dec683 100644
--- a/subversion/bindings/swig/include/svn_types.swg
+++ b/subversion/bindings/swig/include/svn_types.swg
@@ -532,14 +532,29 @@ svn_ ## TYPE ## _swig_rb_closed(VALUE self)
#endif
#ifdef SWIGRUBY
-%typemap(out) svn_error_t *
+%typemap(out) svn_error_t * (VALUE *svn_presult = NULL)
{
if ($1) {
svn_swig_rb_destroy_pool(_global_svn_swig_rb_pool);
svn_swig_rb_pop_pool(_global_svn_swig_rb_pool);
svn_swig_rb_handle_svn_error($1);
}
- $result = Qnil;
+ $result = rb_ary_new();
+ svn_presult = &$result;
+}
+
+%typemap(ret) svn_error_t *
+{
+ if (TYPE(*svn_presult) == T_ARRAY) {
+ switch (rb_array_len(*svn_presult)) {
+ case 0:
+ *svn_presult = Qnil;
+ break;
+ case 1:
+ *svn_presult = rb_ary_entry(*svn_presult, 0);
+ break;
+ }
+ }
}
#endif
diff --git a/subversion/bindings/swig/ruby/svn/core.rb
b/subversion/bindings/swig/ruby/svn/core.rb
index 26e5e84d4fba9..ff32926066cc0 100644
--- a/subversion/bindings/swig/ruby/svn/core.rb
+++ b/subversion/bindings/swig/ruby/svn/core.rb
@@ -812,7 +812,7 @@ def initialize(*ranges)
def diff(to, consider_inheritance=false)
result = Core.rangelist_diff(self, to, consider_inheritance)
deleted = result.pop
- added = result
+ added = result.pop
[added, deleted].collect do |result|
self.class.new(*result)
end
diff --git a/subversion/bindings/swig/svn_wc.i
b/subversion/bindings/swig/svn_wc.i
index cee5f7e486727..1d7b461fa008e 100644
--- a/subversion/bindings/swig/svn_wc.i
+++ b/subversion/bindings/swig/svn_wc.i
@@ -242,6 +242,8 @@
{
$result = $1 ? svn_swig_rb_svn_error_to_rb_error($1) : Qnil;
}
+
+%typemap(ret) svn_error_t *err "";
#endif
From 6077352fcc70302bc83de0e3fc995da44afeadee Mon Sep 17 00:00:00 2001
From: Jun Omae <jun6...@gmail.com>
Date: Mon, 14 Oct 2024 18:15:50 +0900
Subject: [PATCH] Make swig-py compatible with SWIG 4.3.0.
* subversion/bindings/swig/include/svn_types.swg
(%typemap(out) svn_error_t *):
Initialize `$result` with an empty list for the workaround to
`%append_output` incorrectly handling for None and a list in Python.
(%typemap(ret) svn_error_t *):
Use first entry for `$result` when the size of the `$result` list is 1.
(%typemap(ret) svn_error_t * SVN_ERR_WITH_ATTRS):
Ditto.
* subversion/bindings/swig/python/svn/fs.py
(svn_fs_commit_txn):
Removed because multiple values are correctly retrieved from SWIG methods
now.
* subversion/bindings/swig/python/tests/client.py
(test_checkout, test_update4):
Check `TypeError` when a NULL pointer passed since SWIG 4.3.0.
* subversion/bindings/swig/python/tests/core.py
(test_svn_rangelist_diff):
Added tests for the workaround for `%append_output` incorrectly handling.
---
.../bindings/swig/include/svn_types.swg | 33 +++++++++++++++----
subversion/bindings/swig/python/svn/fs.py | 28 ----------------
.../bindings/swig/python/tests/client.py | 8 +++--
subversion/bindings/swig/python/tests/core.py | 28 ++++++++++++++++
4 files changed, 61 insertions(+), 36 deletions(-)
diff --git a/subversion/bindings/swig/include/svn_types.swg
b/subversion/bindings/swig/include/svn_types.swg
index 45b2c0d4468b8..d251e34129f57 100644
--- a/subversion/bindings/swig/include/svn_types.swg
+++ b/subversion/bindings/swig/include/svn_types.swg
@@ -435,8 +435,8 @@ svn_ ## TYPE ## _swig_rb_closed(VALUE self)
svn_error_clear($1);
SWIG_fail;
}
- Py_INCREF(Py_None);
- $result = Py_None;
+ Py_XDECREF($result);
+ $result = PyList_New(0);
}
%typemap(out) svn_error_t * SVN_ERR_WITH_ATTRS (apr_status_t apr_err,
@@ -470,11 +470,31 @@ svn_ ## TYPE ## _swig_rb_closed(VALUE self)
SWIG_fail;
}
}
- else
- {
- Py_INCREF(Py_None);
- $result = Py_None;
+ Py_XDECREF($result);
+ $result = PyList_New(0);
+}
+
+%typemap(ret) svn_error_t * {
+ if ($result == NULL) {
+ $result = Py_None;
+ Py_INCREF($result);
+ }
+ else {
+ switch (PyList_Size($result)) {
+ case 0:
+ $result = Py_None;
+ Py_INCREF($result);
+ break;
+ case 1:
+ {
+ PyObject *tmp = $result;
+ $result = PyList_GetItem(tmp, 0);
+ Py_INCREF($result);
+ Py_DECREF(tmp);
+ }
+ break;
}
+ }
}
%typemap(ret) svn_error_t * SVN_ERR_WITH_ATTRS {
@@ -486,6 +506,7 @@ svn_ ## TYPE ## _swig_rb_closed(VALUE self)
Py_XDECREF($result);
SWIG_fail;
}
+ $typemap(ret, svn_error_t *);
}
#endif
diff --git a/subversion/bindings/swig/python/svn/fs.py
b/subversion/bindings/swig/python/svn/fs.py
index 8e75bff10cb28..08439c752a961 100644
--- a/subversion/bindings/swig/python/svn/fs.py
+++ b/subversion/bindings/swig/python/svn/fs.py
@@ -25,34 +25,6 @@
import errno
from libsvn.fs import *
-
-######################################################################
-# Any adjustment of SWIG generated wrapper functions on svn.fs module
-# should be placed before adding alternative names for them.
-
-# fs.commit_txn() should return a 2-tupple of conflict_p and new_rev.
-# However if conflict_p is None, SWIG generated wrapper function
-# libsvn.fs.svn_fs_commit_txn returns an integer value of new_rev.
-# This is a bug of SWIG generated wrapper function, so we fix it here.
-#
-# (There was discussion about this behavior because C API
-# svn_fs_commit_txn() always set NULL to conflict_p if it returns
-# SVN_NO_ERROR and so it seems to be reasonable that fs.commit_txn()
-# returns only rev_new value. However for compatibility, we decided
-# that fs.commit_txn always returns 2-tuple if it does not raises
-# an exception.)
-
-_svn_fs_commit_txn = svn_fs_commit_txn
-
-def svn_fs_commit_txn(*args):
- r"""svn_fs_commit_txn(svn_fs_txn_t txn, apr_pool_t pool) -> svn_error_t"""
- ret = _svn_fs_commit_txn(*args)
- if not isinstance(ret, tuple):
- ret = (None, ret)
- return ret
-
-######################################################################
-
from svn.core import _unprefix_names, Pool, _as_list
_unprefix_names(locals(), 'svn_fs_')
_unprefix_names(locals(), 'SVN_FS_')
diff --git a/subversion/bindings/swig/python/tests/client.py
b/subversion/bindings/swig/python/tests/client.py
index 258d9d3bf5982..ced58ce540b54 100644
--- a/subversion/bindings/swig/python/tests/client.py
+++ b/subversion/bindings/swig/python/tests/client.py
@@ -172,7 +172,9 @@ def test_checkout(self):
path = self.temper.alloc_empty_dir('-checkout')
- self.assertRaises(ValueError, client.checkout2,
+ # TypeError is raised since SWIG 4.3.0
+ self.assertRaises((ValueError, TypeError), r'Received a NULL pointer',
+ client.checkout2,
self.repos_uri, path, None, None, True, True,
self.client_ctx)
@@ -579,7 +581,9 @@ def test_update4(self):
path = self.temper.alloc_empty_dir('-update')
- self.assertRaises(ValueError, client.checkout2,
+ # TypeError is raised since SWIG 4.3.0
+ self.assertRaises((ValueError, TypeError), r'Received a NULL pointer',
+ client.checkout2,
self.repos_uri, path, None, None, True, True,
self.client_ctx)
diff --git a/subversion/bindings/swig/python/tests/core.py
b/subversion/bindings/swig/python/tests/core.py
index aa3bdc9177981..91e7ea9005a30 100644
--- a/subversion/bindings/swig/python/tests/core.py
+++ b/subversion/bindings/swig/python/tests/core.py
@@ -348,6 +348,34 @@ def test_stream_from_stringbuf_unicode(self):
finally:
svn.core.svn_stream_close(stream)
+ def test_svn_rangelist_diff(self):
+ """
+ SWIG incorrectly handles return values when the first %append_output() is
+ invoked with a list instance. svn.core.svn_rangelist_diff() is in the case.
+ We test whether the workaround for it is working.
+ """
+
+ def from_args(start, end, inheritable):
+ instance = svn.core.svn_merge_range_t()
+ instance.start = start
+ instance.end = end
+ instance.inheritable = inheritable
+ return instance
+
+ def to_args(instance):
+ return [instance.start, instance.end, instance.inheritable]
+
+ def map_list(f, iterator):
+ return list(map(f, iterator))
+
+ from_ = [from_args(4, 5, True), from_args(9, 13, True)]
+ to = [from_args(7, 11, True)]
+ rv = svn.core.svn_rangelist_diff(from_, to, True)
+ self.assertIsInstance(rv, (list, tuple))
+ deleted, added = rv
+ self.assertEqual([[7, 9, True]], map_list(to_args, added))
+ self.assertEqual([[4, 5, True], [11, 13, True]],map_list(to_args, deleted))
+
def suite():
return unittest.defaultTestLoader.loadTestsFromTestCase(
From e17b4799d40c0494f7535ba7fad19df944997c2f Mon Sep 17 00:00:00 2001
From: Jun Omae <jun6...@gmail.com>
Date: Mon, 14 Oct 2024 18:15:50 +0900
Subject: [PATCH] Make swig-py compatible with SWIG 4.3.0.
* subversion/bindings/swig/include/svn_types.swg
(%typemap(out) svn_error_t *):
Initialize `$result` with an empty list for the workaround to
`%append_output` incorrectly handling for None and a list in Python.
(%typemap(ret) svn_error_t *):
Use first entry for `$result` when the size of the `$result` list is 1.
(%typemap(ret) svn_error_t * SVN_ERR_WITH_ATTRS):
Ditto.
* subversion/bindings/swig/python/tests/client.py
(test_checkout, test_update4):
Check `TypeError` when a NULL pointer passed since SWIG 4.3.0.
* subversion/bindings/swig/python/tests/core.py
(test_svn_rangelist_diff):
Added tests for the workaround for `%append_output` incorrectly handling.
---
.../bindings/swig/include/svn_types.swg | 27 ++++++++++++++++--
.../bindings/swig/python/tests/client.py | 8 ++++--
subversion/bindings/swig/python/tests/core.py | 28 +++++++++++++++++++
3 files changed, 59 insertions(+), 4 deletions(-)
diff --git a/subversion/bindings/swig/include/svn_types.swg
b/subversion/bindings/swig/include/svn_types.swg
index fa024a2335e41..4d23b07ff88de 100644
--- a/subversion/bindings/swig/include/svn_types.swg
+++ b/subversion/bindings/swig/include/svn_types.swg
@@ -435,8 +435,31 @@ svn_ ## TYPE ## _swig_rb_closed(VALUE self)
svn_error_clear($1);
SWIG_fail;
}
- Py_INCREF(Py_None);
- $result = Py_None;
+ Py_XDECREF($result);
+ $result = PyList_New(0);
+}
+
+%typemap(ret) svn_error_t * {
+ if ($result == NULL) {
+ $result = Py_None;
+ Py_INCREF($result);
+ }
+ else {
+ switch (PyList_Size($result)) {
+ case 0:
+ $result = Py_None;
+ Py_INCREF($result);
+ break;
+ case 1:
+ {
+ PyObject *tmp = $result;
+ $result = PyList_GetItem(tmp, 0);
+ Py_INCREF($result);
+ Py_DECREF(tmp);
+ }
+ break;
+ }
+ }
}
#endif
diff --git a/subversion/bindings/swig/python/tests/client.py
b/subversion/bindings/swig/python/tests/client.py
index 3d91d868427db..3f32c3d31cbd0 100644
--- a/subversion/bindings/swig/python/tests/client.py
+++ b/subversion/bindings/swig/python/tests/client.py
@@ -172,7 +172,9 @@ def test_checkout(self):
path = self.temper.alloc_empty_dir('-checkout')
- self.assertRaises(ValueError, client.checkout2,
+ # TypeError is raised since SWIG 4.3.0
+ self.assertRaises((ValueError, TypeError), r'Received a NULL pointer',
+ client.checkout2,
self.repos_uri, path, None, None, True, True,
self.client_ctx)
@@ -526,7 +528,9 @@ def test_update4(self):
path = self.temper.alloc_empty_dir('-update')
- self.assertRaises(ValueError, client.checkout2,
+ # TypeError is raised since SWIG 4.3.0
+ self.assertRaises((ValueError, TypeError), r'Received a NULL pointer',
+ client.checkout2,
self.repos_uri, path, None, None, True, True,
self.client_ctx)
diff --git a/subversion/bindings/swig/python/tests/core.py
b/subversion/bindings/swig/python/tests/core.py
index e48d493d1a265..26e2963df199f 100644
--- a/subversion/bindings/swig/python/tests/core.py
+++ b/subversion/bindings/swig/python/tests/core.py
@@ -333,6 +333,34 @@ def test_stream_readline(self):
[b'', 1])
svn.core.svn_stream_close(stream)
+ def test_svn_rangelist_diff(self):
+ """
+ SWIG incorrectly handles return values when the first %append_output() is
+ invoked with a list instance. svn.core.svn_rangelist_diff() is in the case.
+ We test whether the workaround for it is working.
+ """
+
+ def from_args(start, end, inheritable):
+ instance = svn.core.svn_merge_range_t()
+ instance.start = start
+ instance.end = end
+ instance.inheritable = inheritable
+ return instance
+
+ def to_args(instance):
+ return [instance.start, instance.end, instance.inheritable]
+
+ def map_list(f, iterator):
+ return list(map(f, iterator))
+
+ from_ = [from_args(4, 5, True), from_args(9, 13, True)]
+ to = [from_args(7, 11, True)]
+ rv = svn.core.svn_rangelist_diff(from_, to, True)
+ self.assertIsInstance(rv, (list, tuple))
+ deleted, added = rv
+ self.assertEqual([[7, 9, True]], map_list(to_args, added))
+ self.assertEqual([[4, 5, True], [11, 13, True]],map_list(to_args, deleted))
+
def suite():
return unittest.defaultTestLoader.loadTestsFromTestCase(