[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