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

Dmitry Shachnev null at kde.org
Sun Aug 17 07:48:50 BST 2025


Git commit b16cea2ffdca1efd59af529d0545663bc9fb3804 by Dmitry Shachnev.
Committed on 03/06/2025 at 16:10.
Pushed by carlosdem into branch 'Neon/release'.

New upstream version 6.12.0

M  +3    -3    .git_archival.txt
M  +1    -1    docs/conf.py
M  +10   -8    docs/other_topics.rst
M  +16   -0    docs/releases.md
M  +4    -1    docs/specification_files.rst
M  +2    -2    sipbuild/generator/instantiations.py
M  +32   -3    sipbuild/generator/outputs/code/code.py
M  +4    -2    sipbuild/generator/parser/parser_manager.py
M  +28   -4    sipbuild/generator/parser/rules.py
M  +41   -2    sipbuild/generator/resolver/resolver.py
M  +3    -0    sipbuild/generator/specification.py
D  +0    -26   test/template_superclasses/test_template_superclasses.py
R  +0    -0    test/templates/__init__.py [from: test/template_superclasses/__init__.py - 100% similarity]
R  +46   -7    test/templates/templates_module.sip [from: test/template_superclasses/template_superclasses_module.sip - 057% similarity]
A  +40   -0    test/templates/test_templates.py

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

diff --git a/.git_archival.txt b/.git_archival.txt
index 7556300..5c7f21c 100644
--- a/.git_archival.txt
+++ b/.git_archival.txt
@@ -1,3 +1,3 @@
-node: e9b5cb9242fb39e1edf3bcd9b2dc09d359318775
-node-date: 2025-05-23T12:36:52+01:00
-describe-name: 6.11.1
+node: 4f0ca7fd43b941311e070051f637d931e17fa5d7
+node-date: 2025-06-03T14:59:00+01:00
+describe-name: 6.12.0
diff --git a/docs/conf.py b/docs/conf.py
index 37c0ae7..90bc454 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.11.1'
+version = 'v6.12.0'
 
 
 # -- General configuration ---------------------------------------------------
diff --git a/docs/other_topics.rst b/docs/other_topics.rst
index 9104d7f..f5f8928 100644
--- a/docs/other_topics.rst
+++ b/docs/other_topics.rst
@@ -1,13 +1,13 @@
 Other Topics
 ============
 
-Wrapping Enums
---------------
+Wrapping Enums using ABI v12
+----------------------------
 
-SIP wraps C/C++ enums using a dedicated Python type and implements behaviour
-that mimics the C/C++ behaviour regarding the visibility of the enum's members.
-In other words, an enum's members have the same visibility as the enum itself.
-For example::
+When using ABI v12 SIP wraps C/C++ named enums using a dedicated Python type
+and implements behaviour that mimics the C/C++ behaviour regarding the
+visibility of the enum's members.  In other words, an enum's members have the
+same visibility as the enum itself.  For example::
 
     class MyClass
     {
@@ -35,8 +35,10 @@ this, SIP makes the members of traditional C/C++ enums visible from the scope
 of the enum as well.
 
 It is recommended that Python code should always specify the enum scope when
-referencing an enum member.  A future version of SIP will remove support for
-the traditional behaviour.
+referencing an enum member.
+
+When using ABI v13 SIP uses the :mod:`enum` module to wrap all C/C++ named
+enums.
 
 
 .. _ref-object-ownership:
diff --git a/docs/releases.md b/docs/releases.md
index 36bc2e4..1ba077e 100644
--- a/docs/releases.md
+++ b/docs/releases.md
@@ -1,5 +1,21 @@
 # Release Notes
 
+## v6.12.0
+
+### Support for C++11 enum base types
+
+Support was added for C++11 enum base types.  At the moment this is limited
+to base types no larger than `int`s.  Prior to this support, all enums were
+assumed to be `int` which breaks on big-endian systems.
+
+Resolves [#75](https://github.com/Python-SIP/sip/issues/75)
+
+### Bug fixes
+
+- Fixed a regression in v6.11.0 affecting class names as template
+  arguments.  Resolves [#77](https://github.com/Python-SIP/sip/issues/77)
+
+
 ## v6.11.1
 
 ### Bug fixes
diff --git a/docs/specification_files.rst b/docs/specification_files.rst
index 5ccb542..85946d3 100644
--- a/docs/specification_files.rst
+++ b/docs/specification_files.rst
@@ -203,10 +203,13 @@ file.
     *mapped-type-template* :: = **template** **<** *type-list* **>**
             *mapped-type*
 
-    *enum* ::= **enum** [*enum-key*] [*name*] [*enum-annotations*] **{** {*enum-line*} **};**
+    *enum* ::= **enum** [*enum-key*] [*name*] [**:** *enum-base*]
+            [*enum-annotations*] **{** {*enum-line*} **};**
 
     *enum-key* ::= [**class** | **struct**]
 
+    *enum-base* ::= *base-type*
+
     *enum-line* ::= [:directive:`%If` | *name* [*enum-annotations*] **,**
 
     *function* ::= *typed-name* **(** [*argument-list*] **)** [**noexcept**]
diff --git a/sipbuild/generator/instantiations.py b/sipbuild/generator/instantiations.py
index b351a29..de38c0c 100644
--- a/sipbuild/generator/instantiations.py
+++ b/sipbuild/generator/instantiations.py
@@ -461,7 +461,7 @@ def _superclass_from_class(klass, p, symbol, tmpl_names, template, pm):
     # Only deal with undefined classes with unscoped names which is how
     # template argument names are passed.
     if klass.iface_file.module is None and superclass_name.is_simple:
-        superclass = _find_argument_value(superclass_name, p, symbol,
+        superclass = _find_argument_value(superclass_name.base_name, p, symbol,
                 tmpl_names, template, pm)
 
         if superclass is None:
@@ -536,7 +536,7 @@ def _superclass_from_template(superclass, p, symbol, tmpl_names, template, pm):
 
 def _find_argument_value(name, p, symbol, tmpl_names, template, pm):
     """ Return the WrappedClass instance that is the value of a named argument.
-    None is returned is an appropriate value couldn't be found.
+    None is returned if an appropriate value couldn't be found.
     """
 
     for a, arg in enumerate(tmpl_names.args):
diff --git a/sipbuild/generator/outputs/code/code.py b/sipbuild/generator/outputs/code/code.py
index bc84e07..3f9e452 100644
--- a/sipbuild/generator/outputs/code/code.py
+++ b/sipbuild/generator/outputs/code/code.py
@@ -5379,6 +5379,10 @@ def _call_args(sf, spec, cpp_signature, py_signature):
             if nr_derefs == 1:
                 indirection = '&'
 
+            if _arg_is_small_enum(arg):
+                prefix = 'static_cast<' + fmt_enum_as_cpp_type(arg.definition) + '>('
+                suffix = ')'
+
         # See if we need to cast a Python void * to the correct C/C++ pointer
         # type.  Note that we assume that the arguments correspond and are just
         # different types.
@@ -5460,6 +5464,8 @@ def _argument_variable(sf, spec, scope, arg, arg_nr):
     saved_is_reference = arg.is_reference
     saved_is_const = arg.is_const
 
+    use_typename = True
+
     if arg.type in (ArgumentType.ASCII_STRING, ArgumentType.LATIN1_STRING, ArgumentType.UTF8_STRING, ArgumentType.SSTRING, ArgumentType.USTRING, ArgumentType.STRING, ArgumentType.WSTRING):
         if not arg.is_reference:
             if nr_derefs == 2:
@@ -5473,6 +5479,10 @@ def _argument_variable(sf, spec, scope, arg, arg_nr):
     else:
         arg.derefs = []
 
+        if _arg_is_small_enum(arg):
+            arg.type = ArgumentType.INT
+            use_typename = False
+
     # Array sizes are always Py_ssize_t.
     if arg.array is ArrayArgument.ARRAY_SIZE:
         arg.type = ArgumentType.SSIZE
@@ -5483,7 +5493,7 @@ def _argument_variable(sf, spec, scope, arg, arg_nr):
         arg.is_const = False
 
     modified_arg_cpp_type = fmt_argument_as_cpp_type(spec, arg,
-            scope=scope_iface_file)
+            scope=scope_iface_file, use_typename=use_typename)
 
     sf.write(f'        {modified_arg_cpp_type} {arg_name}')
 
@@ -5500,8 +5510,14 @@ def _argument_variable(sf, spec, scope, arg, arg_nr):
         if arg.type in (ArgumentType.CLASS, ArgumentType.MAPPED) and (nr_derefs == 0 or arg.is_reference):
             sf.write(f'&{arg_name}def')
         else:
+            if _arg_is_small_enum(arg):
+                sf.write('static_cast<int>(')
+
             sf.write(fmt_value_list_as_cpp_expression(spec, arg.default_value))
 
+            if _arg_is_small_enum(arg):
+                sf.write(')')
+
     sf.write(';\n')
 
     # Some types have supporting variables.
@@ -7504,9 +7520,16 @@ def _get_slot_arg(spec, overload, arg_nr):
 
     arg = overload.py_signature.args[arg_nr]
 
-    dereference = '*' if arg.type in (ArgumentType.CLASS, ArgumentType.MAPPED) and len(arg.derefs) == 0 else ''
+    prefix = suffix = ''
 
-    return dereference + fmt_argument_as_name(spec, arg, arg_nr)
+    if arg.type in (ArgumentType.CLASS, ArgumentType.MAPPED):
+        if len(arg.derefs) == 0:
+            prefix = '*'
+    elif _arg_is_small_enum(arg):
+        prefix = 'static_cast<' + fmt_enum_as_cpp_type(arg.definition) + '>('
+        suffix = ')'
+
+    return prefix + fmt_argument_as_name(spec, arg, arg_nr) + suffix
 
 
 # A map of operators and their complements.
@@ -9001,6 +9024,12 @@ def _optional_ptr(is_ptr, name):
     return name if is_ptr else 'SIP_NULLPTR'
 
 
+def _arg_is_small_enum(arg):
+    """ Return True if an argument refers to a small C++11 enum. """
+
+    return arg.type is ArgumentType.ENUM and arg.definition.enum_base_type is not None
+
+
 # The map of slots to C++ names.
 _SLOT_NAME_MAP = {
     PySlot.ADD:         'operator+',
diff --git a/sipbuild/generator/parser/parser_manager.py b/sipbuild/generator/parser/parser_manager.py
index 9757369..e46a399 100644
--- a/sipbuild/generator/parser/parser_manager.py
+++ b/sipbuild/generator/parser/parser_manager.py
@@ -565,7 +565,8 @@ class ParserManager:
         if self.parsing_virtual or len(scope.dealloc_code) != 0:
             scope.needs_shadow = True
 
-    def add_enum(self, p, symbol, cpp_name, is_scoped, annotations, members):
+    def add_enum(self, p, symbol, cpp_name, is_scoped, enum_base_type,
+            annotations, members):
         """ Create a new enum and add it to the current scope. """
 
         if self.scope_access_specifier is AccessSpecifier.PRIVATE:
@@ -618,7 +619,8 @@ class ParserManager:
 
         w_enum = WrappedEnum(base_type, fq_cpp_name, self.module_state.module,
                 cached_fq_cpp_name=cached_fq_cpp_name, is_scoped=is_scoped,
-                py_name=py_name, scope=self.scope)
+                enum_base_type=enum_base_type, py_name=py_name,
+                scope=self.scope)
 
         if self.scope_access_specifier is AccessSpecifier.PROTECTED:
             if not self._protected_is_public:
diff --git a/sipbuild/generator/parser/rules.py b/sipbuild/generator/parser/rules.py
index 6fd99b7..3a45736 100644
--- a/sipbuild/generator/parser/rules.py
+++ b/sipbuild/generator/parser/rules.py
@@ -2211,28 +2211,52 @@ _ENUM_MEMBER_ANNOTATIONS = (
 
 
 def p_enum_decl(p):
-    "enum_decl : enum opt_enum_key opt_name opt_annos '{' opt_enum_body '}' ';'"
+    "enum_decl : enum opt_enum_key opt_name opt_enum_base opt_annos '{' opt_enum_body '}' ';'"
 
     pm = p.parser.pm
 
     if pm.skipping:
         return
 
-    pm.check_annotations(p, 4, "enum", _ENUM_ANNOTATIONS)
+    pm.check_annotations(p, 5, "enum", _ENUM_ANNOTATIONS)
 
-    pm.add_enum(p, 1, p[3], p[2], p[4], p[6])
+    pm.add_enum(p, 1, p[3], p[2], p[4], p[5], p[7])
 
 
 def p_opt_enum_key(p):
     """opt_enum_key : class
         | struct
-        | union
         | empty"""
 
+    pm = p.parser.pm
+
+    if pm.skipping:
+        return
+
+    if p[1] is not None:
+        pm.cpp_only(p, 1, "enum keys")
+
     # Return True if the enum is scoped.
     p[0] = p[1] is not None
 
 
+def p_opt_enum_base(p):
+    """opt_enum_base : ':' base_type
+        | empty"""
+
+    pm = p.parser.pm
+
+    if pm.skipping:
+        return
+
+    if len(p) == 3:
+        pm.cpp_only(p, 1, "enum bases")
+
+        p[0] = p[2]
+    else:
+        p[0] = None
+
+
 def p_opt_enum_body(p):
     """opt_enum_body : enum_body
         | empty"""
diff --git a/sipbuild/generator/resolver/resolver.py b/sipbuild/generator/resolver/resolver.py
index 7921a5e..9692ba5 100644
--- a/sipbuild/generator/resolver/resolver.py
+++ b/sipbuild/generator/resolver/resolver.py
@@ -196,8 +196,9 @@ def _resolve_module(spec, mod, error_log, final_checks, seen=None):
     for imported_mod in mod.imports:
         _resolve_module(spec, imported_mod, error_log, final_checks, seen=seen)
 
-    # Resolve typedefs, variables and global functions.
+    # Resolve typedefs, enums, variables and global functions.
     _resolve_typedefs(spec, mod, error_log)
+    _resolve_enums(spec, error_log)
     _resolve_variables(spec, mod, error_log)
     _resolve_scope_overloads(spec, mod.overloads, error_log, final_checks)
 
@@ -920,7 +921,6 @@ def _resolve_scope_overloads(spec, overloads, error_log, final_checks,
                     break
 
         if isinstance(scope, WrappedClass):
-
             if scope.deprecated and not overload.deprecated :
                 overload.deprecated = scope.deprecated
 
@@ -928,6 +928,45 @@ def _resolve_scope_overloads(spec, overloads, error_log, final_checks,
                 scope.is_abstract = True
 
 
+# The supported enum base types.  Note that we use the STRING types and the
+# BYTE types because there is no opportunity to apply the /PyInt/ annotation
+# and there can be no confusion about context.  We still need the BYTE types
+# because /PyInt/ could have been specified in a typedef.
+_ENUM_BASE_TYPES = (
+    ArgumentType.STRING,
+    ArgumentType.SSTRING,
+    ArgumentType.USTRING,
+    ArgumentType.BYTE,
+    ArgumentType.SBYTE,
+    ArgumentType.UBYTE,
+    ArgumentType.SHORT,
+    ArgumentType.USHORT,
+    ArgumentType.INT,
+    ArgumentType.UINT,
+)
+
+def _resolve_enums(spec, error_log):
+    """ Resolve the base types for all the enums. """
+
+    for enum in spec.enums:
+        base_type = enum.enum_base_type
+
+        if base_type is None:
+            continue
+
+        _resolve_type(spec, enum.module, enum.scope, base_type, error_log)
+
+        # The current ABI implementations only support enums no larger than an
+        # int.
+        if base_type.type not in _ENUM_BASE_TYPES or len(base_type.derefs) != 0:
+            error_log.log(f"unsupported enum base type",
+                    source_location=base_type.source_location)
+
+        # The default is int.
+        if base_type.type is ArgumentType.INT:
+            enum.enum_base_type = None
+
+
 def _resolve_variables(spec, mod, error_log):
     """ Resolve the data types for the variables of a module. """
 
diff --git a/sipbuild/generator/specification.py b/sipbuild/generator/specification.py
index 79c9e3d..9daea9a 100644
--- a/sipbuild/generator/specification.py
+++ b/sipbuild/generator/specification.py
@@ -1670,6 +1670,9 @@ class WrappedEnum:
     # The defining module.
     module: Module
 
+    # The C++11 enum base type.
+    enum_base_type: Optional[Argument] = None
+
     # The cached fully qualified C++ name.
     cached_fq_cpp_name: Optional[CachedName] = None
 
diff --git a/test/template_superclasses/test_template_superclasses.py b/test/template_superclasses/test_template_superclasses.py
deleted file mode 100644
index 32fb877..0000000
--- a/test/template_superclasses/test_template_superclasses.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# SPDX-License-Identifier: BSD-2-Clause
-
-# Copyright (c) 2025 Phil Thompson <phil at riverbankcomputing.com>
-
-
-from utils import SIPTestCase
-
-
-class TemplateSuperclassesTestCase(SIPTestCase):
-    """ Test the support for template arguments as super-classes in a template
-    class.  (See issue/12.)
-    """
-
-    def test_TemplateSuperclasses(self):
-        """ Test the use of template arguments as super-classes. """
-
-        from template_superclasses_module import (AValue, AValueWrapper,
-                BaseClass, BValue, BValueWrapper)
-
-        self.assertTrue(issubclass(AValueWrapper, BaseClass))
-        a = AValueWrapper()
-        self.assertIsInstance(a.getValue(), AValue)
-
-        self.assertTrue(issubclass(BValueWrapper, BaseClass))
-        b = BValueWrapper()
-        self.assertIsInstance(b.getValue(), BValue)
diff --git a/test/template_superclasses/__init__.py b/test/templates/__init__.py
similarity index 100%
rename from test/template_superclasses/__init__.py
rename to test/templates/__init__.py
diff --git a/test/template_superclasses/template_superclasses_module.sip b/test/templates/templates_module.sip
similarity index 57%
rename from test/template_superclasses/template_superclasses_module.sip
rename to test/templates/templates_module.sip
index e139e76..a179bed 100644
--- a/test/template_superclasses/template_superclasses_module.sip
+++ b/test/templates/templates_module.sip
@@ -1,7 +1,7 @@
-// The SIP implementation of the template_superclasses_module test module.
+// The SIP implementation of the templates_module test module.
 
 
-%Module(name=template_superclasses_module)
+%Module(name=templates_module)
 
 
 %ModuleHeaderCode
@@ -35,6 +35,28 @@ class BValue
 typedef ExtendedValueWrapper<AValue> AValueWrapper;
 typedef ExtendedValueWrapper<BValue> BValueWrapper;
 
+
+template <class T3>
+class SimpleWrapper : public T3
+{
+};
+
+class CValue
+{
+};
+
+typedef SimpleWrapper<CValue> CWrapper;
+
+
+namespace Scope
+{
+    class Nested
+    {
+    };
+};
+
+typedef SimpleWrapper<Scope::Nested> NestedWrapper;
+
 %End
 
 
@@ -42,7 +64,6 @@ class BaseClass
 {
 };
 
-
 template <T1>
 class ValueWrapper : public BaseClass
 {
@@ -50,22 +71,40 @@ public:
     T1 getValue() const;
 };
 
-
 template <T2>
 class ExtendedValueWrapper : public ValueWrapper<T2>
 {
 };
 
-
 class AValue
 {
 };
 
-
 class BValue
 {
 };
 
-
 typedef ExtendedValueWrapper<AValue> AValueWrapper;
 typedef ExtendedValueWrapper<BValue> BValueWrapper;
+
+
+template <T3>
+class SimpleWrapper : public T3
+{
+};
+
+class CValue
+{
+};
+
+typedef SimpleWrapper<CValue> CWrapper;
+
+
+namespace Scope
+{
+    class Nested
+    {
+    };
+};
+
+typedef SimpleWrapper<Scope::Nested> NestedWrapper;
diff --git a/test/templates/test_templates.py b/test/templates/test_templates.py
new file mode 100644
index 0000000..311599c
--- /dev/null
+++ b/test/templates/test_templates.py
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+# Copyright (c) 2025 Phil Thompson <phil at riverbankcomputing.com>
+
+
+from utils import SIPTestCase
+
+
+class TemplatesTestCase(SIPTestCase):
+    """ Test the support for templates. """
+
+    def test_SimpleTemplateSuperclasses(self):
+        """ Test the use of simple template arguments as super-classes. """
+
+        from templates_module import CValue, CWrapper
+
+        self.assertTrue(issubclass(CWrapper, CValue))
+
+    def test_ScopedTemplateSuperclasses(self):
+        """ Test the use of simple template arguments as super-classes within
+        a scope.
+        """
+
+        from templates_module import NestedWrapper, Scope
+
+        self.assertTrue(issubclass(NestedWrapper, Scope.Nested))
+
+    def test_TemplateSuperclasses(self):
+        """ Test the use of template arguments as super-classes. """
+
+        from templates_module import (AValue, AValueWrapper, BaseClass, BValue,
+                BValueWrapper)
+
+        self.assertTrue(issubclass(AValueWrapper, BaseClass))
+        a = AValueWrapper()
+        self.assertIsInstance(a.getValue(), AValue)
+
+        self.assertTrue(issubclass(BValueWrapper, BaseClass))
+        b = BValueWrapper()
+        self.assertIsInstance(b.getValue(), BValue)



More information about the Neon-commits mailing list