svn commit: r1922345 [2/10] - in /subversion/branches/pristine-checksum-kind: ./ .github/ build/ build/ac-macros/ build/cmake/ build/generator/ build/generator/swig/ build/generator/templates/ build/hudson/ contrib/client-side/ contrib/client-side/svn_...

[email protected]
Newsgroups gmane.comp.version-control.subversion.svn
Message-ID <[email protected]>
Modified: subversion/branches/pristine-checksum-kind/contrib/client-side/svn_load_dirs/svn_load_dirs.pl.in
URL: http://svn.apache.org/viewvc/subversion/branches/pristine-checksum-kind/contrib/client-side/svn_load_dirs/svn_load_dirs.pl.in?rev=1922345&r1=1922344&r2=1922345&view=diff
==============================================================================
--- subversion/branches/pristine-checksum-kind/contrib/client-side/svn_load_dirs/svn_load_dirs.pl.in (original)
+++ subversion/branches/pristine-checksum-kind/contrib/client-side/svn_load_dirs/svn_load_dirs.pl.in Fri Dec  6 13:59:05 2024
@@ -1239,6 +1239,15 @@ while (defined (my $load_dir = &get_next
             # get the incorrect information.  So always append @BASE
             # and any preceding @'s will be treated normally and the
             # correct information will be retrieved.
+
+            # first make sure that the file has the eol-style property
+            # else svn 1.9 will error when it is not found
+            my @proplist = read_from_process($svn,
+                                             'proplist',
+                                             '--quiet',
+                                             "$upd_file\@BASE");
+            next unless grep(/svn:eol-style/, @proplist);
+
             my @command = ($svn,
                            'propget',
                            'svn:eol-style',

Modified: subversion/branches/pristine-checksum-kind/contrib/hook-scripts/check-mime-type.pl
URL: http://svn.apache.org/viewvc/subversion/branches/pristine-checksum-kind/contrib/hook-scripts/check-mime-type.pl?rev=1922345&r1=1922344&r2=1922345&view=diff
==============================================================================
--- subversion/branches/pristine-checksum-kind/contrib/hook-scripts/check-mime-type.pl (original)
+++ subversion/branches/pristine-checksum-kind/contrib/hook-scripts/check-mime-type.pl Fri Dec  6 13:59:05 2024
@@ -1,28 +1,36 @@
 #!/usr/bin/env perl
 
 # ====================================================================
-# commit-mime-type-check.pl: check that every added file has the
-# svn:mime-type property set and every added file with a mime-type
-# matching text/* also has svn:eol-style set. If any file fails this
-# test the user is sent a verbose error message suggesting solutions and
-# the commit is aborted.
+# check-mime-type.pl: check that every added or property-modified file
+# has the svn:mime-type property set and every added or property-modified
+# file with a mime-type matching text/* also has svn:eol-style set.
+# If any file fails this test the user is sent a verbose error message
+# suggesting solutions and the commit is aborted.
 #
-# Usage: commit-mime-type-check.pl REPOS TXN-NAME
+# Usage: check-mime-type.pl REPOS TXN-NAME
 # ====================================================================
-# Most of commit-mime-type-check.pl was taken from
+# Most of check-mime-type.pl was taken from
 # commit-access-control.pl, Revision 9986, 2004-06-14 16:29:22 -0400.
 # ====================================================================
-# Copyright (c) 2000-2004 CollabNet.  All rights reserved.
+# Copyright (c) 2000-2009 CollabNet.  All rights reserved.
+# Copyright (c) 2010-2020 Apache Software Foundation (ASF).
+# ====================================================================
+#    Licensed to the Apache Software Foundation (ASF) under one
+#    or more contributor license agreements.  See the NOTICE file
+#    distributed with this work for additional information
+#    regarding copyright ownership.  The ASF licenses this file
+#    to you under the Apache License, Version 2.0 (the
+#    "License"); you may not use this file except in compliance
+#    with the License.  You may obtain a copy of the License at
 #
-# This software is licensed as described in the file COPYING, which
-# you should have received as part of this distribution.  The terms
-# are also available at http://subversion.tigris.org/license.html.
-# If newer versions of this license are posted there, you may use a
-# newer version instead, at your option.
+#      http://www.apache.org/licenses/LICENSE-2.0
 #
-# This software consists of voluntary contributions made by many
-# individuals.  For exact contribution history, see the revision
-# history and logs, available at http://subversion.tigris.org/.
+#    Unless required by applicable law or agreed to in writing,
+#    software distributed under the License is distributed on an
+#    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#    KIND, either express or implied.  See the License for the
+#    specific language governing permissions and limitations
+#    under the License.
 # ====================================================================
 
 # Turn on warnings the best way depending on the Perl version.
@@ -40,6 +48,12 @@ use Carp;
 ######################################################################
 # Configuration section.
 
+# Toggle: Check files of mime-type text/* for svn:eol-style property.
+my $check_text_eol = 1;
+
+# Toggle: Check property-modified files too.
+my $check_prop_modified_files = 0;
+
 # Svnlook path.
 my $svnlook = "/usr/bin/svnlook";
 
@@ -100,19 +114,28 @@ my $tmp_dir = '/tmp';
 chdir($tmp_dir)
   or die "$0: cannot chdir `$tmp_dir': $!\n";
 
-# Figure out what files have added using svnlook.
-my @files_added;
+# Figure out what files have been added/property-modified using svnlook.
+my $regex_files_to_check;
+if ($check_prop_modified_files)
+  {
+    $regex_files_to_check = qr/^(?:A.|.U)  (.*[^\/])$/;
+  }
+else
+  {
+    $regex_files_to_check = qr/^A.  (.*[^\/])$/;
+  }
+my @files_to_check;
 foreach my $line (&read_from_process($svnlook, 'changed', $repos, '-t', $txn))
   {
-		# Add only files that were added to @files_added
-    if ($line =~ /^A.  (.*[^\/])$/)
+    # Add only files that were added/property-modified to @files_to_check
+    if ($line =~ /$regex_files_to_check/)
       {
-        push(@files_added, $1);
+        push(@files_to_check, $1);
       }
   }
 
 my @errors;
-foreach my $path ( @files_added )
+foreach my $path ( @files_to_check )
 	{
 		my $mime_type;
 		my $eol_style;
@@ -168,7 +191,7 @@ foreach my $path ( @files_added )
 			{
 				push @errors, "$path : svn:mime-type is not set";
 			}
-		elsif ($mime_type =~ /^text\// and not $eol_style)
+		elsif ($check_text_eol and $mime_type =~ /^text\// and not $eol_style)
 			{
 				push @errors, "$path : svn:mime-type=$mime_type but svn:eol-style is not set";
 			}
@@ -179,20 +202,30 @@ foreach my $path ( @files_added )
 # and will not see this verbose message more than once.
 if (@errors)
   {
+    my $addition1 = '';
+    my $addition2 = '';
+    my $addition3 = '';
+    if ($check_prop_modified_files)
+      {
+        $addition1 = '/property-modified';
+      }
+    if ($check_text_eol)
+      {
+        $addition2 = "    In addition text files must have the svn:eol-style property set.\n";
+        $addition3 = "    svn propset svn:eol-style native path/of/file\n";
+      }
     warn "$0:\n\n",
          join("\n", @errors), "\n\n",
-				 <<EOS;
-
-    Every added file must have the svn:mime-type property set. In
-    addition text files must have the svn:eol-style property set.
+                 <<"EOS";
 
+    Every added$addition1 file must have the svn:mime-type property set.
+$addition2
     For binary files try running
     svn propset svn:mime-type application/octet-stream path/of/file
 
     For text files try
     svn propset svn:mime-type text/plain path/of/file
-    svn propset svn:eol-style native path/of/file
-
+$addition3
     You may want to consider uncommenting the auto-props section
     in your ~/.subversion/config file. Read the Subversion book
     (https://svnbook.red-bean.com/), Chapter 7, Properties section,

Modified: subversion/branches/pristine-checksum-kind/contrib/server-side/fsfsverify.py
URL: http://svn.apache.org/viewvc/subversion/branches/pristine-checksum-kind/contrib/server-side/fsfsverify.py?rev=1922345&r1=1922344&r2=1922345&view=diff
==============================================================================
--- subversion/branches/pristine-checksum-kind/contrib/server-side/fsfsverify.py (original)
+++ subversion/branches/pristine-checksum-kind/contrib/server-side/fsfsverify.py Fri Dec  6 13:59:05 2024
@@ -726,7 +726,10 @@ class NodeRev(object):
         self.text = TextRep(rev, offset, length, size, digest,
                             contentType, currentRev, self, sha1, uniquifier)
       elif field == 'props':
-        (rev, offset, length, size, digest) = value.split(' ')
+        if len(value.split(' ')) == 5:
+          (rev, offset, length, size, digest) = value.split(' ')
+        else:
+          (rev, offset, length, size, digest, sha1, uniquifier) = value.split(' ')
         rev = int(rev)
         offset = int(offset)
         length = int(length)

Modified: subversion/branches/pristine-checksum-kind/gen-make.py
URL: http://svn.apache.org/viewvc/subversion/branches/pristine-checksum-kind/gen-make.py?rev=1922345&r1=1922344&r2=1922345&view=diff
==============================================================================
--- subversion/branches/pristine-checksum-kind/gen-make.py (original)
+++ subversion/branches/pristine-checksum-kind/gen-make.py Fri Dec  6 13:59:05 2024
@@ -47,6 +47,7 @@ sys.path.insert(0, os.path.join('build',
 sys.path.insert(1, 'build')
 
 gen_modules = {
+  'cmake' : ('gen_cmake', 'CMake build system'),
   'make' : ('gen_make', 'Makefiles for POSIX systems'),
   'vcproj' : ('gen_vcnet_vcproj', 'VC.Net project files'),
   }

Modified: subversion/branches/pristine-checksum-kind/subversion/bindings/javahl/README
URL: http://svn.apache.org/viewvc/subversion/branches/pristine-checksum-kind/subversion/bindings/javahl/README?rev=1922345&r1=1922344&r2=1922345&view=diff
==============================================================================
--- subversion/branches/pristine-checksum-kind/subversion/bindings/javahl/README (original)
+++ subversion/branches/pristine-checksum-kind/subversion/bindings/javahl/README Fri Dec  6 13:59:05 2024
@@ -39,7 +39,7 @@ jar file with --with-junit when running
 has been tested.  JUnit can be downloaded from https://junit.org/ .)
 
 
-MacOS X:
+MacOS:
 
 After building libsvnjavahl.dylib, you must rename it to
 libsvnjavahl.jnilib. Make install-javahl creates a symbolic

Modified: subversion/branches/pristine-checksum-kind/subversion/bindings/swig/INSTALL
URL: http://svn.apache.org/viewvc/subversion/branches/pristine-checksum-kind/subversion/bindings/swig/INSTALL?rev=1922345&r1=1922344&r2=1922345&view=diff
==============================================================================
--- subversion/branches/pristine-checksum-kind/subversion/bindings/swig/INSTALL (original)
+++ subversion/branches/pristine-checksum-kind/subversion/bindings/swig/INSTALL Fri Dec  6 13:59:05 2024
@@ -92,6 +92,9 @@ Step 1: [Optional] Install a suitable ve
         (See https://sourceforge.net/p/swig/news/2016/06/swig-3010-released/)
       - For Perl 5.16 and later, SWIG 2.0.8 or later is required.
       - For Ruby bindings, SWIG 3.0.8 is not supported.
+        (See https://github.com/swig/swig/issues/602)
+      - For Ruby bindings, SWIG 4.2.0 is broken. 
+        (See https://github.com/swig/swig/issues/2751)
 
     * Perhaps your distribution packages a suitable version of SWIG.
       If so, install it and skip to the last bullet point of this

Modified: subversion/branches/pristine-checksum-kind/subversion/bindings/swig/include/svn_containers.swg
URL: http://svn.apache.org/viewvc/subversion/branches/pristine-checksum-kind/subversion/bindings/swig/include/svn_containers.swg?rev=1922345&r1=1922344&r2=1922345&view=diff
==============================================================================
--- subversion/branches/pristine-checksum-kind/subversion/bindings/swig/include/svn_containers.swg (original)
+++ subversion/branches/pristine-checksum-kind/subversion/bindings/swig/include/svn_containers.swg Fri Dec  6 13:59:05 2024
@@ -299,7 +299,7 @@
   $1 = svn_swig_rb_hash_to_apr_hash_svn_string($input, _global_pool);
   _global_pool = NULL;
   if (!NIL_P(rb_pool)) {
-    if (NIL_P($1)) {
+    if ($1 == NULL) {
       svn_swig_rb_destroy_pool(rb_pool);
     } else {
       svn_swig_rb_set_pool_for_no_swig_type($input, rb_pool);
@@ -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 {
@@ -373,7 +371,7 @@
     svn_swig_rb_hash_to_apr_hash_string($input, _global_pool);
   _global_pool = NULL;
   if (!NIL_P(rb_pool)) {
-    if (NIL_P($1)) {
+    if ($1 == NULL) {
       svn_swig_rb_destroy_pool(rb_pool);
     } else {
       svn_swig_rb_set_pool_for_no_swig_type($input, rb_pool);
@@ -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 {

Modified: subversion/branches/pristine-checksum-kind/subversion/bindings/swig/include/svn_types.swg
URL: http://svn.apache.org/viewvc/subversion/branches/pristine-checksum-kind/subversion/bindings/swig/include/svn_types.swg?rev=1922345&r1=1922344&r2=1922345&view=diff
==============================================================================
--- subversion/branches/pristine-checksum-kind/subversion/bindings/swig/include/svn_types.swg (original)
+++ subversion/branches/pristine-checksum-kind/subversion/bindings/swig/include/svn_types.swg Fri Dec  6 13:59:05 2024
@@ -435,8 +435,8 @@ svn_ ## TYPE ## _swig_rb_closed(VALUE se
             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 se
             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 se
         Py_XDECREF($result);
         SWIG_fail;
       }
+    $typemap(ret, svn_error_t *);
 }
 #endif
 
@@ -511,14 +532,29 @@ svn_ ## TYPE ## _swig_rb_closed(VALUE se
 #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
 
@@ -1262,7 +1298,7 @@ svn_ ## TYPE ## _swig_rb_closed(VALUE se
   if (NIL_P($input)) {
     $1 = NULL;
   } else if (RSTRING_LEN($input) != APR_MD5_DIGESTSIZE) {
-    rb_raise(rb_eArgError, "digest size (%d) must be %d",
+    rb_raise(rb_eArgError, "digest size (%" PRIuSIZE ") must be %d",
              RSTRING_LEN($input), APR_MD5_DIGESTSIZE);
   } else {
     $1 = ($1_ltype)StringValuePtr($input);

Modified: subversion/branches/pristine-checksum-kind/subversion/bindings/swig/perl/native/t/5delta-compat.t
URL: http://svn.apache.org/viewvc/subversion/branches/pristine-checksum-kind/subversion/bindings/swig/perl/native/t/5delta-compat.t?rev=1922345&r1=1922344&r2=1922345&view=diff
==============================================================================
--- subversion/branches/pristine-checksum-kind/subversion/bindings/swig/perl/native/t/5delta-compat.t (original)
+++ subversion/branches/pristine-checksum-kind/subversion/bindings/swig/perl/native/t/5delta-compat.t Fri Dec  6 13:59:05 2024
@@ -43,3 +43,4 @@ SVN::TxDelta::send_txstream($txstream, @
 # TEST
 is($result, $tgttext, 'delta self test');
 
+close $aresult;

Modified: subversion/branches/pristine-checksum-kind/subversion/bindings/swig/perl/native/t/5delta.t
URL: http://svn.apache.org/viewvc/subversion/branches/pristine-checksum-kind/subversion/bindings/swig/perl/native/t/5delta.t?rev=1922345&r1=1922344&r2=1922345&view=diff
==============================================================================
--- subversion/branches/pristine-checksum-kind/subversion/bindings/swig/perl/native/t/5delta.t (original)
+++ subversion/branches/pristine-checksum-kind/subversion/bindings/swig/perl/native/t/5delta.t Fri Dec  6 13:59:05 2024
@@ -45,3 +45,5 @@ is($result, $tgttext, 'delta self test')
 
 # TEST
 is("$md5", 'a22b3dadcbddac48d2f1eae3ec5fb86a', 'md5 matched');
+
+close $aresult;

Modified: subversion/branches/pristine-checksum-kind/subversion/bindings/swig/perl/native/t/6ra.t
URL: http://svn.apache.org/viewvc/subversion/branches/pristine-checksum-kind/subversion/bindings/swig/perl/native/t/6ra.t?rev=1922345&r1=1922344&r2=1922345&view=diff
==============================================================================
--- subversion/branches/pristine-checksum-kind/subversion/bindings/swig/perl/native/t/6ra.t (original)
+++ subversion/branches/pristine-checksum-kind/subversion/bindings/swig/perl/native/t/6ra.t Fri Dec  6 13:59:05 2024
@@ -278,7 +278,9 @@ sub apply_textdelta {
         or die "error opening in-memory file to store Subversion update: $!";
     open my $in_fh, '<', \''
         or die "error opening in-memory file for delta source: $!";
-    return [ SVN::TxDelta::apply($in_fh, $out_fh, undef, "$baton", $pool) ];
+    my $result = [SVN::TxDelta::apply($in_fh, $out_fh, undef, "$baton", $pool)];
+    close $out_fh;
+    return $result;
 }
 
 sub close_edit {

Modified: subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c
URL: http://svn.apache.org/viewvc/subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c?rev=1922345&r1=1922344&r2=1922345&view=diff
==============================================================================
--- subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c (original)
+++ subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c Fri Dec  6 13:59:05 2024
@@ -1783,44 +1783,97 @@ static svn_error_t *type_conversion_erro
 
 /*** Editor Wrapping ***/
 
-/* this baton is used for the editor, directory, and file batons. */
-typedef struct item_baton {
-  PyObject *editor;     /* the editor handling the callbacks */
-  PyObject *baton;      /* the dir/file baton (or NULL for edit baton) */
-  apr_pool_t *pool;     /* top-level pool */
-} item_baton;
-
-static item_baton *make_baton(apr_pool_t *pool,
-                              PyObject *editor,
-                              PyObject *baton)
-{
-  item_baton *newb = apr_palloc(pool, sizeof(*newb));
-
-  /* Note: We steal the caller's reference to 'baton'. */
-  Py_INCREF(editor);
-  newb->editor = editor;
-  newb->baton = baton;
-  newb->pool = pool;
+static PyObject *
+make_baton(apr_pool_t *pool, PyObject *parent, PyObject *baton)
+{
+  PyObject *newb;
 
+  newb = PyObject_CallMethod(parent, "make_decendant", "O&O",
+                             make_ob_pool, pool, baton);
+  /* We always borrow the reference in ancestor's dict during the C API
+     processing, so that we never leak the reference even the API aborted
+     by some error */
+  Py_XDECREF(newb);
   return newb;
 }
 
-static svn_error_t *close_baton(void *baton,
-                                const char *method)
+/* Get 'editor' and 'baton' from _ItemBaton instance. The caller
+   should be within a Python thread lock. */
+static svn_error_t *
+unwrap_item_baton(PyObject **editor, PyObject **baton, PyObject *item_baton)
 {
-  item_baton *ib = baton;
+  svn_error_t *err;
+
+  if ((*editor = PyObject_GetAttrString(item_baton, "editor")) == NULL)
+    {
+      err = callback_exception_error();
+      *baton = NULL;
+      goto finished;
+    }
+  if ((*baton = PyObject_GetAttrString(item_baton, "baton")) == NULL)
+    {
+      Py_CLEAR(*editor);
+      err = callback_exception_error();
+      goto finished;
+    }
+  err = SVN_NO_ERROR;
+ finished:
+  Py_XDECREF(*editor);
+  Py_XDECREF(*baton);
+  return err;
+}
+
+/* Get 'editor', 'baton', 'pool' from _ItemBaton instance. The caller
+   should be within a Python thread lock. */
+static svn_error_t *
+unwrap_item_baton_with_pool(PyObject **editor, PyObject **baton,
+                            PyObject **py_pool, PyObject *item_baton)
+{
+  svn_error_t *err;
+
+  if ((err = unwrap_item_baton(editor, baton, item_baton)) != SVN_NO_ERROR)
+    {
+      *py_pool = NULL;
+      goto finished;
+    }
+  if ((*py_pool = PyObject_GetAttrString(item_baton, "pool")) == NULL)
+    {
+      err = callback_exception_error();
+      *editor = NULL;
+      *baton = NULL;
+      goto finished;
+    }
+  err = SVN_NO_ERROR;
+ finished:
+  Py_XDECREF(*py_pool);
+  return err;
+}
+
+static svn_error_t *
+close_baton(void *baton, const char *method, svn_boolean_t without_item)
+{
+  PyObject *editor = NULL, *baton_item = NULL;
+  PyObject *ib = baton;
   PyObject *result;
   svn_error_t *err;
 
   svn_swig_py_acquire_py_lock();
 
+  if ((err = unwrap_item_baton(&editor, &baton_item, ib)) != SVN_NO_ERROR)
+    {
+      goto finished;
+    }
+  if (without_item)
+    {
+      baton_item = NULL;
+    }
   /* If there is no baton object, then it is an edit_baton, and we should
      not bother to pass an object. Note that we still shove a NULL onto
      the stack, but the format specified just won't reference it.  */
   /* ### python doesn't have 'const' on the method name and format */
-  if ((result = PyObject_CallMethod(ib->editor, (char *)method,
-                                    ib->baton ? (char *)"(O)" : NULL,
-                                    ib->baton)) == NULL)
+  if ((result = PyObject_CallMethod(editor, (char *)method,
+                                    baton_item ? (char *)"(O)" : NULL,
+                                    baton_item)) == NULL)
     {
       err = callback_exception_error();
       goto finished;
@@ -1829,19 +1882,24 @@ static svn_error_t *close_baton(void *ba
   /* there is no return value, so just toss this object (probably Py_None) */
   Py_DECREF(result);
 
-  /* Release the editor object */
-  Py_DECREF(ib->editor);
-
-  /* We're now done with the baton. Since there isn't really a free, all
-     we need to do is note that its objects are no longer referenced by
-     the baton.  */
-  Py_XDECREF(ib->baton);
-
-#ifdef SVN_DEBUG
-  ib->editor = ib->baton = NULL;
-#endif
-
-  err = SVN_NO_ERROR;
+  /* We're now done with the baton. Release it from ancestor's dict */
+  if (PyObject_HasAttrString(ib, "release_self"))
+    {
+      /* Get reference for ib, because following function call remove
+         ib object from ancestor's dict, which we borrow the reference */
+      Py_INCREF(ib);
+      result = PyObject_CallMethod(ib, "release_self", NULL, NULL);
+      /* Now we can release the reference safely */
+      Py_DECREF(ib);
+      if (result == NULL)
+        {
+          err = callback_exception_error();
+          goto finished;
+        }
+        /* there is no return value, so just toss this object
+           (probably Py_None) */
+        Py_DECREF(result);
+    }
 
  finished:
   svn_swig_py_release_py_lock();
@@ -1852,14 +1910,19 @@ static svn_error_t *set_target_revision(
                                         svn_revnum_t target_revision,
                                         apr_pool_t *pool)
 {
-  item_baton *ib = edit_baton;
+  PyObject *editor = NULL, *baton_item = NULL;
+  PyObject *ib = edit_baton;
   PyObject *result;
   svn_error_t *err;
 
   svn_swig_py_acquire_py_lock();
 
+  if ((err = unwrap_item_baton(&editor, &baton_item, ib)) != SVN_NO_ERROR)
+    {
+      goto finished;
+    }
   /* ### python doesn't have 'const' on the method name and format */
-  if ((result = PyObject_CallMethod(ib->editor, (char *)"set_target_revision",
+  if ((result = PyObject_CallMethod(editor, (char *)"set_target_revision",
                                     (char *)"l", target_revision)) == NULL)
     {
       err = callback_exception_error();
@@ -1880,14 +1943,19 @@ static svn_error_t *open_root(void *edit
                               apr_pool_t *dir_pool,
                               void **root_baton)
 {
-  item_baton *ib = edit_baton;
-  PyObject *result;
+  PyObject *editor = NULL, *baton_item = NULL;
+  PyObject *ib = edit_baton;
+  PyObject *result = NULL;
   svn_error_t *err;
 
   svn_swig_py_acquire_py_lock();
 
+  if ((err = unwrap_item_baton(&editor, &baton_item, ib)) != SVN_NO_ERROR)
+    {
+      goto finished;
+    }
   /* ### python doesn't have 'const' on the method name and format */
-  if ((result = PyObject_CallMethod(ib->editor, (char *)"open_root",
+  if ((result = PyObject_CallMethod(editor, (char *)"open_root",
                                     (char *)"lO&", base_revision,
                                     make_ob_pool, dir_pool)) == NULL)
     {
@@ -1895,11 +1963,15 @@ static svn_error_t *open_root(void *edit
       goto finished;
     }
 
-  /* make_baton takes our 'result' reference */
-  *root_baton = make_baton(dir_pool, ib->editor, result);
+  if ((*root_baton = make_baton(dir_pool, ib, result)) == NULL)
+    {
+      err = callback_exception_error();
+      goto finished;
+    }
   err = SVN_NO_ERROR;
 
  finished:
+  Py_XDECREF(result);
   svn_swig_py_release_py_lock();
   return err;
 }
@@ -1909,16 +1981,21 @@ static svn_error_t *delete_entry(const c
                                  void *parent_baton,
                                  apr_pool_t *pool)
 {
-  item_baton *ib = parent_baton;
+  PyObject *editor = NULL, *baton_item = NULL;
+  PyObject *ib = parent_baton;
   PyObject *result;
   svn_error_t *err;
 
   svn_swig_py_acquire_py_lock();
 
+  if ((err = unwrap_item_baton(&editor, &baton_item, ib)) != SVN_NO_ERROR)
+    {
+      goto finished;
+    }
   /* ### python doesn't have 'const' on the method name and format */
-  if ((result = PyObject_CallMethod(ib->editor, (char *)"delete_entry",
+  if ((result = PyObject_CallMethod(editor, (char *)"delete_entry",
                                     (char *)SVN_SWIG_BYTES_FMT "lOO&",
-                                    path, revision, ib->baton,
+                                    path, revision, baton_item,
                                     make_ob_pool, pool)) == NULL)
     {
       err = callback_exception_error();
@@ -1941,20 +2018,25 @@ static svn_error_t *add_directory(const
                                   apr_pool_t *dir_pool,
                                   void **child_baton)
 {
-  item_baton *ib = parent_baton;
-  PyObject *result;
+  PyObject *editor = NULL, *baton_item = NULL;
+  PyObject *ib = parent_baton;
+  PyObject *result = NULL;
   svn_error_t *err;
 
   svn_swig_py_acquire_py_lock();
 
+  if ((err = unwrap_item_baton(&editor, &baton_item, ib)) != SVN_NO_ERROR)
+    {
+      goto finished;
+    }
   /* ### python doesn't have 'const' on the method name and format */
-  if ((result = PyObject_CallMethod(ib->editor, (char *)"add_directory",
+  if ((result = PyObject_CallMethod(editor, (char *)"add_directory",
 #if IS_PY3
                                     (char *)"yOylO&",
 #else
                                     (char *)"sOslO&",
 #endif
-                                    path, ib->baton,
+                                    path, baton_item,
                                     copyfrom_path, copyfrom_revision,
                                     make_ob_pool, dir_pool)) == NULL)
     {
@@ -1962,11 +2044,15 @@ static svn_error_t *add_directory(const
       goto finished;
     }
 
-  /* make_baton takes our 'result' reference */
-  *child_baton = make_baton(dir_pool, ib->editor, result);
+  if ((*child_baton = make_baton(dir_pool, ib, result)) == NULL)
+    {
+      err = callback_exception_error();
+      goto finished;
+    }
   err = SVN_NO_ERROR;
 
  finished:
+  Py_XDECREF(result);
   svn_swig_py_release_py_lock();
   return err;
 }
@@ -1977,27 +2063,36 @@ static svn_error_t *open_directory(const
                                    apr_pool_t *dir_pool,
                                    void **child_baton)
 {
-  item_baton *ib = parent_baton;
-  PyObject *result;
+  PyObject *editor = NULL, *baton_item = NULL;
+  PyObject *ib = parent_baton;
+  PyObject *result = NULL;
   svn_error_t *err;
 
   svn_swig_py_acquire_py_lock();
 
+  if ((err = unwrap_item_baton(&editor, &baton_item, ib)) != SVN_NO_ERROR)
+    {
+      goto finished;
+    }
   /* ### python doesn't have 'const' on the method name and format */
-  if ((result = PyObject_CallMethod(ib->editor, (char *)"open_directory",
+  if ((result = PyObject_CallMethod(editor, (char *)"open_directory",
                                     (char *)SVN_SWIG_BYTES_FMT "OlO&",
-                                    path, ib->baton, base_revision,
+                                    path, baton_item, base_revision,
                                     make_ob_pool, dir_pool)) == NULL)
     {
       err = callback_exception_error();
       goto finished;
     }
 
-  /* make_baton takes our 'result' reference */
-  *child_baton = make_baton(dir_pool, ib->editor, result);
+  if ((*child_baton = make_baton(dir_pool, ib, result)) == NULL)
+    {
+      err = callback_exception_error();
+      goto finished;
+    }
   err = SVN_NO_ERROR;
 
  finished:
+  Py_XDECREF(result);
   svn_swig_py_release_py_lock();
   return err;
 }
@@ -2007,20 +2102,25 @@ static svn_error_t *change_dir_prop(void
                                     const svn_string_t *value,
                                     apr_pool_t *pool)
 {
-  item_baton *ib = dir_baton;
+  PyObject *editor = NULL, *baton_item = NULL;
+  PyObject *ib = dir_baton;
   PyObject *result;
   svn_error_t *err;
 
   svn_swig_py_acquire_py_lock();
 
+  if ((err = unwrap_item_baton(&editor, &baton_item, ib)) != SVN_NO_ERROR)
+    {
+      goto finished;
+    }
   /* ### python doesn't have 'const' on the method name and format */
-  if ((result = PyObject_CallMethod(ib->editor, (char *)"change_dir_prop",
+  if ((result = PyObject_CallMethod(editor, (char *)"change_dir_prop",
 #if IS_PY3
                                     (char *)"Oyy#O&",
 #else
                                     (char *)"Oss#O&",
 #endif
-                                    ib->baton, name,
+                                    baton_item, name,
                                     value ? value->data : NULL,
                                     (Py_ssize_t) (value ? value->len : 0),
                                     make_ob_pool, pool)) == NULL)
@@ -2041,7 +2141,7 @@ static svn_error_t *change_dir_prop(void
 static svn_error_t *close_directory(void *dir_baton,
                                     apr_pool_t *pool)
 {
-  return close_baton(dir_baton, "close_directory");
+  return close_baton(dir_baton, "close_directory", FALSE);
 }
 
 static svn_error_t *add_file(const char *path,
@@ -2051,20 +2151,25 @@ static svn_error_t *add_file(const char
                              apr_pool_t *file_pool,
                              void **file_baton)
 {
-  item_baton *ib = parent_baton;
-  PyObject *result;
+  PyObject *editor = NULL, *baton_item = NULL;
+  PyObject *ib = parent_baton;
+  PyObject *result = NULL;
   svn_error_t *err;
 
   svn_swig_py_acquire_py_lock();
 
+  if ((err = unwrap_item_baton(&editor, &baton_item, ib)) != SVN_NO_ERROR)
+    {
+      goto finished;
+    }
   /* ### python doesn't have 'const' on the method name and format */
-  if ((result = PyObject_CallMethod(ib->editor, (char *)"add_file",
+  if ((result = PyObject_CallMethod(editor, (char *)"add_file",
 #if IS_PY3
                                     (char *)"yOylO&",
 #else
                                     (char *)"sOslO&",
 #endif
-                                    path, ib->baton,
+                                    path, baton_item,
                                     copyfrom_path, copyfrom_revision,
                                     make_ob_pool, file_pool)) == NULL)
     {
@@ -2072,12 +2177,16 @@ static svn_error_t *add_file(const char
       goto finished;
     }
 
-  /* make_baton takes our 'result' reference */
-  *file_baton = make_baton(file_pool, ib->editor, result);
+  if ((*file_baton = make_baton(file_pool, ib, result)) == NULL)
+    {
+      err = callback_exception_error();
+      goto finished;
+    }
 
   err = SVN_NO_ERROR;
 
  finished:
+  Py_XDECREF(result);
   svn_swig_py_release_py_lock();
   return err;
 }
@@ -2088,27 +2197,36 @@ static svn_error_t *open_file(const char
                               apr_pool_t *file_pool,
                               void **file_baton)
 {
-  item_baton *ib = parent_baton;
-  PyObject *result;
+  PyObject *editor = NULL, *baton_item = NULL;
+  PyObject *ib = parent_baton;
+  PyObject *result = NULL;
   svn_error_t *err;
 
   svn_swig_py_acquire_py_lock();
 
+  if ((err = unwrap_item_baton(&editor, &baton_item, ib)) != SVN_NO_ERROR)
+    {
+      goto finished;
+    }
   /* ### python doesn't have 'const' on the method name and format */
-  if ((result = PyObject_CallMethod(ib->editor, (char *)"open_file",
+  if ((result = PyObject_CallMethod(editor, (char *)"open_file",
                                     (char *)SVN_SWIG_BYTES_FMT "OlO&",
-                                    path, ib->baton, base_revision,
+                                    path, baton_item, base_revision,
                                     make_ob_pool, file_pool)) == NULL)
     {
       err = callback_exception_error();
       goto finished;
     }
 
-  /* make_baton takes our 'result' reference */
-  *file_baton = make_baton(file_pool, ib->editor, result);
+  if ((*file_baton = make_baton(file_pool, ib, result)) == NULL)
+    {
+      err = callback_exception_error();
+      goto finished;
+    }
   err = SVN_NO_ERROR;
 
  finished:
+  Py_XDECREF(result);
   svn_swig_py_release_py_lock();
   return err;
 }
@@ -2116,12 +2234,19 @@ static svn_error_t *open_file(const char
 static svn_error_t *window_handler(svn_txdelta_window_t *window,
                                    void *baton)
 {
-  PyObject *handler = baton;
-  PyObject *result;
-  svn_error_t *err;
+  PyObject *editor = NULL, *handler = NULL;
+  PyObject *ib = baton;
+  PyObject *result = NULL;
+  int is_last_call = FALSE;
+  svn_error_t *err = SVN_NO_ERROR;
 
   svn_swig_py_acquire_py_lock();
 
+  if ((err = unwrap_item_baton(&editor, &handler, ib)) != SVN_NO_ERROR)
+    {
+      is_last_call = TRUE;
+      goto finished;
+    }
   if (window == NULL)
     {
       /* the last call; it closes the handler */
@@ -2129,9 +2254,8 @@ static svn_error_t *window_handler(svn_t
       /* invoke the handler with None for the window */
       /* ### python doesn't have 'const' on the format */
       result = PyObject_CallFunction(handler, (char *)"O", Py_None);
+      is_last_call = TRUE;
 
-      /* we no longer need to refer to the handler object */
-      Py_DECREF(handler);
     }
   else
     {
@@ -2144,14 +2268,40 @@ static svn_error_t *window_handler(svn_t
   if (result == NULL)
     {
       err = callback_exception_error();
+      is_last_call = TRUE;
       goto finished;
     }
-
-  /* there is no return value, so just toss this object (probably Py_None) */
-  Py_DECREF(result);
-  err = SVN_NO_ERROR;
+  else
+    {
+      /* there is no return value, so just toss this object
+         (probably Py_None) */
+      Py_DECREF(result);
+      err = SVN_NO_ERROR;
+    }
 
  finished:
+  if (is_last_call)
+    {
+      /* now we should release the handler object */
+      if (PyObject_HasAttrString(ib, "release_self"))
+        {
+          /* Get reference for ib, because following function call remove
+             ib object from ancestor's dict, which we borrow the reference */
+          Py_INCREF(ib);
+          result = PyObject_CallMethod(ib, "release_self", NULL, NULL);
+          /* Now we can release the reference safely */
+          Py_DECREF(ib);
+          if (result == NULL)
+            {
+              if (err == SVN_NO_ERROR)
+                {
+                  err = callback_exception_error();
+                }
+            }
+          Py_XDECREF(result);
+        }
+    }
+
   svn_swig_py_release_py_lock();
   return err;
 }
@@ -2162,20 +2312,25 @@ static svn_error_t *apply_textdelta(void
                                     svn_txdelta_window_handler_t *handler,
                                     void **h_baton)
 {
-  item_baton *ib = file_baton;
-  PyObject *result;
+  PyObject *editor = NULL, *baton_item = NULL;
+  PyObject *ib = file_baton;
+  PyObject *result = NULL;
   svn_error_t *err;
 
   svn_swig_py_acquire_py_lock();
 
+  if ((err = unwrap_item_baton(&editor, &baton_item, ib)) != SVN_NO_ERROR)
+    {
+      goto finished;
+    }
   /* ### python doesn't have 'const' on the method name and format */
-  if ((result = PyObject_CallMethod(ib->editor, (char *)"apply_textdelta",
+  if ((result = PyObject_CallMethod(editor, (char *)"apply_textdelta",
 #if IS_PY3
                                     (char *)"(Oy)",
 #else
                                     (char *)"(Os)",
 #endif
-                                    ib->baton,
+                                    baton_item,
                                     base_checksum)) == NULL)
     {
       err = callback_exception_error();
@@ -2187,22 +2342,26 @@ static svn_error_t *apply_textdelta(void
      in Python.  */
   if (result == Py_None)
     {
-      Py_DECREF(result);
-
       *handler = svn_delta_noop_window_handler;
       *h_baton = NULL;
     }
   else
     {
-      /* return the thunk for invoking the handler. the baton takes our
-         'result' reference, which is the handler. */
+      /* return the thunk for invoking the handler. the baton creates
+         new reference of our 'result' reference, which is the handler,
+         so we release it even if no error. */
       *handler = window_handler;
-      *h_baton = result;
+      if ((*h_baton = make_baton(pool, ib, result)) == NULL)
+        {
+          err = callback_exception_error();
+          goto finished;
+        }
     }
 
   err = SVN_NO_ERROR;
 
  finished:
+  Py_XDECREF(result);
   svn_swig_py_release_py_lock();
   return err;
 }
@@ -2212,20 +2371,25 @@ static svn_error_t *change_file_prop(voi
                                      const svn_string_t *value,
                                      apr_pool_t *pool)
 {
-  item_baton *ib = file_baton;
+  PyObject *editor = NULL, *baton_item = NULL;
+  PyObject *ib = file_baton;
   PyObject *result;
   svn_error_t *err;
 
   svn_swig_py_acquire_py_lock();
 
+  if ((err = unwrap_item_baton(&editor, &baton_item, ib)) != SVN_NO_ERROR)
+    {
+      goto finished;
+    }
   /* ### python doesn't have 'const' on the method name and format */
-  if ((result = PyObject_CallMethod(ib->editor, (char *)"change_file_prop",
+  if ((result = PyObject_CallMethod(editor, (char *)"change_file_prop",
 #if IS_PY3
                                     (char *)"Oyy#O&",
 #else
                                     (char *)"Oss#O&",
 #endif
-                                    ib->baton, name,
+                                    baton_item, name,
                                     value ? value->data : NULL,
                                     (Py_ssize_t) (value ? value->len : 0),
                                     make_ob_pool, pool)) == NULL)
@@ -2247,20 +2411,25 @@ static svn_error_t *close_file(void *fil
                                const char *text_checksum,
                                apr_pool_t *pool)
 {
-  item_baton *ib = file_baton;
+  PyObject *editor = NULL, *baton_item = NULL;
+  PyObject *ib = file_baton;
   PyObject *result;
   svn_error_t *err;
 
   svn_swig_py_acquire_py_lock();
 
+  if ((err = unwrap_item_baton(&editor, &baton_item, ib)) != SVN_NO_ERROR)
+    {
+      goto finished;
+    }
   /* ### python doesn't have 'const' on the method name and format */
-  if ((result = PyObject_CallMethod(ib->editor, (char *)"close_file",
+  if ((result = PyObject_CallMethod(editor, (char *)"close_file",
 #if IS_PY3
                                     (char *)"(Oy)",
 #else
                                     (char *)"(Os)",
 #endif
-                                    ib->baton,
+                                    baton_item,
                                     text_checksum)) == NULL)
     {
       err = callback_exception_error();
@@ -2270,14 +2439,24 @@ static svn_error_t *close_file(void *fil
   /* there is no return value, so just toss this object (probably Py_None) */
   Py_DECREF(result);
 
-  /* We're now done with the baton. Since there isn't really a free, all
-     we need to do is note that its objects are no longer referenced by
-     the baton.  */
-  Py_XDECREF(ib->baton);
-
-#ifdef SVN_DEBUG
-  ib->editor = ib->baton = NULL;
-#endif
+  /* We're now done with the baton. Release it from ancestor's dict */
+  if (PyObject_HasAttrString(ib, "release_self"))
+    {
+      /* Get reference for ib, because following function call remove
+         ib object from ancestor's dict, which we borrow the reference */
+      Py_INCREF(ib);
+      result = PyObject_CallMethod(ib, "release_self", NULL, NULL);
+      /* Now we can release the reference safely */
+      Py_DECREF(ib);
+      if (result == NULL)
+        {
+          err = callback_exception_error();
+          goto finished;
+        }
+        /* there is no return value, so just toss this object
+           (probably Py_None) */
+        Py_DECREF(result);
+    }
 
   err = SVN_NO_ERROR;
 
@@ -2289,18 +2468,16 @@ static svn_error_t *close_file(void *fil
 static svn_error_t *close_edit(void *edit_baton,
                                apr_pool_t *pool)
 {
-  return close_baton(edit_baton, "close_edit");
+  return close_baton(edit_baton, "close_edit", TRUE);
 }
 
 static svn_error_t *abort_edit(void *edit_baton,
                                apr_pool_t *pool)
 {
-  return close_baton(edit_baton, "abort_edit");
+  return close_baton(edit_baton, "abort_edit", TRUE);
 }
 
 void svn_swig_py_make_editor(const svn_delta_editor_t **editor,
-                             void **edit_baton,
-                             PyObject *py_editor,
                              apr_pool_t *pool)
 {
   svn_delta_editor_t *thunk_editor = svn_delta_default_editor(pool);
@@ -2321,7 +2498,6 @@ void svn_swig_py_make_editor(const svn_d
   thunk_editor->abort_edit = abort_edit;
 
   *editor = thunk_editor;
-  *edit_baton = make_baton(pool, py_editor, NULL);
 }
 
 
@@ -2331,14 +2507,19 @@ static svn_error_t *parse_fn3_magic_head
                                                   void *parse_baton,
                                                   apr_pool_t *pool)
 {
-  item_baton *ib = parse_baton;
+  PyObject *editor = NULL, *baton_item = NULL;
+  PyObject *ib = parse_baton;
   PyObject *result;
   svn_error_t *err;
 
   svn_swig_py_acquire_py_lock();
 
+  if ((err = unwrap_item_baton(&editor, &baton_item, ib)) != SVN_NO_ERROR)
+    {
+      goto finished;
+    }
   /* ### python doesn't have 'const' on the method name and format */
-  if ((result = PyObject_CallMethod(ib->editor, (char *)"magic_header_record",
+  if ((result = PyObject_CallMethod(editor, (char *)"magic_header_record",
                                     (char *)"lO&", version,
                                     make_ob_pool, pool)) == NULL)
     {
@@ -2360,14 +2541,19 @@ static svn_error_t *parse_fn3_uuid_recor
                                           void *parse_baton,
                                           apr_pool_t *pool)
 {
-  item_baton *ib = parse_baton;
+  PyObject *editor = NULL, *baton_item = NULL;
+  PyObject *ib = parse_baton;
   PyObject *result;
   svn_error_t *err;
 
   svn_swig_py_acquire_py_lock();
 
+  if ((err = unwrap_item_baton(&editor, &baton_item, ib)) != SVN_NO_ERROR)
+    {
+      goto finished;
+    }
   /* ### python doesn't have 'const' on the method name and format */
-  if ((result = PyObject_CallMethod(ib->editor, (char *)"uuid_record",
+  if ((result = PyObject_CallMethod(editor, (char *)"uuid_record",
                                     (char *)SVN_SWIG_BYTES_FMT "O&", uuid,
                                     make_ob_pool, pool)) == NULL)
     {
@@ -2390,14 +2576,19 @@ static svn_error_t *parse_fn3_new_revisi
                                                   void *parse_baton,
                                                   apr_pool_t *pool)
 {
-  item_baton *ib = parse_baton;
-  PyObject *result;
+  PyObject *editor = NULL, *baton_item = NULL;
+  PyObject *ib = parse_baton;
+  PyObject *result = NULL;
   PyObject *tmp;
   svn_error_t *err;
 
   svn_swig_py_acquire_py_lock();
 
-  if ((result = PyObject_CallMethod(ib->editor, (char *)"new_revision_record",
+  if ((err = unwrap_item_baton(&editor, &baton_item, ib)) != SVN_NO_ERROR)
+    {
+      goto finished;
+    }
+  if ((result = PyObject_CallMethod(editor, (char *)"new_revision_record",
                                    (char *)"O&O&",
                                    svn_swig_py_stringhash_to_dict, headers,
                                    make_ob_pool, pool)) == NULL) {
@@ -2405,11 +2596,15 @@ static svn_error_t *parse_fn3_new_revisi
       goto finished;
     }
 
-  /* make_baton takes our 'result' reference */
-  *revision_baton = make_baton(pool, ib->editor, result);
+  if ((*revision_baton = make_baton(pool, ib, result)) == NULL)
+    {
+      err = callback_exception_error();
+      goto finished;
+    }
   err = SVN_NO_ERROR;
 
  finished:
+  Py_XDECREF(result);
   svn_swig_py_release_py_lock();
   return err;
 }
@@ -2420,26 +2615,35 @@ static svn_error_t *parse_fn3_new_node_r
                                               void *revision_baton,
                                               apr_pool_t *pool)
 {
-  item_baton *ib = revision_baton;
-  PyObject *result;
+  PyObject *editor = NULL, *baton_item = NULL;
+  PyObject *ib = revision_baton;
+  PyObject *result = NULL;
   svn_error_t *err;
 
   svn_swig_py_acquire_py_lock();
 
-  if ((result = PyObject_CallMethod(ib->editor, (char *)"new_node_record",
+  if ((err = unwrap_item_baton(&editor, &baton_item, ib)) != SVN_NO_ERROR)
+    {
+      goto finished;
+    }
+  if ((result = PyObject_CallMethod(editor, (char *)"new_node_record",
                                    (char *)"O&OO&",
                                    svn_swig_py_stringhash_to_dict, headers,
-                                   ib->baton,
+                                   baton_item,
                                    make_ob_pool, pool)) == NULL) {
       err = callback_exception_error();
       goto finished;
     }
 
-  /* make_baton takes our 'result' reference */
-  *node_baton = make_baton(pool, ib->editor, result);
+  if ((*node_baton = make_baton(pool, ib, result)) == NULL)
+    {
+      err = callback_exception_error();
+      goto finished;
+    }
   err = SVN_NO_ERROR;
 
  finished:
+  Py_XDECREF(result);
   svn_swig_py_release_py_lock();
   return err;
 }
@@ -2449,20 +2653,25 @@ static svn_error_t *parse_fn3_set_revisi
                                                     const char *name,
                                                     const svn_string_t *value)
 {
-  item_baton *ib = revision_baton;
+  PyObject *editor = NULL, *baton_item = NULL;
+  PyObject *ib = revision_baton;
   PyObject *result;
   svn_error_t *err;
 
   svn_swig_py_acquire_py_lock();
 
+  if ((err = unwrap_item_baton(&editor, &baton_item, ib)) != SVN_NO_ERROR)
+    {
+      goto finished;
+    }
   /* ### python doesn't have 'const' on the method name and format */
-  if ((result = PyObject_CallMethod(ib->editor, (char *)"set_revision_property",
+  if ((result = PyObject_CallMethod(editor, (char *)"set_revision_property",
 #if IS_PY3
                                     (char *)"Oyy#",
 #else
                                     (char *)"Oss#",
 #endif
-                                    ib->baton, name,
+                                    baton_item, name,
                                     value ? value->data : NULL,
                                     (Py_ssize_t) (value ? value->len : 0)))
       == NULL)
@@ -2485,20 +2694,25 @@ static svn_error_t *parse_fn3_set_node_p
                                                 const char *name,
                                                 const svn_string_t *value)
 {
-  item_baton *ib = node_baton;
+  PyObject *editor = NULL, *baton_item = NULL;
+  PyObject *ib = node_baton;
   PyObject *result;
   svn_error_t *err;
 
   svn_swig_py_acquire_py_lock();
 
+  if ((err = unwrap_item_baton(&editor, &baton_item, ib)) != SVN_NO_ERROR)
+    {
+      goto finished;
+    }
   /* ### python doesn't have 'const' on the method name and format */
-  if ((result = PyObject_CallMethod(ib->editor, (char *)"set_node_property",
+  if ((result = PyObject_CallMethod(editor, (char *)"set_node_property",
 #if IS_PY3
                                     (char *)"Oyy#",
 #else
                                     (char *)"Oss#",
 #endif
-                                    ib->baton, name,
+                                    baton_item, name,
                                     value ? value->data : NULL,
                                     (Py_ssize_t) (value ? value->len : 0)))
       == NULL)
@@ -2520,16 +2734,21 @@ static svn_error_t *parse_fn3_set_node_p
 static svn_error_t *parse_fn3_delete_node_property(void *node_baton,
                                                    const char *name)
 {
-  item_baton *ib = node_baton;
+  PyObject *editor = NULL, *baton_item = NULL;
+  PyObject *ib = node_baton;
   PyObject *result;
   svn_error_t *err;
 
   svn_swig_py_acquire_py_lock();
 
+  if ((err = unwrap_item_baton(&editor, &baton_item, ib)) != SVN_NO_ERROR)
+    {
+      goto finished;
+    }
   /* ### python doesn't have 'const' on the method name and format */
-  if ((result = PyObject_CallMethod(ib->editor, (char *)"delete_node_property",
+  if ((result = PyObject_CallMethod(editor, (char *)"delete_node_property",
                                     (char *)"O" SVN_SWIG_BYTES_FMT,
-                                    ib->baton, name)) == NULL)
+                                    baton_item, name)) == NULL)
     {
       err = callback_exception_error();
       goto finished;
@@ -2547,15 +2766,20 @@ static svn_error_t *parse_fn3_delete_nod
 
 static svn_error_t *parse_fn3_remove_node_props(void *node_baton)
 {
-  item_baton *ib = node_baton;
+  PyObject *editor = NULL, *baton_item = NULL;
+  PyObject *ib = node_baton;
   PyObject *result;
   svn_error_t *err;
 
   svn_swig_py_acquire_py_lock();
 
+  if ((err = unwrap_item_baton(&editor, &baton_item, ib)) != SVN_NO_ERROR)
+    {
+      goto finished;
+    }
   /* ### python doesn't have 'const' on the method name and format */
-  if ((result = PyObject_CallMethod(ib->editor, (char *)"remove_node_props",
-                                    (char *)"(O)", ib->baton)) == NULL)
+  if ((result = PyObject_CallMethod(editor, (char *)"remove_node_props",
+                                    (char *)"(O)", baton_item)) == NULL)
     {
       err = callback_exception_error();
       goto finished;
@@ -2574,15 +2798,22 @@ static svn_error_t *parse_fn3_remove_nod
 static svn_error_t *parse_fn3_set_fulltext(svn_stream_t **stream,
                                            void *node_baton)
 {
-  item_baton *ib = node_baton;
+  PyObject *editor = NULL, *baton_item = NULL, *py_pool = NULL;
+  PyObject *ib = node_baton;
   PyObject *result = NULL;
+  apr_pool_t *pool;
   svn_error_t *err = SVN_NO_ERROR;
 
   svn_swig_py_acquire_py_lock();
 
+  if ((err = unwrap_item_baton_with_pool(&editor, &baton_item, &py_pool,
+                                         ib)) != SVN_NO_ERROR)
+    {
+      goto finished;
+    }
   /* ### python doesn't have 'const' on the method name and format */
-  if ((result = PyObject_CallMethod(ib->editor, (char *)"set_fulltext",
-                                    (char *)"(O)", ib->baton)) == NULL)
+  if ((result = PyObject_CallMethod(editor, (char *)"set_fulltext",
+                                    (char *)"(O)", baton_item)) == NULL)
     {
       err = callback_exception_error();
       goto finished;
@@ -2595,9 +2826,15 @@ static svn_error_t *parse_fn3_set_fullte
     }
   else
     {
+      if (svn_swig_ConvertPtrString(py_pool, (void **)&pool,
+                                    "apr_pool_t *") == -1)
+        {
+          err = type_conversion_error("apr_pool_t *");
+          goto finished;
+        }
       /* create a stream from the IO object. it will increment the
          reference on the 'result'. */
-      *stream = svn_swig_py_make_stream(result, ib->pool);
+      *stream = svn_swig_py_make_stream(result, pool);
       if (*stream == NULL)
         {
           err = callback_exception_error();
@@ -2614,19 +2851,26 @@ finished:
 }
 
 
-static svn_error_t *parse_fn3_apply_textdelta(svn_txdelta_window_handler_t *handler,
-                                              void **handler_baton,
-                                              void *node_baton)
+static svn_error_t *
+parse_fn3_apply_textdelta(svn_txdelta_window_handler_t *handler,
+                          void **handler_baton,
+                          void *node_baton)
 {
-  item_baton *ib = node_baton;
-  PyObject *result;
+  PyObject *editor = NULL, *baton_item = NULL, *py_pool = NULL;
+  PyObject *ib = node_baton;
+  PyObject *result = NULL;
   svn_error_t *err;
 
   svn_swig_py_acquire_py_lock();
 
+  if ((err = unwrap_item_baton_with_pool(&editor, &baton_item, &py_pool,
+                                         ib)) != SVN_NO_ERROR)
+    {
+      goto finished;
+    }
   /* ### python doesn't have 'const' on the method name and format */
-  if ((result = PyObject_CallMethod(ib->editor, (char *)"apply_textdelta",
-                                    (char *)"(O)", ib->baton)) == NULL)
+  if ((result = PyObject_CallMethod(editor, (char *)"apply_textdelta",
+                                    (char *)"(O)", baton_item)) == NULL)
     {
       err = callback_exception_error();
       goto finished;
@@ -2637,22 +2881,33 @@ static svn_error_t *parse_fn3_apply_text
      in Python.  */
   if (result == Py_None)
     {
-      Py_DECREF(result);
-
       *handler = svn_delta_noop_window_handler;
       *handler_baton = NULL;
     }
   else
     {
-      /* return the thunk for invoking the handler. the baton takes our
-         'result' reference, which is the handler. */
+      apr_pool_t *pool;
+      /* return the thunk for invoking the handler. the baton creates
+         new reference of our 'result' reference, which is the handler,
+         so we release it even if no error. */
       *handler = window_handler;
-      *handler_baton = result;
+      if (svn_swig_ConvertPtrString(py_pool, (void **)&pool,
+                                    "apr_pool_t *") == -1)
+        {
+          err = type_conversion_error("apr_pool_t *");
+          goto finished;
+        }
+      if ((*handler_baton = make_baton(pool, ib, result)) == NULL)
+        {
+          err = callback_exception_error();
+          goto finished;
+        }
     }
 
   err = SVN_NO_ERROR;
 
  finished:
+  Py_XDECREF(result);
   svn_swig_py_release_py_lock();
   return err;
 }
@@ -2660,13 +2915,13 @@ static svn_error_t *parse_fn3_apply_text
 
 static svn_error_t *parse_fn3_close_node(void *node_baton)
 {
-  return close_baton(node_baton, "close_node");
+  return close_baton(node_baton, "close_node", FALSE);
 }
 
 
 static svn_error_t *parse_fn3_close_revision(void *revision_baton)
 {
-  return close_baton(revision_baton, "close_revision");
+  return close_baton(revision_baton, "close_revision", FALSE);
 }
 
 
@@ -2686,26 +2941,11 @@ static const svn_repos_parse_fns3_t thun
     parse_fn3_close_revision
   };
 
-static apr_status_t
-svn_swig_py_parse_fns3_destroy(void *parse_baton)
-{
-  close_baton(parse_baton, "_close_dumpstream");
-  return APR_SUCCESS;
-}
-
 void svn_swig_py_make_parse_fns3(const svn_repos_parse_fns3_t **parse_fns3,
-                                 void **parse_baton,
-                                 PyObject *py_parse_fns3,
                                  apr_pool_t *pool)
 {
   *parse_fns3 = &thunk_parse_fns3_vtable;
-  *parse_baton = make_baton(pool, py_parse_fns3, NULL);
-
-  /* Dump stream vtable does not provide a method which is called right before
-     the end of the parsing (similar to close_edit/abort_edit in delta editor).
-     Thus, register a pool clean-up routine to release this parse baton. */
-  apr_pool_cleanup_register(pool, *parse_baton, svn_swig_py_parse_fns3_destroy,
-                            apr_pool_cleanup_null);
+  return;
 }
 
 
@@ -2951,7 +3191,7 @@ void svn_swig_py_notify_func(void *baton
   svn_swig_py_acquire_py_lock();
 
   /* As caller can't understand Python context and we can't notify if
-     Python call back function raise exception to caller, we must catch it
+     Python callback function raise exception to caller, we must catch it
      if it is occurred, and restore error indicator */
   PyErr_Fetch(&exc_type, &exc, &exc_traceback);
 
@@ -3001,7 +3241,7 @@ void svn_swig_py_notify_func2(void *bato
   svn_swig_py_acquire_py_lock();
 
   /* As caller can't understand Python context and we can't notify if
-     Python call back function raise exception to caller, we must catch it
+     Python callback function raise exception to caller, we must catch it
      if it is occurred, and restore error indicator */
   PyErr_Fetch(&exc_type, &exc, &exc_traceback);
 
@@ -3044,7 +3284,7 @@ void svn_swig_py_status_func(void *baton
   svn_swig_py_acquire_py_lock();
 
   /* As caller can't understand Python context and we can't notify if
-     Python call back function raise exception to caller, we must catch it
+     Python callback function raise exception to caller, we must catch it
      if it is occurred, and restore error indicator */
   PyErr_Fetch(&exc_type, &exc, &exc_traceback);
 
@@ -3193,7 +3433,7 @@ void svn_swig_py_status_func2(void *bato
   svn_swig_py_acquire_py_lock();
 
   /* As caller can't understand Python context and we can't notify if
-     Python call back function raise exception to caller, we must catch it
+     Python callback function raise exception to caller, we must catch it
      if it is occurred, and restore error indicator */
   PyErr_Fetch(&exc_type, &exc, &exc_traceback);
 
@@ -4478,7 +4718,7 @@ ra_callbacks_progress_func(apr_off_t pro
   svn_swig_py_acquire_py_lock();
 
   /* As caller can't understand Python context and we can't notify if
-     Python call back function raise exception to caller, we must catch it
+     Python callback function raise exception to caller, we must catch it
      if it is occurred, and restore error indicator */
   PyErr_Fetch(&exc_type, &exc, &exc_traceback);
 
@@ -5389,7 +5629,7 @@ svn_swig_py_config_enumerator2(const cha
   svn_swig_py_acquire_py_lock();
 
   /* As caller can't understand Python context and we can't notify if
-     Python call back function raise exception to caller, we must catch it
+     Python callback function raise exception to caller, we must catch it
      if it is occurred, and restore error indicator */
   PyErr_Fetch(&exc_type, &exc, &exc_traceback);
 
@@ -5447,7 +5687,7 @@ svn_swig_py_config_section_enumerator2(c
   svn_swig_py_acquire_py_lock();
 
   /* As caller can't understand Python context and we can't notify if
-     Python call back function raise exception to caller, we must catch it
+     Python callback function raise exception to caller, we must catch it
      if it is occurred, and restore error indicator */
   PyErr_Fetch(&exc_type, &exc, &exc_traceback);
 

Modified: subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h
URL: http://svn.apache.org/viewvc/subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h?rev=1922345&r1=1922344&r2=1922345&view=diff
==============================================================================
--- subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h (original)
+++ subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.h Fri Dec  6 13:59:05 2024
@@ -275,14 +275,10 @@ svn_swig_py_unwrap_struct_ptr(PyObject *
 
 /* make an editor that "thunks" from C callbacks up to Python */
 void svn_swig_py_make_editor(const svn_delta_editor_t **editor,
-                             void **edit_baton,
-                             PyObject *py_editor,
                              apr_pool_t *pool);
 
 /* make a parse vtable that "thunks" from C callbacks up to Python */
 void svn_swig_py_make_parse_fns3(const svn_repos_parse_fns3_t **parse_fns3,
-                                 void **parse_baton,
-                                 PyObject *py_parse_fns3,
                                  apr_pool_t *pool);
 
 apr_file_t *svn_swig_py_make_file(PyObject *py_file,

Modified: subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/svn/delta.py
URL: http://svn.apache.org/viewvc/subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/svn/delta.py?rev=1922345&r1=1922344&r2=1922345&view=diff
==============================================================================
--- subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/svn/delta.py (original)
+++ subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/svn/delta.py Fri Dec  6 13:59:05 2024
@@ -77,5 +77,6 @@ class Editor:
     pass
 
 
-def make_editor(editor, pool=None):
-  return svn_swig_py_make_editor(editor, pool)
+def make_editor(editor, pool=None, baton=None):
+  from libsvn.delta import _AncBaton
+  return svn_swig_py_make_editor(pool), _AncBaton(editor, pool, baton)

Modified: subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/svn/fs.py
URL: http://svn.apache.org/viewvc/subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/svn/fs.py?rev=1922345&r1=1922344&r2=1922345&view=diff
==============================================================================
--- subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/svn/fs.py (original)
+++ subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/svn/fs.py Fri Dec  6 13:59:05 2024
@@ -23,35 +23,8 @@
 #    under the License.
 ######################################################################
 
+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_')
@@ -158,6 +131,18 @@ class FileDiff:
     return self.tempfile1, self.tempfile2
 
   def get_pipe(self):
+    """Perform diff and return a file object from which the output can
+    be read.
+
+    When DIFFOPTIONS is None (the default), use svn's internal diff.
+
+    With any other DIFFOPTIONS, exec the external diff found on PATH,
+    passing it DIFFOPTIONS. On Windows, exec diff.exe rather than
+    diff. If a diff utility is not installed or found on PATH, throws
+    FileNotFoundError. Caveat: On some systems, including Windows, an
+    external diff may not be available unless installed and added to
+    PATH manually.
+    """
     self.get_files()
 
     # If diffoptions were provided, then the diff command needs to be
@@ -170,8 +155,17 @@ class FileDiff:
             + [self.tempfile1, self.tempfile2]
 
       # open the pipe, and return the file object for reading from the child.
-      p = _subprocess.Popen(cmd, stdout=_subprocess.PIPE, bufsize=-1,
-                            close_fds=_sys.platform != "win32")
+      try:
+        p = _subprocess.Popen(cmd, stdout=_subprocess.PIPE, bufsize=-1,
+                              close_fds=_sys.platform != "win32")
+      # When removing Python 2 support: Change to FileNotFoundError and 
+      # remove check for ENOENT (FileNotFoundError "Corresponds to errno
+      # ENOENT" according to documentation)
+      except OSError as err:
+        if err.errno == errno.ENOENT:
+          err.strerror = "External diff command not found in PATH"
+        raise err
+
       return _PopenStdoutWrapper(p)
 
     else:

Modified: subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/svn/repos.py
URL: http://svn.apache.org/viewvc/subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/svn/repos.py?rev=1922345&r1=1922344&r2=1922345&view=diff
==============================================================================
--- subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/svn/repos.py (original)
+++ subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/svn/repos.py Fri Dec  6 13:59:05 2024
@@ -336,5 +336,16 @@ class ParseFns3:
         pass
 
 
-def make_parse_fns3(parse_fns3, pool=None):
-    return svn_swig_py_make_parse_fns3(parse_fns3, pool)
+def make_parse_fns3(parse_fns3, pool=None, baton=None):
+  from libsvn.delta import _AncBaton
+
+  class _ParseBaton(_AncBaton):
+    # Drive _close_dumpstream method when the instance is deleted.
+    # For backward compatibility before Subversion 1.15, we call it even if
+    # the instance would not be used by C API, or the C API would cause
+    # some error.
+    def __del__(self):
+      self.editor._close_dumpstream()
+
+  parse_baton = _ParseBaton(parse_fns3, pool, baton)
+  return svn_swig_py_make_parse_fns3(pool), parse_baton

Modified: subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/tests/client.py
URL: http://svn.apache.org/viewvc/subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/tests/client.py?rev=1922345&r1=1922344&r2=1922345&view=diff
==============================================================================
--- subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/tests/client.py (original)
+++ subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/tests/client.py Fri Dec  6 13:59:05 2024
@@ -172,7 +172,9 @@ class SubversionClientTestCase(unittest.
 
     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 @@ class SubversionClientTestCase(unittest.
 
     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)
 

Modified: subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/tests/core.py
URL: http://svn.apache.org/viewvc/subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/tests/core.py?rev=1922345&r1=1922344&r2=1922345&view=diff
==============================================================================
--- subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/tests/core.py (original)
+++ subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/tests/core.py Fri Dec  6 13:59:05 2024
@@ -348,6 +348,35 @@ class SubversionCoreTestCase(unittest.Te
     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(

Modified: subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/tests/delta.py
URL: http://svn.apache.org/viewvc/subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/tests/delta.py?rev=1922345&r1=1922344&r2=1922345&view=diff
==============================================================================
--- subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/tests/delta.py (original)
+++ subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/tests/delta.py Fri Dec  6 13:59:05 2024
@@ -21,6 +21,7 @@
 import unittest, setup_path
 import os
 import tempfile
+import weakref
 import svn.delta
 import svn.core
 from sys import version_info # For Python version check
@@ -117,6 +118,19 @@ class DeltaTestCase(unittest.TestCase):
     # Check that the ops inherit the window's pool
     self.assertEqual(window.ops[0]._parent_pool, window._parent_pool)
 
+  def testMakeEditorLeak(self):
+    """Issue 4916, check ref-count leak on svn.delta.make_editor()"""
+    pool = svn.core.Pool()
+    editor = svn.delta.Editor()
+    editor_ref = weakref.ref(editor)
+    e_ptr, e_baton = svn.delta.make_editor(editor, pool)
+    del e_ptr, e_baton
+    self.assertNotEqual(editor_ref(), None)
+    del pool
+    self.assertNotEqual(editor_ref(), None)
+    del editor
+    self.assertEqual(editor_ref(), None)
+
 def suite():
   return unittest.defaultTestLoader.loadTestsFromTestCase(DeltaTestCase)
 

Modified: subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/tests/fs.py
URL: http://svn.apache.org/viewvc/subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/tests/fs.py?rev=1922345&r1=1922344&r2=1922345&view=diff
==============================================================================
--- subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/tests/fs.py (original)
+++ subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/tests/fs.py Fri Dec  6 13:59:05 2024
@@ -308,6 +308,9 @@ class SubversionFSTestCase(unittest.Test
     try:
       diffout, differr = Popen(["diff"], stdin=PIPE, stderr=PIPE).communicate()
 
+    # When removing Python 2 support: Change to FileNotFoundError and 
+    # remove check for ENOENT (FileNotFoundError "Corresponds to errno
+    # ENOENT" according to documentation)
     except OSError as err:
       if err.errno == errno.ENOENT:
         self.skipTest("'diff' command not present")

Modified: subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/tests/repository.py
URL: http://svn.apache.org/viewvc/subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/tests/repository.py?rev=1922345&r1=1922344&r2=1922345&view=diff
==============================================================================
--- subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/tests/repository.py (original)
+++ subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/tests/repository.py Fri Dec  6 13:59:05 2024
@@ -18,11 +18,11 @@
 # under the License.
 #
 #
-import unittest, setup_path, os, sys
+import unittest, setup_path, os, sys, weakref
 from sys import version_info # For Python version check
 from io import BytesIO
 from svn import core, repos, fs, delta
-from svn.core import SubversionException
+from svn.core import SubversionException, Pool
 import utils
 
 class ChangeReceiver(delta.Editor):
@@ -40,9 +40,20 @@ class ChangeReceiver(delta.Editor):
     return textdelta_handler
 
 class DumpStreamParser(repos.ParseFns3):
-  def __init__(self):
+  def __init__(self, stream=None, pool=None):
     repos.ParseFns3.__init__(self)
+    self.stream = stream
     self.ops = []
+    # for leak checking only. If the parse_fns3 object holds some proxy
+    # object allocated from 'pool' or the 'pool' itself, the 'pool' is not
+    # destroyed until the parse_fns3 object is removed.
+    self.pool = pool
+  def _close_dumpstream(self):
+    if self.stream:
+      self.stream.close()
+      self.stream = None
+    if self.pool:
+      self.pool = None
   def magic_header_record(self, version, pool=None):
     self.ops.append((b"magic-header", version))
   def uuid_record(self, uuid, pool=None):
@@ -74,6 +85,76 @@ class DumpStreamParser(repos.ParseFns3):
     self.ops.append((b"set-fulltext", node_baton[0], node_baton[1]))
     return None
 
+class BatonCollector(repos.ChangeCollector):
+  """A ChangeCollector with collecting batons, too"""
+  def __init__(self, fs_ptr, root, pool=None, notify_cb=None):
+    repos.ChangeCollector.__init__(self, fs_ptr, root, pool, notify_cb)
+    self.batons = []
+    self.close_called = False
+    self.abort_called = False
+
+  def open_root(self, base_revision, dir_pool=None):
+    bt = repos.ChangeCollector.open_root(self, base_revision, dir_pool)
+    self.batons.append((b'dir baton', b'', bt, sys.getrefcount(bt)))
+    return bt
+
+  def add_directory(self, path, parent_baton,
+                    copyfrom_path, copyfrom_revision, dir_pool=None):
+    bt = repos.ChangeCollector.add_directory(self, path, parent_baton,
+                                             copyfrom_path,
+                                             copyfrom_revision,
+                                             dir_pool)
+    self.batons.append((b'dir baton', path, bt, sys.getrefcount(bt)))
+    return bt
+
+  def open_directory(self, path, parent_baton, base_revision,
+                     dir_pool=None):
+    bt = repos.ChangeCollector.open_directory(self, path, parent_baton,
+                                              base_revision, dir_pool)
+    self.batons.append((b'dir baton', path, bt, sys.getrefcount(bt)))
+    return bt
+
+  def add_file(self, path, parent_baton,
+               copyfrom_path, copyfrom_revision, file_pool=None):
+    bt = repos.ChangeCollector.add_file(self, path, parent_baton,
+                                        copyfrom_path, copyfrom_revision,
+                                        file_pool)
+    self.batons.append((b'file baton', path, bt, sys.getrefcount(bt)))
+    return bt
+
+  def open_file(self, path, parent_baton, base_revision, file_pool=None):
+    bt = repos.ChangeCollector.open_file(self, path, parent_baton,
+                                         base_revision, file_pool)
+    self.batons.append((b'file baton', path, bt, sys.getrefcount(bt)))
+    return bt
+
+  def close_edit(self, pool=None):
+    self.close_called = True
+    return
+
+  def abort_edit(self, pool=None):
+    self.abort_called = True
+    return
+
+class BatonCollectorErrorOnClose(BatonCollector):
+  """Same as BatonCollector, but raises an Exception when close the
+     file/dir specified by error_path"""
+  def __init__(self, fs_ptr, root, pool=None, notify_cb=None, error_path=b''):
+    BatonCollector.__init__(self, fs_ptr, root, pool, notify_cb)
+    self.error_path = error_path
+
+  def close_directory(self, dir_baton):
+    if dir_baton[0] == self.error_path:
+      raise SubversionException('A Dummy Exception!', core.SVN_ERR_BASE)
+    else:
+      BatonCollector.close_directory(self, dir_baton)
+
+  def close_file(self, file_baton, text_checksum):
+    if file_baton[0] == self.error_path:
+      raise SubversionException('A Dummy Exception!', core.SVN_ERR_BASE)
+    else:
+      return BatonCollector.close_file(self, file_baton, text_checksum)
+
 
 def _authz_callback(root, path, pool):
   "A dummy authz callback which always returns success."
@@ -175,13 +256,15 @@ class SubversionRepositoryTestCase(unitt
     def is_cancelled():
       self.cancel_calls += 1
       return None
+    pool = Pool()
+    subpool = Pool(pool)
     dump_path = os.path.join(os.path.dirname(sys.argv[0]),
         "trac/versioncontrol/tests/svnrepos.dump")
     stream = open(dump_path, 'rb')
-    dsp = DumpStreamParser()
-    ptr, baton = repos.make_parse_fns3(dsp)
+    dsp = DumpStreamParser(stream, subpool)
+    dsp_ref = weakref.ref(dsp)
+    ptr, baton = repos.make_parse_fns3(dsp, subpool)
     repos.parse_dumpstream3(stream, ptr, baton, False, is_cancelled)
-    stream.close()
     self.assertEqual(self.cancel_calls, 76)
     expected_list = [
         (b"magic-header", 2),
@@ -226,6 +309,13 @@ class SubversionRepositoryTestCase(unitt
     # the comparison list gets too long.
     self.assertEqual(dsp.ops[:len(expected_list)], expected_list)
 
+    # _close_dumpstream should be invoked after 'baton' is removed.
+    self.assertEqual(False, stream.closed)
+    del ptr, baton, subpool, dsp
+    self.assertEqual(True, stream.closed)
+    # Issue SVN-4918
+    self.assertEqual(None, dsp_ref())
+
   def test_parse_fns3_invalid_set_fulltext(self):
     class DumpStreamParserSubclass(DumpStreamParser):
       def set_fulltext(self, node_baton):
@@ -241,6 +331,33 @@ class SubversionRepositoryTestCase(unitt
     finally:
       stream.close()
 
+  def test_parse_fns3_apply_textdelta_handler_refcount(self):
+    handler = lambda node_baton: None
+    handler_ref = weakref.ref(handler)
+
+    class ParseFns3(repos.ParseFns3):
+      def __init__(self, handler):
+        self.called = set()
+        self.handler = handler
+      def apply_textdelta(self, node_baton):
+        self.called.add('apply_textdelta')
+        return self.handler
+
+    dumpfile = os.path.join(os.path.dirname(__file__), 'data',
+                            'repository-deltas.dump')
+    pool = Pool()
+    subpool = Pool(pool)
+    parser = ParseFns3(handler)
+    ptr, baton = repos.make_parse_fns3(parser, subpool)
+    with open(dumpfile, "rb") as stream:
+      repos.parse_dumpstream3(stream, ptr, baton, False, None)
+    del ptr, baton, stream
+
+    self.assertIn('apply_textdelta', parser.called)
+    self.assertNotEqual(None, handler_ref())
+    del parser, handler, subpool, ParseFns3
+    self.assertEqual(None, handler_ref())
+
   def test_get_logs(self):
     """Test scope of get_logs callbacks"""
     logs = []
@@ -290,6 +407,79 @@ class SubversionRepositoryTestCase(unitt
       repos.dir_delta(prev_root, b'', b'', this_root, b'', e_ptr, e_baton,
               _authz_callback, 1, 1, 0, 0)
 
+  def test_delta_editor_leak_with_change_collector(self):
+    pool = Pool()
+    subpool = Pool(pool)
+    root = fs.revision_root(self.fs, self.rev, subpool)
+    editor = repos.ChangeCollector(self.fs, root, subpool)
+    editor_ref = weakref.ref(editor)
+    e_ptr, e_baton = delta.make_editor(editor, subpool)
+    repos.replay(root, e_ptr, e_baton, subpool)
+
+    fs.close_root(root)
+    del root
+    self.assertNotEqual(None, editor_ref())
+
+    del e_ptr, e_baton, editor
+    del subpool
+    self.assertEqual(None, editor_ref())
+
+  def test_replay_batons_refcounts(self):
+    """Issue SVN-4917: check ref-count of batons created and used in callbacks"""
+    root = fs.revision_root(self.fs, self.rev)
+    editor = BatonCollector(self.fs, root)
+    e_ptr, e_baton = delta.make_editor(editor)
+    repos.replay(root, e_ptr, e_baton)
+    for baton in editor.batons:
+      self.assertEqual(sys.getrefcount(baton[2]), 2,
+                       "leak on baton %s after replay without errors"
+                       % repr(baton))
+    del e_baton
+    self.assertEqual(sys.getrefcount(e_ptr), 2,
+                     "leak on editor baton after replay without errors")
+
+    editor = BatonCollectorErrorOnClose(self.fs, root,
+                                        error_path=b'branches/v1x')
+    e_ptr, e_baton = delta.make_editor(editor)
+    self.assertRaises(SubversionException, repos.replay, root, e_ptr, e_baton)
+    batons = editor.batons
+    # As svn_repos_replay calls neither close_edit callback nor abort_edit
+    # if an error has occurred during processing, references of Python objects
+    # in descendant batons may live until e_baton is deleted.
+    del e_baton
+    for baton in batons:
+      self.assertEqual(sys.getrefcount(baton[2]), 2,
+                       "leak on baton %s after replay with an error"
+                       % repr(baton))
+    self.assertEqual(sys.getrefcount(e_ptr), 2,
+                     "leak on editor baton after replay with an error")
+
+  def test_delta_editor_apply_textdelta_handler_refcount(self):
+    handler = lambda textdelta: None
+    handler_ref = weakref.ref(handler)
+
+    class Editor(delta.Editor):
+      def __init__(self, handler):
+        self.called = set()
+        self.handler = handler
+      def apply_textdelta(self, file_baton, base_checksum, pool=None):
+        self.called.add('apply_textdelta')
+        return self.handler
+
+    pool = Pool()
+    subpool = Pool(pool)
+    root = fs.revision_root(self.fs, 3)  # change of trunk/README.txt
+    editor = Editor(handler)
+    e_ptr, e_baton = delta.make_editor(editor, subpool)
+    repos.replay(root, e_ptr, e_baton, subpool)
+    del e_ptr, e_baton
+
+    self.assertIn('apply_textdelta', editor.called)
+    self.assertNotEqual(None, handler_ref())
+    del root, editor, handler, Editor
+    del subpool
+    self.assertEqual(None, handler_ref())
+
   def test_retrieve_and_change_rev_prop(self):
     """Test playing with revprops"""
     self.assertEqual(repos.fs_revision_prop(self.repos, self.rev, b"svn:log",

Modified: subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/tests/wc.py
URL: http://svn.apache.org/viewvc/subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/tests/wc.py?rev=1922345&r1=1922344&r2=1922345&view=diff
==============================================================================
--- subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/tests/wc.py (original)
+++ subversion/branches/pristine-checksum-kind/subversion/bindings/swig/python/tests/wc.py Fri Dec  6 13:59:05 2024
@@ -180,9 +180,9 @@ class SubversionWorkingCopyTestCase(unit
         self.assertTrue(target.startswith(self.path))
 
   def test_status_editor_callback_exception(self):
-      """test case for status_editor call back not to be crashed by Python exception"""
+      """test case for status_editor callback not to be crashed by Python exception"""
       def status_func(target, status):
-        # Note: exception with in this call back doesn't propagate to
+        # Note: exception with in this callback doesn't propagate to
         # the caller
         raise AssertionError('intentional exception')
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.