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')
 


Reply via email to