Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-ijson for openSUSE:Factory 
checked in at 2026-07-21 22:55:46
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-ijson (Old)
 and      /work/SRC/openSUSE:Factory/.python-ijson.new.24530 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-ijson"

Tue Jul 21 22:55:46 2026 rev:10 rq:1365759 version:3.5.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-ijson/python-ijson.changes        
2026-03-16 14:20:00.499958685 +0100
+++ /work/SRC/openSUSE:Factory/.python-ijson.new.24530/python-ijson.changes     
2026-07-21 22:58:14.945313114 +0200
@@ -1,0 +2,14 @@
+Tue Jul 14 19:54:06 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 3.5.1:
+  * The internal `ijson.common.ObjectBuilder` no longer creates
+    internal reference cycles, so discarded builders are freed by
+    reference counting instead of waiting for a cyclic garbage
+    collection pass. This lowers peak memory usage for code that
+    builds and discards one large object at a time. As a side
+    effect, `builder.value` is now `None` before any
+    event is processed (previously accessing it raised
+    `AttributeError`), and the undocumented `containers` attribute
+    holds the in-progress containers rather than setter closures.
+
+-------------------------------------------------------------------

Old:
----
  ijson-3.5.0.tar.gz

New:
----
  ijson-3.5.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-ijson.spec ++++++
--- /var/tmp/diff_new_pack.cm1U6X/_old  2026-07-21 22:58:15.469330971 +0200
+++ /var/tmp/diff_new_pack.cm1U6X/_new  2026-07-21 22:58:15.469330971 +0200
@@ -20,7 +20,7 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-ijson
-Version:        3.5.0
+Version:        3.5.1
 Release:        0
 Summary:        Iterative JSON parser with a standard Python iterator interface
 License:        BSD-3-Clause

++++++ ijson-3.5.0.tar.gz -> ijson-3.5.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ijson-3.5.0/CHANGELOG.md new/ijson-3.5.1/CHANGELOG.md
--- old/ijson-3.5.0/CHANGELOG.md        2026-02-24 04:43:16.000000000 +0100
+++ new/ijson-3.5.1/CHANGELOG.md        2026-07-06 19:24:12.000000000 +0200
@@ -2,6 +2,17 @@
 
 ## Development
 
+## [3.5.1]
+
+* The internal `ijson.common.ObjectBuilder` no longer creates internal 
reference
+  cycles, so discarded builders are freed by reference counting instead
+  of waiting for a cyclic garbage collection pass. This lowers peak
+  memory usage for code that builds and discards one large object at a
+  time. As a side effect, `builder.value` is now `None` before any
+  event is processed (previously accessing it raised `AttributeError`),
+  and the undocumented `containers` attribute holds the in-progress
+  containers rather than setter closures.
+
 ## [3.5.0]
 
 * Added input iterator support via the new `ijson.from_iter` adapter.
@@ -398,3 +409,4 @@
 [3.4.0]: https://github.com/ICRAR/ijson/releases/tag/v3.4.0
 [3.4.0.post0]: https://github.com/ICRAR/ijson/releases/tag/v3.4.0.post0
 [3.5.0]: https://github.com/ICRAR/ijson/releases/tag/v3.5.0
+[3.5.1]: https://github.com/ICRAR/ijson/releases/tag/v3.5.1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ijson-3.5.0/PKG-INFO new/ijson-3.5.1/PKG-INFO
--- old/ijson-3.5.0/PKG-INFO    2026-02-24 04:43:22.860385000 +0100
+++ new/ijson-3.5.1/PKG-INFO    2026-07-06 19:24:17.241391400 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: ijson
-Version: 3.5.0
+Version: 3.5.1
 Summary: Iterative JSON parser with standard Python iterator interfaces
 Author-email: Rodrigo Tobar <[email protected]>, Ivan Sagalaev 
<[email protected]>
 License-Expression: BSD-3-Clause AND ISC
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ijson-3.5.0/pyproject.toml 
new/ijson-3.5.1/pyproject.toml
--- old/ijson-3.5.0/pyproject.toml      2026-02-24 04:43:16.000000000 +0100
+++ new/ijson-3.5.1/pyproject.toml      2026-07-06 19:24:12.000000000 +0200
@@ -72,3 +72,21 @@
 description = "Converts README.rst to HTML"
 commands = [['rst2html5', "--strict", "README.rst", "--output=readme.html"]]
 deps = ["docutils", "Pygments"]
+
+[tool.cibuildwheel]
+before-all = "bash -c 'cd \"{project}\"; sh .github/tools/install_yajl.sh'"
+before-test = "pip install -r test-requirements.txt"
+build-verbosity = 1
+enable = [
+    "pypy",
+]
+# Our C extension made PyPy < 7.3.13 crash (a bug on their end)
+# it doesn't make sense to build wheels for platforms not supported by that 
version
+skip = "pp39*"
+test-command = "pytest -vv {project}/tests"
+
+[tool.cibuildwheel.macos.environment]
+IJSON_EMBED_YAJL = "1"
+
+[tool.cibuildwheel.windows.environment]
+IJSON_EMBED_YAJL = "1"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ijson-3.5.0/src/ijson/benchmark.py 
new/ijson-3.5.1/src/ijson/benchmark.py
--- old/ijson-3.5.0/src/ijson/benchmark.py      2026-02-24 04:43:16.000000000 
+0100
+++ new/ijson-3.5.1/src/ijson/benchmark.py      2026-07-06 19:24:12.000000000 
+0200
@@ -215,41 +215,36 @@
 
 
 def main():
-    DEFAULT_N = 100000
-    DEFAULT_ITERATIONS = 1
-    DEFAULT_BUFSIZE = 64 * 1024
-    ALL_BENCHMARKS = ','.join(_benchmarks)
-    ALL_BACKENDS = ','.join(_backends)
     parser = argparse.ArgumentParser()
     parser.add_argument('-s', '--size', type=int,
-        help='Size of JSON content; actual size in bytes might differ, 
defaults to %d' % DEFAULT_N,
-        default=DEFAULT_N)
+        help='Size of JSON content; actual size in bytes might differ, 
defaults to %(default)s',
+        default=100000)
     parser.add_argument('-I', '--iterations', type=int,
-        help='How many times each method should be tested, defaults to %d' % 
DEFAULT_ITERATIONS,
-        default=DEFAULT_ITERATIONS)
+        help='How many times each method should be tested, defaults to 
%(default)s',
+        default=1)
     parser.add_argument('-S', '--bufsize', type=int,
-        help='Buffer size used during parsing; defaults to %d' % 
DEFAULT_BUFSIZE,
-        default=DEFAULT_BUFSIZE)
+        help='Buffer size used during parsing; defaults to %(default)s',
+        default=64 * 1024)
     parser.add_argument('-b', '--benchmarks', type=parse_benchmarks,
-        help='Comma-separated list of benchmarks to include, defaults to %s' % 
ALL_BENCHMARKS,
-        default=ALL_BENCHMARKS)
+        help='Comma-separated list of benchmarks to include, defaults to 
%(default)s',
+        default=','.join(_benchmarks))
     parser.add_argument('-B', '--backends', type=parse_backends,
-        help='Comma-separated list of backends to include, defaults to %s' % 
ALL_BACKENDS,
-        default=ALL_BACKENDS)
+        help='Comma-separated list of backends to include, defaults to 
%(default)s',
+        default=','.join(_backends))
     parser.add_argument('-l', '--list', action='store_true',
         help='List available benchmarks and backends')
     parser.add_argument('inputs', nargs='*',
         help='File to use for benchmarks rather than built-in benchmarking 
functions')
-    parser.add_argument('-M', '--multiple-values', action='store_true', 
default=False,
+    parser.add_argument('-M', '--multiple-values', action='store_true',
         help='Content has multiple JSON values, useful when used with -i')
-    parser.add_argument('-f', '--use-float', action='store_true', 
default=False,
+    parser.add_argument('-f', '--use-float', action='store_true',
         help='Parse non-integer numbers as float instead of Decimal')
     parser.add_argument('-m', '--method', choices=['basic_parse', 'parse', 
'kvitems', 'items'],
-                        help='The method to benchmark', default='basic_parse')
-    parser.add_argument('-c', '--coro', action='store_true', default=False,
-                        dest='run_coro', help='Benchmark coroutine methods')
-    parser.add_argument('-a', '--async', action='store_true', default=False,
-                        dest='run_async', help='Benchmark asyncio-enabled 
methods')
+        help='The method to benchmark, defaults to %(default)s', 
default='basic_parse')
+    parser.add_argument('-c', '--coro', action='store_true',
+        dest='run_coro', help='Benchmark coroutine methods')
+    parser.add_argument('-a', '--async', action='store_true',
+        dest='run_async', help='Benchmark asyncio-enabled methods')
     parser.add_argument('-p', '--prefix', help='Prefix (used with -M 
items|kvitems)', default='')
 
     args = parser.parse_args()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ijson-3.5.0/src/ijson/common.py 
new/ijson-3.5.1/src/ijson/common.py
--- old/ijson-3.5.0/src/ijson/common.py 2026-02-24 04:43:16.000000000 +0100
+++ new/ijson-3.5.1/src/ijson/common.py 2026-07-06 19:24:12.000000000 +0200
@@ -113,28 +113,38 @@
 
     '''
     def __init__(self, map_type=None):
-        def initial_set(value):
-            self.value = value
-        self.containers = [initial_set]
+        self.value = None
+        self.containers = []
         self.map_type = map_type or dict
 
+    def _add(self, value):
+        # Storing the in-progress containers themselves (rather than setter
+        # closures, which would reference back to self and create reference
+        # cycles) keeps the builder collectable by plain refcounting
+        if not self.containers:
+            self.value = value
+        else:
+            top = self.containers[-1]
+            if isinstance(top, list):
+                top.append(value)
+            else:
+                top[self.key] = value
+
     def event(self, event, value):
         if event == 'map_key':
             self.key = value
         elif event == 'start_map':
             mappable = self.map_type()
-            self.containers[-1](mappable)
-            def setter(value):
-                mappable[self.key] = value
-            self.containers.append(setter)
+            self._add(mappable)
+            self.containers.append(mappable)
         elif event == 'start_array':
             array = []
-            self.containers[-1](array)
-            self.containers.append(array.append)
+            self._add(array)
+            self.containers.append(array)
         elif event == 'end_array' or event == 'end_map':
             self.containers.pop()
         else:
-            self.containers[-1](value)
+            self._add(value)
 
 
 @utils.coroutine
@@ -156,7 +166,6 @@
                         object_depth += 1
                     elif event in ('end_map', 'end_array'):
                         object_depth -= 1
-                del builder.containers[:]
                 target.send(builder.value)
             else:
                 target.send(value)
@@ -187,7 +196,6 @@
                     object_depth += 1
                 elif event == 'end_map':
                     object_depth -= 1
-            del builder.containers[:]
             target.send((key, builder.value))
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ijson-3.5.0/src/ijson/dump.py 
new/ijson-3.5.1/src/ijson/dump.py
--- old/ijson-3.5.0/src/ijson/dump.py   2026-02-24 04:43:16.000000000 +0100
+++ new/ijson-3.5.1/src/ijson/dump.py   2026-07-06 19:24:12.000000000 +0200
@@ -6,7 +6,7 @@
 import ijson
 
 
-HEADERS = {
+METHODS_WITH_COLUMN_HEADERS = {
     'basic_parse': 'name, value',
     'parse': 'path, name, value',
     'kvitems': 'key, value',
@@ -19,10 +19,14 @@
     return str(o)
 
 def dump():
-    parser = argparse.ArgumentParser(description='Dump ijson events')
-    parser.add_argument('-m', '--method', choices=['basic_parse', 'parse', 
'kvitems', 'items'],
-                        help='The method to use for dumping', 
default='basic_parse')
-    parser.add_argument('-p', '--prefix', help='Prefix (used with -M 
items|kvitems)', default='')
+    description = '''
+    Read JSON data from standard input, and dump
+    ijson events (using methods basic_parse or parse),
+    or content (using methods kvitems or items).'''
+    parser = argparse.ArgumentParser(description=description)
+    parser.add_argument('-m', '--method', choices=METHODS_WITH_COLUMN_HEADERS,
+                        help='The method to use for dumping. Defaults to 
%(default)s', default='basic_parse')
+    parser.add_argument('-p', '--prefix', help='Prefix (used with -m 
items|kvitems)', default='')
     parser.add_argument('-M', '--multiple-values', help='Allow multiple 
values', action='store_true')
     args = parser.parse_args()
 
@@ -33,7 +37,7 @@
         method_args = args.prefix,
     if args.multiple_values:
         method_kwargs['multiple_values'] = True
-    header = '#: ' + HEADERS[args.method]
+    header = '#: ' + METHODS_WITH_COLUMN_HEADERS[args.method]
     print(header)
     print('-' * len(header))
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ijson-3.5.0/src/ijson/version.py 
new/ijson-3.5.1/src/ijson/version.py
--- old/ijson-3.5.0/src/ijson/version.py        2026-02-24 04:43:16.000000000 
+0100
+++ new/ijson-3.5.1/src/ijson/version.py        2026-07-06 19:24:12.000000000 
+0200
@@ -1 +1 @@
-__version__ = '3.5.0'
+__version__ = '3.5.1'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ijson-3.5.0/src/ijson.egg-info/PKG-INFO 
new/ijson-3.5.1/src/ijson.egg-info/PKG-INFO
--- old/ijson-3.5.0/src/ijson.egg-info/PKG-INFO 2026-02-24 04:43:22.000000000 
+0100
+++ new/ijson-3.5.1/src/ijson.egg-info/PKG-INFO 2026-07-06 19:24:17.000000000 
+0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: ijson
-Version: 3.5.0
+Version: 3.5.1
 Summary: Iterative JSON parser with standard Python iterator interfaces
 Author-email: Rodrigo Tobar <[email protected]>, Ivan Sagalaev 
<[email protected]>
 License-Expression: BSD-3-Clause AND ISC
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ijson-3.5.0/tests/test_misc.py 
new/ijson-3.5.1/tests/test_misc.py
--- old/ijson-3.5.0/tests/test_misc.py  2026-02-24 04:43:16.000000000 +0100
+++ new/ijson-3.5.1/tests/test_misc.py  2026-07-06 19:24:12.000000000 +0200
@@ -1,9 +1,14 @@
+import collections
+import gc
 import importlib.util
 import io
+import sys
+import weakref
 
 import pytest
 
 from ijson import common
+from ijson.common import ObjectBuilder
 
 from tests.test_base import JSON, JSON_EVENTS, JSON_PARSE_EVENTS, JSON_OBJECT,\
     JSON_KVITEMS
@@ -23,6 +28,96 @@
         importlib.util.module_from_spec(spec)
 
 
+class TestObjectBuilder:
+    """Unit tests for the pure-python ObjectBuilder class"""
+
+    def test_initial_value_is_none(self):
+        assert ObjectBuilder().value is None
+
+    def test_value_available_during_construction(self):
+        builder = ObjectBuilder()
+        builder.event('start_map', None)
+        assert builder.value == {}
+        builder.event('map_key', 'a')
+        builder.event('string', 'value')
+        assert builder.value == {'a': 'value'}
+        builder.event('map_key', 'b')
+        builder.event('number', 1)
+        assert builder.value == {'a': 'value', 'b': 1}
+
+    def test_builds_expected_value(self):
+        builder = ObjectBuilder()
+        for event, value in JSON_EVENTS:
+            builder.event(event, value)
+        assert builder.value == JSON_OBJECT
+
+    def test_custom_map_type(self):
+        builder = ObjectBuilder(map_type=collections.OrderedDict)
+        events = [
+            ('start_map', None),
+            ('map_key', 'a'),
+            ('start_map', None),
+            ('map_key', 'b'),
+            ('number', 1),
+            ('end_map', None),
+            ('map_key', 'c'),
+            ('start_array', None),
+            ('number', 2),
+            ('end_array', None),
+            ('end_map', None),
+        ]
+        for event, value in events:
+            builder.event(event, value)
+        assert builder.value == {'a': {'b': 1}, 'c': [2]}
+        assert type(builder.value) is collections.OrderedDict
+        assert type(builder.value['a']) is collections.OrderedDict
+
+    @pytest.mark.skipif(sys.implementation.name != "cpython",
+                        reason="deterministic refcounting is CPython-only")
+    @pytest.mark.parametrize("events", [
+        pytest.param([
+            ('start_map', None),
+            ('map_key', 'key'),
+            ('start_map', None),
+            ('map_key', 'inner'),
+            ('string', 'value'),
+            ('end_map', None),
+            ('end_map', None),
+        ], id="nested_map"),
+        pytest.param([
+            ('start_array', None),
+            ('number', 1),
+            ('start_array', None),
+            ('number', 2),
+            ('end_array', None),
+            ('end_array', None),
+        ], id="nested_array"),
+        pytest.param([
+            ('string', 'scalar'),
+        ], id="scalar_only"),
+        pytest.param([], id="no_events"),
+    ])
+    def test_builder_freed_without_cyclic_gc(self, events):
+        """A discarded builder must be freed by refcounting alone.
+
+        The previous implementation stored setter closures in
+        self.containers; each closure referenced the builder, creating a
+        reference cycle that kept the builder (and the value being built)
+        alive until a cyclic GC pass."""
+        builder = ObjectBuilder()
+        for event, value in events:
+            builder.event(event, value)
+        ref = weakref.ref(builder)
+        gc_was_enabled = gc.isenabled()
+        gc.disable()
+        try:
+            del builder
+            assert ref() is None
+        finally:
+            if gc_was_enabled:
+                gc.enable()
+
+
 class TestMainEntryPoints:
     """Tests that main API entry points work against different types of inputs 
automatically"""
 

Reply via email to