[neon/forks/sip6/Neon/release] /: New upstream version 6.15.1

Dmitry Shachnev null at kde.org
Mon Jul 27 12:04:48 BST 2026


Git commit 7a192e315ca3e15f7497d060558e9c97dbff5f2a by Dmitry Shachnev.
Committed on 21/12/2025 at 09:09.
Pushed by carlosdem into branch 'Neon/release'.

New upstream version 6.15.1

M  +3    -3    .git_archival.txt
M  +1    -1    docs/conf.py
M  +14   -0    docs/releases.md
M  +9    -8    sipbuild/generator/indexed_lists.py
M  +34   -7    sipbuild/generator/outputs/code/backends/v12v13.py
M  +6    -30   sipbuild/generator/outputs/code/snippets.py
M  +6    -2    sipbuild/generator/parser/parser_manager.py
M  +3    -0    sipbuild/generator/parser/rules.py
M  +6    -1    sipbuild/generator/resolver/resolver.py
A  +34   -0    test/issue-99/issue_99_module.sip
A  +10   -0    test/issue-99/test_issue_99.py

https://invent.kde.org/neon/forks/sip6/-/commit/7a192e315ca3e15f7497d060558e9c97dbff5f2a

diff --git a/.git_archival.txt b/.git_archival.txt
index 148366b..85a7bbb 100644
--- a/.git_archival.txt
+++ b/.git_archival.txt
@@ -1,3 +1,3 @@
-node: 7378531efd3500f1dc8a0c3a1219fdc239ae3dfb
-node-date: 2025-12-06T12:06:53Z
-describe-name: 6.15.0
+node: 4884efc25d98c9b2cf734adf43695ab3192f8dcc
+node-date: 2025-12-20T13:46:07Z
+describe-name: 6.15.1
diff --git a/docs/conf.py b/docs/conf.py
index fbe76a5..032cbee 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -13,7 +13,7 @@ project = 'sip'
 copyright = '{0} Phil Thompson <phil at riverbankcomputing.com>'.format(
         date.today().year)
 author = 'Phil Thompson'
-version = 'v6.15.0'
+version = 'v6.15.1'
 
 
 # -- General configuration ---------------------------------------------------
diff --git a/docs/releases.md b/docs/releases.md
index e46d911..fad429b 100644
--- a/docs/releases.md
+++ b/docs/releases.md
@@ -1,5 +1,19 @@
 # Release Notes
 
+## v6.15.1
+
+### Bug fixes
+
+- A super-class of a disabled class was not ignored if it was disabled itself.
+- Fixed a regression in the generation of class definitions when using old ABI
+  versions that don't support arrays.
+- Fixed a regression in the checking if a name clashed with the name of a class
+  in the same scope.  Resolves
+  [#99](https://github.com/Python-SIP/sip/issues/99)
+- Fixed a regression in the code generated for static functions defined in
+  hidden namespaces.
+
+
 ## v6.15.0
 
 ### Removal of support for Python v3.9
diff --git a/sipbuild/generator/indexed_lists.py b/sipbuild/generator/indexed_lists.py
index 01109f9..5505f56 100644
--- a/sipbuild/generator/indexed_lists.py
+++ b/sipbuild/generator/indexed_lists.py
@@ -108,30 +108,27 @@ class IndexedClassList(IndexedList['WrappedClass']):
     """
     A list of WrappedClasss keeping the following indices:
     - classes by fq_cpp_name
-    - classes by scope and py_name
     """
+
     def _index_clear(self):
         """Set up indices. See IndexedList._index_clear()."""
+
         self._by_cppname = defaultdict(list)
-        self._by_scope_pyname = defaultdict(list)
 
     def _index_add(self, klass):
         """Add element to indices. See IndexedList._index_add()."""
+
         self._by_cppname[klass.iface_file.fq_cpp_name].append(klass)
-        self._by_scope_pyname[klass.scope, str(klass.py_name)].append(klass)
 
     def _index_remove(self, klass):
         """Remove element from indices. See IndexedList._index_add()."""
+
         self._by_cppname[klass.iface_file.fq_cpp_name].remove(klass)
-        del self._by_scope_pyname[klass.scope, str(klass.py_name)]
 
     def by_fq_cpp_name(self, name):
         """Find WrappedClasses by their .iface_file.fq_cpp_name."""
-        return self._by_cppname[name]
 
-    def by_scope_and_py_name(self, scope, name):
-        """Find WrappedClasses by their .scope and .py_name."""
-        return self._by_scope_pyname[scope, str(name)]
+        return self._by_cppname[name]
 
 
 class IndexedEnumList(IndexedList['WrappedEnum']):
@@ -153,9 +150,11 @@ class IndexedEnumList(IndexedList['WrappedEnum']):
         if enum.fq_cpp_name:
             assert enum.fq_cpp_name not in self._by_cppname, f"Duplicate enum: {enum.fq_cpp_name}"
             self._by_cppname[enum.fq_cpp_name] = enum
+
         if enum.py_name:
             assert (enum.scope, str(enum.py_name)) not in self._by_scope_pyname
             self._by_scope_pyname[enum.scope, str(enum.py_name)] = enum
+
         if not enum.is_scoped:
             for member in enum.members:
                 assert (enum.scope, str(member.py_name)) not in self._unscoped_by_scope_member, f"Duplicate enum member: {member.py_name.name}"
@@ -165,8 +164,10 @@ class IndexedEnumList(IndexedList['WrappedEnum']):
         """Remove WrappedEnum from indices. See IndexedList._index_add()."""
         if enum.fq_cpp_name:
             del self._by_cppname[enum.fq_cpp_name]
+
         if enum.py_name:
             del self._by_scope_pyname[enum.scope, enum.py_name.name]
+
         if not enum.is_scoped:
             for member in enum.members:
                 del self._unscoped_by_scope_member[(enum.scope, str(member.py_name))]
diff --git a/sipbuild/generator/outputs/code/backends/v12v13.py b/sipbuild/generator/outputs/code/backends/v12v13.py
index 23c758f..eccbae4 100644
--- a/sipbuild/generator/outputs/code/backends/v12v13.py
+++ b/sipbuild/generator/outputs/code/backends/v12v13.py
@@ -12,8 +12,7 @@ from ...formatters import fmt_argument_as_cpp_type
 
 from ..snippets import (g_class_docstring, g_class_method_table,
         g_enum_member_table, g_module_docstring, g_type_init_body,
-        g_module_init_start, g_pyqt_class_plugin, g_pyqt_helper_defns,
-        g_pyqt_helper_init)
+        g_pyqt_class_plugin, g_pyqt_helper_defns, g_pyqt_helper_init)
 from ..utils import (get_class_flags, get_const_cast, get_docstring_text,
         get_encoded_type, get_enum_member, get_named_value_decl,
         get_normalised_cached_name, get_optional_ptr, get_use_in_code,
@@ -159,7 +158,7 @@ const sipAPIDef *sipAPI_{module_name};
 ''')
 
         g_pyqt_helper_defns(sf, spec)
-        g_module_init_start(sf, spec)
+        self.g_module_init_start(sf)
         has_module_functions = self.g_module_functions_table(sf, bindings,
                 module)
         self.g_module_definition(sf, has_module_functions=has_module_functions)
@@ -274,6 +273,33 @@ f'''    static PyModuleDef sip_module_def = {{
 
         return True
 
+    def g_module_init_start(self, sf):
+        """ Generate the start of the Python module initialisation function.
+        """
+
+        spec = self.spec
+
+        if spec.is_composite or spec.c_bindings:
+            extern_c = ''
+            arg_type = 'void'
+        else:
+            extern_c = 'extern "C" '
+            arg_type = ''
+
+        module_name = spec.module.py_name
+
+        sf.write(
+f'''
+
+/* The Python module initialisation function. */
+#if defined(SIP_STATIC_MODULE)
+{extern_c}PyObject *PyInit_{module_name}({arg_type})
+#else
+PyMODINIT_FUNC PyInit_{module_name}({arg_type})
+#endif
+{{
+''')
+
     def g_py_method_table(self, sf, bindings, members, scope):
         """ Generate a Python method table for a class or mapped type and
         return the number of entries.
@@ -875,10 +901,11 @@ static sipPySlotDef slots_{klass_name}[] = {{
                             (spec.c_bindings or klass.needs_array_helper),
                             'array_delete', klass_name))
 
-        if klass.can_create:
-            class_fields.append(f'sizeof ({scoped_class_name(spec, klass)})')
-        else:
-            class_fields.append('0')
+            if klass.can_create:
+                class_fields.append(
+                        f'sizeof ({scoped_class_name(spec, klass)})')
+            else:
+                class_fields.append('0')
 
         base_fields = ',\n        '.join(base_fields)
         container_fields = ',\n        '.join(container_fields)
diff --git a/sipbuild/generator/outputs/code/snippets.py b/sipbuild/generator/outputs/code/snippets.py
index 20da422..f469950 100644
--- a/sipbuild/generator/outputs/code/snippets.py
+++ b/sipbuild/generator/outputs/code/snippets.py
@@ -96,7 +96,7 @@ static void sip_import_component_module(PyObject *d, const char *name)
 ''')
 
     g_module_docstring(sf, module)
-    g_module_init_start(sf, spec)
+    backend.g_module_init_start(sf)
     backend.g_module_definition(sf)
 
     sf.write(
@@ -931,31 +931,6 @@ def g_type_init_body(backend, sf, bindings, klass):
 ''')
 
 
-def g_module_init_start(sf, spec):
-    """ Generate the start of the Python module initialisation function. """
-
-    if spec.is_composite or spec.c_bindings:
-        extern_c = ''
-        arg_type = 'void'
-    else:
-        extern_c = 'extern "C" '
-        arg_type = ''
-
-    module_name = spec.module.py_name
-
-    sf.write(
-f'''
-
-/* The Python module initialisation function. */
-#if defined(SIP_STATIC_MODULE)
-{extern_c}PyObject *PyInit_{module_name}({arg_type})
-#else
-PyMODINIT_FUNC PyInit_{module_name}({arg_type})
-#endif
-{{
-''')
-
-
 def g_pyqt_class_plugin(backend, sf, bindings, klass):
     """ Generate any extended class definition data for PyQt.  Return True if
     anything was generated.
@@ -2677,12 +2652,13 @@ def _static_function(backend, sf, bindings, member, scope=None):
 
     if scope is None:
         overloads = spec.module.overloads
+        scope_py = None
     else:
         overloads = scope.overloads
-        scope = py_scope(scope)
+        scope_py = py_scope(scope)
 
-        if scope is not None:
-            member_name = scope.iface_file.fq_cpp_name.as_word + '_' + member_name
+        if scope_py is not None:
+            member_name = scope_py.iface_file.fq_cpp_name.as_word + '_' + member_name
 
     sf.write('\n\n')
 
@@ -2701,7 +2677,7 @@ def _static_function(backend, sf, bindings, member, scope=None):
     else:
         kw_fw_decl = kw_decl = ''
 
-    if scope is None:
+    if scope_py is None:
         if not spec.c_bindings:
             sf.write(f'extern "C" {{static PyObject *func_{member_name}({backend.get_py_method_args(is_impl=False, is_module_fn=True)}{kw_fw_decl});}}\n')
 
diff --git a/sipbuild/generator/parser/parser_manager.py b/sipbuild/generator/parser/parser_manager.py
index f94fca6..36c11b1 100644
--- a/sipbuild/generator/parser/parser_manager.py
+++ b/sipbuild/generator/parser/parser_manager.py
@@ -1056,7 +1056,10 @@ class ParserManager:
                 break
 
         # Check the classes.
-        for cd in self.spec.classes.by_scope_and_py_name(self.scope, py_name):
+        for cd in self.spec.classes:
+            if cd.scope is not self.scope:
+                continue
+
             # A class will have already been added to the scope and this will
             # tell us to ignore it.
             if cd is ignore:
@@ -1065,7 +1068,8 @@ class ParserManager:
             if cd.external:
                 continue
 
-            clash("a class or namespace")
+            if cd.py_name.name == py_name:
+                clash("a class or namespace")
 
         if self.scope is None:
             # Check the exceptions.
diff --git a/sipbuild/generator/parser/rules.py b/sipbuild/generator/parser/rules.py
index 8b9b429..7f3993d 100644
--- a/sipbuild/generator/parser/rules.py
+++ b/sipbuild/generator/parser/rules.py
@@ -1986,6 +1986,9 @@ def p_simple_superclass(p):
 
     pm = p.parser.pm
 
+    if pm.skipping:
+        return
+
     # Handle templates.
     if len(p) == 5:
         if pm.parsing_template:
diff --git a/sipbuild/generator/resolver/resolver.py b/sipbuild/generator/resolver/resolver.py
index cc8a780..c226801 100644
--- a/sipbuild/generator/resolver/resolver.py
+++ b/sipbuild/generator/resolver/resolver.py
@@ -2162,8 +2162,13 @@ def _create_sorted_numbered_types(spec, mod, error_log):
 
         if mod is spec.module or klass.iface_file.needed:
             if not klass.is_hidden_namespace:
+                # For ABI v14 and later the sip module searches this table
+                # using the Python name (as part of attribute lookup).  For
+                # earlier versions it uses the C/C++ name (for sipFindType()).
+                key_name = klass.py_name if spec.target_abi >= (14, 0) else klass.iface_file.cpp_name
+
                 mod.needed_types.append(Argument(ArgumentType.CLASS,
-                        definition=klass, name=klass.iface_file.cpp_name))
+                        definition=klass, name=key_name))
 
     for mapped_type in spec.mapped_types:
         if mapped_type.iface_file.module is not mod:
diff --git a/test/issue-99/issue_99_module.sip b/test/issue-99/issue_99_module.sip
new file mode 100644
index 0000000..a88dc82
--- /dev/null
+++ b/test/issue-99/issue_99_module.sip
@@ -0,0 +1,34 @@
+// The SIP implementation of the issue_99_module test module.  Note that the
+// issue meant that this module wouldn't build.  We also test the correct
+// behaviour once built.
+
+
+%Module(name=issue_99_module)
+
+
+%ModuleHeaderCode
+class KlassA
+{
+public:
+    static const char *get_cpp_name() {return "KlassA";}
+};
+
+class KlassB
+{
+public:
+    static const char *get_cpp_name() {return "KlassB";}
+};
+%End
+
+
+class KlassA /PyName=KlassC/
+{
+public:
+    static const char *get_cpp_name();
+};
+
+class KlassB /PyName=KlassA/
+{
+public:
+    static const char *get_cpp_name();
+};
diff --git a/test/issue-99/test_issue_99.py b/test/issue-99/test_issue_99.py
new file mode 100644
index 0000000..6fb5f5d
--- /dev/null
+++ b/test/issue-99/test_issue_99.py
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+# Copyright (c) 2025 Phil Thompson <phil at riverbankcomputing.com>
+
+
+def test_class_pyname(module):
+    assert module.KlassC.get_cpp_name() == b'KlassA'
+
+def test_class_pyname_using_existing_cpp_name(module):
+    assert module.KlassA.get_cpp_name() == b'KlassB'



More information about the Neon-commits mailing list