Hello,
On 2025/06/19 20:30, Branko Čibej wrote:
> On 19. 6. 25 12:42, Joe Orton wrote:
>> On Thu, Jun 19, 2025 at 10:26:43AM +0200, Branko Čibej wrote:
>>> On 19. 6. 25 10:22,[email protected] wrote:
>>>> Author: jorton
>>>> Date: Thu Jun 19 08:22:56 2025
>>>> New Revision: 1926575
>>>>
>>>> URL:http://svn.apache.org/viewvc?rev=1926575&view=rev
>>>> Log:
>>>> Fix SWIG test cases for Python 3.14.
>>>>
>>>> The interpreter internally avoids some reference count modifications when
>>>> loading objects onto the operands stack by borrowing references when
>>>> possible. This can lead to smaller reference count values compared to
>>>> previous Python versions.
>>>>
>>>> https://docs.python.org/3.14/whatsnew/3.14.html#whatsnew314-refcount
>>> How come all the other getrefcount()s in repository.py aren't affected?
>> I have no idea, sorry, but all the tests pass under 3.14 with this
>> applied.
>
> Yes, I verified that (3.14.0b3 via pyenv). Well, as long as it works ...
Then, I've tweak repository.py without using magic number of refcount
and adding comments. Although I didn't test it on Python 3.14, but
it is expected to work.
--
Yasuhito FUTATSUKI <[email protected]>
Index: subversion/bindings/swig/python/tests/repository.py
===================================================================
--- subversion/bindings/swig/python/tests/repository.py (revision 1926579)
+++ subversion/bindings/swig/python/tests/repository.py (working copy)
@@ -95,7 +95,9 @@
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)))
+ # refcount for the object refered by bt is counted down after
+ # the variable bt is deleted (i.e. after leaving this function)
+ self.batons.append((b'dir baton', b'', bt, sys.getrefcount(bt) - 1))
return bt
def add_directory(self, path, parent_baton,
@@ -104,7 +106,9 @@
copyfrom_path,
copyfrom_revision,
dir_pool)
- self.batons.append((b'dir baton', path, bt, sys.getrefcount(bt)))
+ # refcount for the object refered by bt is counted down after
+ # the variable bt is deleted (i.e. after leaving this function)
+ self.batons.append((b'dir baton', path, bt, sys.getrefcount(bt) - 1))
return bt
def open_directory(self, path, parent_baton, base_revision,
@@ -111,7 +115,9 @@
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)))
+ # refcount for the object refered by bt is counted down after
+ # the variable bt is deleted (i.e. after leaving this function)
+ self.batons.append((b'dir baton', path, bt, sys.getrefcount(bt) - 1))
return bt
def add_file(self, path, parent_baton,
@@ -119,13 +125,17 @@
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)))
+ # refcount for the object refered by bt is counted down after
+ # the variable bt is deleted (i.e. after leaving this function)
+ self.batons.append((b'file baton', path, bt, sys.getrefcount(bt) - 1))
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)))
+ # refcount for the object refered by bt is counted down after
+ # the variable bt is deleted (i.e. after leaving this function)
+ self.batons.append((b'file baton', path, bt, sys.getrefcount(bt) - 1))
return bt
def close_edit(self, pool=None):
@@ -429,19 +439,22 @@
root = fs.revision_root(self.fs, self.rev)
editor = BatonCollector(self.fs, root)
e_ptr, e_baton = delta.make_editor(editor)
- expected = 1 if sys.version_info >= (3, 14) else 2
+ refcount_at_first = sys.getrefcount(e_ptr)
repos.replay(root, e_ptr, e_baton)
- for baton in editor.batons:
- self.assertEqual(sys.getrefcount(baton[2]), 2,
+ for baton_tuple in editor.batons:
+ # baton_tuple: 4-tuple(baton_type: bytes, node: bytes, bt: baton,
+ # expected_refcount_of_bt: int)
+ self.assertEqual(sys.getrefcount(baton_tuple[2]), baton_tuple[3],
"leak on baton %s after replay without errors"
- % repr(baton))
+ % repr(baton_tuple))
del e_baton
- self.assertEqual(sys.getrefcount(e_ptr), expected,
+ self.assertEqual(sys.getrefcount(e_ptr), refcount_at_first,
"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)
+ refcount_at_first = sys.getrefcount(e_ptr)
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
@@ -448,11 +461,13 @@
# 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,
+ for baton_tuple in batons:
+ # baton_tuple: 4-tuple(baton_type: bytes, node: bytes, bt: baton,
+ # expected_refcount_of_bt: int)
+ self.assertEqual(sys.getrefcount(baton_tuple[2]), baton_tuple[3],
"leak on baton %s after replay with an error"
- % repr(baton))
- self.assertEqual(sys.getrefcount(e_ptr), expected,
+ % repr(baton_tuple))
+ self.assertEqual(sys.getrefcount(e_ptr), refcount_at_first,
"leak on editor baton after replay with an error")
def test_delta_editor_apply_textdelta_handler_refcount(self):