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