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,jor...@apache.org  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 <futat...@yf.bsdclub.org>
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):

Reply via email to