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

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


Git commit 73d9a4fd11d39a83be035f07f5d5bc49f272c591 by Dmitry Shachnev.
Committed on 08/12/2024 at 14:12.
Pushed by carlosdem into branch 'Neon/release'.

New upstream version 6.9.0

M  +3    -3    .git_archival.txt
M  +2    -2    README.md
M  +2    -1    docs/abi_12.rst
M  +8    -6    docs/annotations.rst
M  +1    -1    docs/conf.py
M  +1    -1    docs/introduction.rst
M  +39   -0    docs/releases.md
M  +1    -1    pyproject.toml
C  +2    -2    sipbuild/generator/outputs/code/__init__.py [from: sipbuild/py_versions.py - 055% similarity]
R  +64   -37   sipbuild/generator/outputs/code/code.py [from: sipbuild/generator/outputs/code.py - 098% similarity]
M  +3    -3    sipbuild/generator/outputs/formatters/argument.py
M  +25   -6    sipbuild/generator/outputs/pyi.py
M  +59   -15   sipbuild/generator/outputs/type_hints.py
M  +8    -4    sipbuild/generator/parser/annotations.py
M  +19   -7    sipbuild/generator/parser/parser_manager.py
M  +6    -3    sipbuild/generator/parser/rules.py
M  +13   -11   sipbuild/generator/resolver/resolver.py
M  +67   -66   sipbuild/generator/specification.py
M  +12   -0    sipbuild/generator/utils.py
M  +3    -1    sipbuild/module/abi_version.py
M  +0    -6    sipbuild/module/source/12/apiversions.c
M  +14   -3    sipbuild/module/source/12/sip.h.in
M  +1    -0    sipbuild/module/source/12/sipint.h
M  +29   -40   sipbuild/module/source/12/siplib.c
M  +9    -4    sipbuild/module/source/13/sip.h.in
M  +25   -34   sipbuild/module/source/13/sip_core.c
M  +2    -1    sipbuild/module/source/13/sip_core.h
M  +1    -1    sipbuild/py_versions.py

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

diff --git a/.git_archival.txt b/.git_archival.txt
index ecc645b..10b638a 100644
--- a/.git_archival.txt
+++ b/.git_archival.txt
@@ -1,3 +1,3 @@
-node: 10cb0c23cd25756769a1a388ab0976245e7f6590
-node-date: 2024-07-12T12:19:04+01:00
-describe-name: 6.8.6
+node: 179c169514095effa9653b254b11d160b2ad923a
+node-date: 2024-12-05T15:01:49Z
+describe-name: 6.9.0
diff --git a/README.md b/README.md
index fea762f..d25ce4c 100644
--- a/README.md
+++ b/README.md
@@ -27,8 +27,8 @@ corresponding header file.
 The `sip` module provides support functions to the automatically generated
 code.  The `sip` module is installed as part of the same Python package as the
 generated extension modules.  Unlike the extension modules the `sip` module is
-specific to a particular version of Python (e.g. v3.8, v3.9, v3.10, v3.11,
-v3.12).
+specific to a particular version of Python (e.g. v3.9, v3.10, v3.11, v3.12,
+v3.13).
 
 SIP makes it easy to exploit existing C or C++ libraries in a productive
 interpretive programming environment.  SIP also makes it easy to take a Python
diff --git a/docs/abi_12.rst b/docs/abi_12.rst
index 2ed54a9..6467ca8 100644
--- a/docs/abi_12.rst
+++ b/docs/abi_12.rst
@@ -958,7 +958,8 @@ module, that can be used by handwritten code in specification files.
 
 .. c:function:: PyFrameObject *sipGetFrame(int depth)
 
-    This retrieves a frame object from the current execution stack.
+    This retrieves a borrowed reference to the frame object from the current
+    execution stack.
 
     .. note::
         On PyPy this will always return NULL.
diff --git a/docs/annotations.rst b/docs/annotations.rst
index 3d830d6..a226870 100644
--- a/docs/annotations.rst
+++ b/docs/annotations.rst
@@ -398,9 +398,11 @@ Class Annotations
 
 .. class-annotation:: Deprecated
 
-    This boolean annotation is used to specify that the class is deprecated.
-    It is the equivalent of annotating all the class's constructors, function
-    and methods as being deprecated.
+    This optional string annotation is used to specify that the class is
+    deprecated.  Any string is appended to the deprecation warning and is
+    usually used to suggest an appropriate alternative.  It is the equivalent
+    of annotating all the class's constructors, function and methods as being
+    deprecated.
 
 
 .. class-annotation:: FileExtension
@@ -711,9 +713,9 @@ Function Annotations
 
 .. function-annotation:: Deprecated
 
-    This boolean annotation is used to specify that the constructor or function
-    is deprecated.  A deprecation warning is issued whenever the constructor or
-    function is called.
+    This optional string annotation is used to specify that the constructor or
+    function is deprecated.  Any string is appended to the deprecation warning
+    and is usually used to suggest an appropriate alternative.
 
 
 .. function-annotation:: DisallowNone
diff --git a/docs/conf.py b/docs/conf.py
index 799d047..c10b11b 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.8.6'
+version = 'v6.9.0'
 
 
 # -- General configuration ---------------------------------------------------
diff --git a/docs/introduction.rst b/docs/introduction.rst
index c2322d9..9ac7d0d 100644
--- a/docs/introduction.rst
+++ b/docs/introduction.rst
@@ -8,7 +8,7 @@ bindings for the Qt GUI toolkit - but is suitable for generating bindings for
 any C or C++ library.  SIP can also be used write self contained extension
 modules, i.e. without a library to be wrapped.
 
-This version of SIP generates bindings for Python v3.8 and later.
+This version of SIP generates bindings for Python v3.9 and later.
 
 SIP is hosted at `GitHub <https://github.com/Python-SIP/sip>`__.
 
diff --git a/docs/releases.md b/docs/releases.md
index 0cee5a6..21690c2 100644
--- a/docs/releases.md
+++ b/docs/releases.md
@@ -1,6 +1,45 @@
 # Release Notes
 
 
+## v6.9.0
+
+### Removal of support for Python v3.8
+
+- Generated bindings now require Python v3.9 or later.
+- Type hints now conform to PEP 585.
+- The latest `sip` module ABI versions are v12.16 and v13.9.
+
+Resolves [#37](https://github.com/Python-SIP/sip/issues/37)
+
+### Added support for the `deprecated()` decorator in `.pyi` files
+
+- `.pyi` files now use the `deprecated()` decorator when the `/Deprecated/`
+  annotation is specified.
+- The `/Deprecated/` annotation may now specify an optional string which will
+  be appended to the default deprecation warning.
+
+Resolves [#8](https://github.com/Python-SIP/sip/issues/8)
+
+### PyQt-specific support for registering `QFlags` types
+
+Calls to `qMetaTypeId()` for all `QFlags` mapped types are now automatically
+generated for PyQt using ABI v13.
+
+Resolves [#43](https://github.com/Python-SIP/sip/issues/43)
+
+### Bug fixes
+
+- The handling of the SIP versions timeline was fixed.  (Resolves
+  [#47](https://github.com/Python-SIP/sip/issues/47))
+- Annotations are now only validated if they are known to be needed.
+- The buffer protocol support for byte objects was fixed. (Resolves
+  [#36](https://github.com/Python-SIP/sip/issues/36))
+- All outstanding compiler warnings were eliminated when building the `sip`
+  module.  (Resolves [#32](https://github.com/Python-SIP/sip/issues/32))
+- Redundant `%` in trace output of `this` pointer in were removed.  (Pull
+  request [#33](https://github.com/Python-SIP/sip/pull/33))
+
+
 ## v6.8.6
 
 ### Handle single number macOS deployment targets
diff --git a/pyproject.toml b/pyproject.toml
index 156ee38..1d9fab9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -13,7 +13,7 @@ description = "A Python bindings generator for C/C++ libraries"
 readme = "README.md"
 urls.homepage = "https://github.com/Python-SIP/sip"
 dependencies = ["packaging", "setuptools", "tomli; python_version<'3.11'"]
-requires-python = ">=3.8"
+requires-python = ">=3.9"
 license = {file = "LICENSE"}
 classifiers = ["License :: OSI Approved :: BSD License"]
 dynamic = ["version"]
diff --git a/sipbuild/py_versions.py b/sipbuild/generator/outputs/code/__init__.py
similarity index 55%
copy from sipbuild/py_versions.py
copy to sipbuild/generator/outputs/code/__init__.py
index 863f801..f2baf4b 100644
--- a/sipbuild/py_versions.py
+++ b/sipbuild/generator/outputs/code/__init__.py
@@ -3,5 +3,5 @@
 # Copyright (c) 2024 Phil Thompson <phil at riverbankcomputing.com>
 
 
-# The oldest supported minor version of Python v3.
-OLDEST_SUPPORTED_MINOR = 8
+# Publish the API.  This is private to the rest of sip.
+from .code import output_code
diff --git a/sipbuild/generator/outputs/code.py b/sipbuild/generator/outputs/code/code.py
similarity index 98%
rename from sipbuild/generator/outputs/code.py
rename to sipbuild/generator/outputs/code/code.py
index dbadfce..fabe04d 100644
--- a/sipbuild/generator/outputs/code.py
+++ b/sipbuild/generator/outputs/code/code.py
@@ -5,21 +5,22 @@
 
 import os
 
-from ...exceptions import UserException
-from ...version import SIP_VERSION_STR
+from ....exceptions import UserException
+from ....version import SIP_VERSION_STR
 
-from ..python_slots import (is_hash_return_slot, is_inplace_number_slot,
+from ...python_slots import (is_hash_return_slot, is_inplace_number_slot,
         is_inplace_sequence_slot, is_int_arg_slot, is_int_return_slot,
         is_multi_arg_slot, is_number_slot, is_rich_compare_slot,
         is_ssize_return_slot, is_void_return_slot, is_zero_arg_slot)
-from ..scoped_name import STRIP_GLOBAL, STRIP_NONE
-from ..specification import (AccessSpecifier, Argument, ArgumentType,
+from ...scoped_name import STRIP_GLOBAL, STRIP_NONE
+from ...specification import (AccessSpecifier, Argument, ArgumentType,
         ArrayArgument, CodeBlock, DocstringSignature, GILAction, IfaceFileType,
         KwArgs, MappedType, PyQtMethodSpecifier, PySlot, QualifierType,
         Transfer, ValueType, WrappedClass, WrappedEnum)
-from ..utils import find_method, py_as_int, same_signature
+from ...utils import (abi_has_deprecated_message, abi_version_check,
+        find_method, py_as_int, same_signature)
 
-from .formatters import (fmt_argument_as_cpp_type, fmt_argument_as_name,
+from ..formatters import (fmt_argument_as_cpp_type, fmt_argument_as_name,
         fmt_class_as_scoped_name, fmt_copying, fmt_enum_as_cpp_type,
         fmt_scoped_py_name, fmt_signature_as_cpp_declaration,
         fmt_signature_as_cpp_definition, fmt_signature_as_type_hint,
@@ -181,7 +182,6 @@ f'''
 #define sipConvertFromVoidPtrAndSize    sipAPI_{module_name}->api_convert_from_void_ptr_and_size
 #define sipConvertFromConstVoidPtrAndSize   sipAPI_{module_name}->api_convert_from_const_void_ptr_and_size
 #define sipWrappedTypeName(wt)      ((wt)->wt_td->td_cname)
-#define sipDeprecated               sipAPI_{module_name}->api_deprecated
 #define sipGetReference             sipAPI_{module_name}->api_get_reference
 #define sipKeepReference            sipAPI_{module_name}->api_keep_reference
 #define sipRegisterProxyResolver    sipAPI_{module_name}->api_register_proxy_resolver
@@ -242,6 +242,16 @@ f'''
 
     # These are dependent on the specific ABI version.
     if spec.abi_version >= (13, 0):
+        # ABI v13.9 and later
+        if spec.abi_version >= (13, 9):
+            sf.write(
+f'''#define sipDeprecated               sipAPI_{module_name}->api_deprecated_13_9
+''')
+        else:
+            sf.write(
+f'''#define sipDeprecated               sipAPI_{module_name}->api_deprecated
+''')
+                    
         # ABI v13.6 and later.
         if spec.abi_version >= (13, 6):
             sf.write(
@@ -253,7 +263,7 @@ f'''#define sipPyTypeDictRef            sipAPI_{module_name}->api_py_type_dict_r
             sf.write(
 f'''#define sipNextExceptionHandler     sipAPI_{module_name}->api_next_exception_handler
 ''')
-
+            
         # ABI v13.0 and later. */
         sf.write(
 f'''#define sipIsEnumFlag               sipAPI_{module_name}->api_is_enum_flag
@@ -262,6 +272,16 @@ f'''#define sipIsEnumFlag               sipAPI_{module_name}->api_is_enum_flag
 #define sipReleaseTypeUS            sipAPI_{module_name}->api_release_type_us
 ''')
     else:
+        # ABI v12.16 and later
+        if spec.abi_version >= (12, 16):
+            sf.write(
+f'''#define sipDeprecated               sipAPI_{module_name}->api_deprecated_12_16
+''')
+        else:
+            sf.write(
+f'''#define sipDeprecated               sipAPI_{module_name}->api_deprecated
+''')
+            
         # ABI v12.13 and later.
         if spec.abi_version >= (12, 13):
             sf.write(
@@ -1195,8 +1215,8 @@ f'''
     sipExportedExceptions_{module_name}[{module.nr_exceptions}] = SIP_NULLPTR;
 ''')
 
-    # Generate the enum meta-type registrations for PyQt6 so that they can be
-    # used in queued connections.
+    # Generate the enum and QFlag meta-type registrations for PyQt6.  (It may
+    # be possible to create these dynamically on demand.)
     if _pyqt6(spec):
         for enum in spec.enums:
             if enum.module is not module or enum.fq_cpp_name is None:
@@ -1210,6 +1230,17 @@ f'''
 
             sf.write(f'    qMetaTypeId<{enum.fq_cpp_name.as_cpp}>();\n')
 
+        for mapped_type in spec.mapped_types:
+            if mapped_type.iface_file.module is not module:
+                continue
+
+            if mapped_type.pyqt_flags == 0:
+                continue
+
+            mapped_type_type = fmt_argument_as_cpp_type(spec, mapped_type.type,
+                    plain=True, no_derefs=True)
+            sf.write(f'    qMetaTypeId<{mapped_type_type}>();\n')
+
     # Generate any post-initialisation code. */
     sf.write_code(module.postinitialisation_code)
 
@@ -3810,7 +3841,7 @@ def _shadow_code(sf, spec, bindings, klass):
             args = fmt_signature_as_cpp_declaration(spec, ctor.cpp_signature,
                     scope=klass.iface_file)
 
-            sf.write(f'    sipTrace(SIP_TRACE_CTORS, "sip{klass_name}::sip{klass_name}({args}){throw_specifier} (this=0x%%08x)\\n", this);\n\n')
+            sf.write(f'    sipTrace(SIP_TRACE_CTORS, "sip{klass_name}::sip{klass_name}({args}){throw_specifier} (this=0x%08x)\\n", this);\n\n')
 
         if nr_virtuals > 0:
             sf.write('    memset(sipPyMethods, 0, sizeof (sipPyMethods));\n')
@@ -3824,7 +3855,7 @@ def _shadow_code(sf, spec, bindings, klass):
         sf.write(f'\nsip{klass_name}::~sip{klass_name}(){throw_specifier}\n{{\n')
 
         if bindings.tracing:
-            sf.write(f'    sipTrace(SIP_TRACE_DTORS, "sip{klass_name}::~sip{klass_name}(){throw_specifier} (this=0x%%08x)\\n", this);\n\n')
+            sf.write(f'    sipTrace(SIP_TRACE_DTORS, "sip{klass_name}::~sip{klass_name}(){throw_specifier} (this=0x%08x)\\n", this);\n\n')
 
         if klass.dtor_virtual_catcher_code is not None:
             sf.write_code(klass.dtor_virtual_catcher_code)
@@ -3940,7 +3971,7 @@ def _virtual_catcher(sf, spec, bindings, klass, virtual_overload, virt_nr):
     if bindings.tracing:
         args = fmt_signature_as_cpp_declaration(spec, overload.cpp_signature,
                 scope=klass.iface_file)
-        sf.write(f'    sipTrace(SIP_TRACE_CATCHERS, "{result_type} sip{klass_name}::{overload_cpp_name}({args}){const}{throw_specifier} (this=0x%%08x)\\n", this);\n\n')
+        sf.write(f'    sipTrace(SIP_TRACE_CATCHERS, "{result_type} sip{klass_name}::{overload_cpp_name}({args}){const}{throw_specifier} (this=0x%08x)\\n", this);\n\n')
 
     _restore_protections(protection_state)
 
@@ -6234,13 +6265,16 @@ def _constructor_call(sf, spec, bindings, klass, ctor, error_flag,
     elif old_error_flag:
         sf.write('            int sipIsErr = 0;\n\n')
 
-    if ctor.deprecated:
+    if ctor.deprecated is not None:
         # Note that any temporaries will leak if an exception is raised.
-        sf.write(
-f'''            if (sipDeprecated({_cached_name_ref(klass.py_name)}, SIP_NULLPTR) < 0)
-                return SIP_NULLPTR;
 
-''')
+        if abi_has_deprecated_message(spec):
+            str_deprecated_message = f'"{ctor.deprecated}"' if ctor.deprecated else 'SIP_NULLPTR'
+            sf.write(f'            if (sipDeprecated({_cached_name_ref(klass.py_name)}, SIP_NULLPTR, {str_deprecated_message}) < 0)\n')
+        else:
+            sf.write(f'            if (sipDeprecated({_cached_name_ref(klass.py_name)}, SIP_NULLPTR) < 0)\n')
+            
+        sf.write(f'                return SIP_NULLPTR;\n\n')
 
     # Call any pre-hook.
     if ctor.prehook is not None:
@@ -7070,16 +7104,18 @@ f'''            if (!sipOrigSelf)
 
 ''')
 
-    if overload.deprecated:
+    if overload.deprecated is not None:
         scope_py_name_ref = _cached_name_ref(scope.py_name) if scope is not None and scope.py_name is not None else 'SIP_NULLPTR'
         error_return = '-1' if is_void_return_slot(py_slot) or is_int_return_slot(py_slot) or is_ssize_return_slot(py_slot) or is_hash_return_slot(py_slot) else 'SIP_NULLPTR'
 
         # Note that any temporaries will leak if an exception is raised.
-        sf.write(
-f'''            if (sipDeprecated({scope_py_name_ref}, {_cached_name_ref(overload.common.py_name)}) < 0)
-                return {error_return};
-
-''')
+        if abi_has_deprecated_message(spec):
+            str_deprecated_message = f'"{overload.deprecated}"' if overload.deprecated else 'SIP_NULLPTR'
+            sf.write(f'            if (sipDeprecated({scope_py_name_ref}, {_cached_name_ref(overload.common.py_name)}, {str_deprecated_message}) < 0)\n')
+        else:
+            sf.write(f'            if (sipDeprecated({scope_py_name_ref}, {_cached_name_ref(overload.common.py_name)}) < 0)\n')
+        
+        sf.write(f'                return {error_return};\n\n')
 
     # Call any pre-hook.
     if overload.prehook is not None:
@@ -8829,28 +8865,19 @@ f'''            if ({index_arg} < 0 || {index_arg} >= sipCpp->{klass.len_cpp_nam
 def _abi_has_next_exception_handler(spec):
     """ Return True if the ABI implements sipNextExceptionHandler(). """
 
-    return _abi_version_check(spec, (12, 9), (13, 1))
-
+    return abi_version_check(spec, (12, 9), (13, 1))
 
 def _abi_has_working_char_conversion(spec):
     """ Return True if the ABI has working char to/from a Python integer
     converters (ie. char is not assumed to be signed).
     """
 
-    return _abi_version_check(spec, (12, 15), (13, 8))
-
+    return abi_version_check(spec, (12, 15), (13, 8))
 
 def _abi_supports_array(spec):
     """ Return True if the ABI supports sip.array. """
 
-    return _abi_version_check(spec, (12, 11), (13, 4))
-
-
-def _abi_version_check(spec, min_12, min_13):
-    """ Return True if the ABI version meets minimum version requirements. """
-
-    return spec.abi_version >= min_13 or (min_12 <= spec.abi_version < (13, 0))
-
+    return abi_version_check(spec, (12, 11), (13, 4))
 
 def _cached_name_ref(cached_name, as_nr=False):
     """ Return a reference to a cached name. """
diff --git a/sipbuild/generator/outputs/formatters/argument.py b/sipbuild/generator/outputs/formatters/argument.py
index 9e3f678..44ebb25 100644
--- a/sipbuild/generator/outputs/formatters/argument.py
+++ b/sipbuild/generator/outputs/formatters/argument.py
@@ -455,13 +455,13 @@ def _py_arg(spec, arg, pep484, as_xml):
         name = 'typing.Any' if pep484 else 'Any'
 
     elif type is ArgumentType.PYTUPLE:
-        name = 'typing.Tuple' if pep484 else 'Tuple'
+        name = 'tuple'
 
     elif type is ArgumentType.PYLIST:
-        name = 'typing.List' if pep484 else 'List'
+        name = 'list'
 
     elif type is ArgumentType.PYDICT:
-        name = 'typing.Dict' if pep484 else 'Dict'
+        name = 'dict'
 
     elif type is ArgumentType.PYCALLABLE:
         name = 'typing.Callable[..., None]' if pep484 else 'Callable[..., None]'
diff --git a/sipbuild/generator/outputs/pyi.py b/sipbuild/generator/outputs/pyi.py
index f0a5724..abbe454 100644
--- a/sipbuild/generator/outputs/pyi.py
+++ b/sipbuild/generator/outputs/pyi.py
@@ -50,20 +50,28 @@ def _module(pf, spec):
 
     # Generate the imports. Note that we assume the super-types are the
     # standard SIP ones.
+    stdlib_imports = ['collections', 're', 'typing']
+
     if spec.abi_version >= (13, 0):
         for enum in spec.enums:
             if enum.module is spec.module:
-                first = _separate(pf, first=first)
-                pf.write('import enum\n')
+                stdlib_imports.append('enum')
                 break
 
-    if spec.sip_module:
+    if stdlib_imports:
         first = _separate(pf, first=first)
+        pf.write('import ' + ', '.join(stdlib_imports) + '\n')
         pf.write(
-f'''import typing
-
-import {spec.sip_module}
+f'''
+try:
+    from warnings import deprecated
+except ImportError:
+    pass
 ''')
+            
+    if spec.sip_module:
+        first = _separate(pf, first=first, minimum=1)
+        pf.write(f'import {spec.sip_module}\n')
 
     imports = []
 
@@ -168,6 +176,9 @@ def _class(pf, spec, klass, defined, indent=0):
 
         s = _indent(indent)
 
+        if klass.deprecated is not None:
+            s += f'@deprecated("{klass.deprecated}")\n' + _indent(indent)
+
         s += f'class {klass.py_name.name}('
 
         if klass.superclasses:
@@ -328,6 +339,10 @@ def _ctor(pf, spec, ctor, overloaded, defined, indent):
         s += '@typing.overload\n'
         pf.write(s)
 
+    if ctor.deprecated is not None:
+        deprecated_message = f'"""{ctor.deprecated}"""'
+        pf.write(_indent(indent) + f'@deprecated({deprecated_message})\n')
+        
     s = _indent(indent)
     s += 'def __init__'
     s += _python_signature(spec, ctor.py_signature, defined)
@@ -518,6 +533,10 @@ def _overload(pf, spec, overload, overloaded, first_overload, is_method,
     if is_method and overload.is_static:
         pf.write(_indent(indent) + '@staticmethod\n')
 
+    if overload.deprecated is not None:
+        deprecated_message = f'"""{overload.deprecated}"""'
+        pf.write(_indent(indent) + f'@deprecated({deprecated_message})\n')
+        
     py_name = overload.common.py_name.name
     py_signature = overload.py_signature
 
diff --git a/sipbuild/generator/outputs/type_hints.py b/sipbuild/generator/outputs/type_hints.py
index a81c653..2381850 100644
--- a/sipbuild/generator/outputs/type_hints.py
+++ b/sipbuild/generator/outputs/type_hints.py
@@ -5,7 +5,7 @@
 
 from dataclasses import dataclass, field
 from enum import auto, Enum
-from typing import List, Optional, Union
+from typing import Optional, Union
 from weakref import WeakKeyDictionary
 
 from ...exceptions import UserException
@@ -14,14 +14,46 @@ from ..scoped_name import ScopedName
 from ..specification import MappedType, WrappedClass, WrappedEnum
 
 
-# The types defined in the typing module.
-_TYPING_MODULE = (
-    'Any', 'NoReturn', 'Tuple', 'Union', 'Optional', 'Callable', 'Type',
-    'Literal', 'ClassVar', 'Final', 'Annotated', 'AnyStr', 'Protocol',
-    'NamedTuple', 'Dict', 'List', 'Set', 'FrozenSet', 'IO', 'TextIO',
-    'BinaryIO', 'Pattern', 'Match', 'Text', 'Iterable', 'Iterator',
-    'Generator', 'Mapping', 'Sequence',
-)
+# The pre-PEP 585 types.
+_LEGACY_TYPES = ('Dict', 'FrozenSet', 'List', 'Set', 'Tuple', 'Type')
+
+# The modules defining standard types.
+_ABC_MODULE = 'collections.abc'
+_RE_MODULE = 're'
+_TYPING_MODULE = 'typing'
+
+# The standard types and their defining modules.
+_STANDARD_TYPES = {
+    'Annotated':    _TYPING_MODULE,
+    'Any':          _TYPING_MODULE,
+    'AnyStr':       _TYPING_MODULE,
+    'BinaryIO':     _TYPING_MODULE,
+    'Callable':     _ABC_MODULE,
+    'ClassVar':     _TYPING_MODULE,
+    'dict':         None,
+    'Final':        _TYPING_MODULE,
+    'frozenset':    None,
+    'Generator':    _ABC_MODULE,
+    'IO':           _TYPING_MODULE,
+    'Iterable':     _ABC_MODULE,
+    'Iterator':     _ABC_MODULE,
+    'list':         None,
+    'Literal':      _TYPING_MODULE,
+    'Mapping':      _ABC_MODULE,
+    'Match':        _RE_MODULE,
+    'NamedTuple':   _TYPING_MODULE,
+    'NoReturn':     _TYPING_MODULE,
+    'Optional':     _TYPING_MODULE,
+    'Pattern':      _RE_MODULE,
+    'Protocol':     _TYPING_MODULE,
+    'Sequence':     _ABC_MODULE,
+    'set':          None,
+    'Text':         _TYPING_MODULE,
+    'TextIO':       _TYPING_MODULE,
+    'tuple':        None,
+    'type':         None,
+    'Union':        _TYPING_MODULE,
+}
 
 
 class NodeType(Enum):
@@ -69,11 +101,14 @@ class TypeHintNode:
     type: NodeType
 
     # The list of child nodes.
-    children: Optional[List['TypeHintNode']] = None
+    children: Optional[list['TypeHintNode']] = None
 
     # The type-dependent definition.
     definition: Optional[Union[str, MappedType, WrappedClass, WrappedEnum]] = None
 
+    # The PEP 484 definition if it is a TYPING node.
+    pep484_definition: Optional[str] = None
+
 
 class TypeHintManager:
     """ A manager for type hints on behalf of a Specification object. """
@@ -214,8 +249,14 @@ class TypeHintManager:
             # Get the name. */
             name = text[name_start:name_end]
 
-            # See if it is an object in the typing module.
-            if name in _TYPING_MODULE:
+            # Convert pre-PEP 585 types.
+            if name in _LEGACY_TYPES:
+                name = name.lower()
+
+            # See if it is a standard type.
+            try:
+                type_module = _STANDARD_TYPES[name]
+
                 if name == 'Union':
                     # If there are no children assume it is because they have
                     # been omitted.
@@ -234,9 +275,12 @@ class TypeHintManager:
 
                     children = flattened
 
+                pep484_definition = name if type_module is None else type_module + '.' + name
+
                 node = TypeHintNode(NodeType.TYPING, children=children,
-                        definition=name)
-            else:
+                        definition=name, pep484_definition=pep484_definition)
+
+            except KeyError:
                 # Search for the type.
                 node = self._lookup_type(name, out, children)
         else:
@@ -283,7 +327,7 @@ class TypeHintManager:
             if node.definition is None:
                 s = ''
             elif pep484:
-                s = 'typing.' + node.definition
+                s = node.pep484_definition
             else:
                 s = node.definition
 
diff --git a/sipbuild/generator/parser/annotations.py b/sipbuild/generator/parser/annotations.py
index 0bdb5f5..05d2838 100644
--- a/sipbuild/generator/parser/annotations.py
+++ b/sipbuild/generator/parser/annotations.py
@@ -125,8 +125,12 @@ def validate_name(pm, p, symbol, name, value, *, allow_dots, optional):
 name = bind(validate_name, allow_dots=False, optional=False)
 
 
-def validate_string(pm, p, symbol, name, value):
-    """ Return a valid string value. """
+def validate_string(pm, p, symbol, name, value, optional):
+    """ Return a valid, possibly optional, string value. """
+
+    if value is None:
+        if optional:
+            return ''
 
     if not isinstance(value, str):
         raise InvalidAnnotation(name, "must be a quoted string", use='')
@@ -149,7 +153,7 @@ def validate_string(pm, p, symbol, name, value):
     # No value was selected so ignore the annotation completely.
     return None
 
-string = bind(validate_string)
+string = bind(validate_string, optional=False)
 
 
 def validate_string_list(pm, p, symbol, name, value):
@@ -177,7 +181,7 @@ _ANNOTATION_TYPES = {
     'BaseType':                 name(),
     'Capsule':                  boolean(),
     'Constrained':              boolean(),
-    'Deprecated':               boolean(),
+    'Deprecated':               string(optional=True),
     'Default':                  boolean(),
     'DelayDtor':                boolean(),
     'DisallowNone':             boolean(),
diff --git a/sipbuild/generator/parser/parser_manager.py b/sipbuild/generator/parser/parser_manager.py
index 8d3cb13..d50f0f6 100644
--- a/sipbuild/generator/parser/parser_manager.py
+++ b/sipbuild/generator/parser/parser_manager.py
@@ -21,15 +21,14 @@ from ..specification import (AccessSpecifier, Argument, ArgumentType,
         SourceLocation, Specification, Transfer, TypeHints, WrappedClass,
         WrappedException, WrappedEnum, WrappedEnumMember)
 from ..templates import encoded_template_name, same_template_signature
-from ..utils import (argument_as_str, cached_name, find_iface_file,
-        normalised_scoped_name, same_base_type)
+from ..utils import (abi_has_deprecated_message, argument_as_str, cached_name,
+        find_iface_file, normalised_scoped_name, same_base_type)
 
 from . import rules
 from . import tokens
 from .annotations import InvalidAnnotation, validate_annotation_value
 from .ply import lex, yacc
 
-
 class ParserManager:
     """ This object manages the actual lexer and parser objects providing them
     with state and utility functions.
@@ -182,7 +181,7 @@ class ParserManager:
                 else:
                     klass.default_ctor = last_resort
 
-            klass.deprecated = annotations.get('Deprecated', False)
+            klass.deprecated = self.get_deprecated(p, symbol, annotations)
 
             if klass.convert_to_type_code is not None and annotations.get('AllowNone', False):
                 klass.handles_none = True
@@ -490,7 +489,7 @@ class ParserManager:
 
         ctor.docstring = docstring
         ctor.gil_action = self._get_gil_action(p, symbol, annotations)
-        ctor.deprecated = annotations.get('Deprecated', False)
+        ctor.deprecated = self.get_deprecated(p, symbol, annotations)
 
         if access_specifier is not AccessSpecifier.PRIVATE:
             ctor.kw_args = self._get_kw_args(p, symbol, annotations,
@@ -729,7 +728,7 @@ class ParserManager:
 
         overload.gil_action = self._get_gil_action(p, symbol, annotations)
         overload.factory = annotations.get('Factory', False)
-        overload.deprecated = annotations.get('Deprecated', False)
+        overload.deprecated = self.get_deprecated(p, symbol, annotations)
         overload.new_thread = annotations.get('NewThread', False)
         overload.transfer = self.get_transfer(p, symbol, annotations)
 
@@ -1289,7 +1288,7 @@ class ParserManager:
 
             return True
 
-        # See if there is a selected qualifier withing range.
+        # See if there is a selected qualifier within range.
         for qual in module.qualifiers:
             if qual.type is QualifierType.TIME and qual.timeline == timeline and qual.name in self.tags:
                 if lower_qual is not None and qual.order < lower_qual.order:
@@ -1532,6 +1531,19 @@ class ParserManager:
 
         self._error_log.log(text, self.get_source_location(p, symbol))
 
+    def get_deprecated(self, p, symbol, annotations):
+        """ Return deprecated annotation and raise an error if its format is not compatible
+        with abi version
+        """
+
+        deprecated = annotations.get('Deprecated')
+        if not abi_has_deprecated_message(self.spec) and deprecated:
+            self.parser_error(p, symbol,
+                              "/Deprecated/ supports message argument only for ABI v13.9 and later, or v12.16 or later")
+
+        return deprecated
+            
+        
     def pop_file(self):
         """ Restore the current .sip file from the stack and make it current.
         An IndexError is raised if the stack is empty.
diff --git a/sipbuild/generator/parser/rules.py b/sipbuild/generator/parser/rules.py
index 7441fde..4ac4d61 100644
--- a/sipbuild/generator/parser/rules.py
+++ b/sipbuild/generator/parser/rules.py
@@ -1211,8 +1211,6 @@ def p_plugin(p):
     if pm.skipping:
         return
 
-    pm.deprecated(p, 1)
-
     pm.spec.plugins.append(p[2])
 
 
@@ -3215,10 +3213,15 @@ def p_annotation(p):
     """annotation : NAME
         | NAME '=' annotation_value"""
 
+    p[0] = {}
+
+    if p.parser.pm.skipping:
+        return
+
     value = None if len(p) == 2 else p[3]
     value = p.parser.pm.validate_annotation(p, 1, value)
 
-    p[0] = {p[1]: value}
+    p[0][p[1]] = value
 
 
 def p_annotation_value(p):
diff --git a/sipbuild/generator/resolver/resolver.py b/sipbuild/generator/resolver/resolver.py
index 46185de..ec6cc41 100644
--- a/sipbuild/generator/resolver/resolver.py
+++ b/sipbuild/generator/resolver/resolver.py
@@ -649,8 +649,8 @@ def _set_mro(spec, klass, error_log, seen=None):
     if klass.scope is not None:
         _set_mro(spec, klass.scope, error_log, seen=seen)
 
-        if klass.scope.deprecated:
-            klass.deprecated = True
+        if klass.scope.deprecated and not klass.deprecated:
+            klass.deprecated = klass.scope.deprecated
 
     if klass.iface_file.type is IfaceFileType.CLASS:
         # The first thing is itself.
@@ -687,8 +687,8 @@ def _set_mro(spec, klass, error_log, seen=None):
                 if klass.iface_file.module is spec.module:
                     superklass_mro.iface_file.needed = True
 
-                if superklass_mro.deprecated:
-                    klass.deprecated = True
+                if superklass_mro.deprecated and not klass.deprecated:
+                    klass.deprecated = superklass_mro.deprecated
 
                 # If the super-class is a QObject sub-class then this one is as
                 # well.
@@ -816,8 +816,9 @@ def _resolve_ctors(spec, klass, error_log):
                                     klass.iface_file.fq_cpp_name))
                     break
 
-        if klass.deprecated:
-            ctor.deprecated = True
+
+        if klass.deprecated and not ctor.deprecated :
+            ctor.deprecated = klass.deprecated   
 
 
 def _transform_casts(spec, klass, error_log):
@@ -872,9 +873,9 @@ def _add_default_copy_ctor(klass):
     signature = Signature(args=[arg], result=result)
     ctor = Constructor(AccessSpecifier.PUBLIC, py_signature=signature,
             cpp_signature=signature)
- 
-    if klass.deprecated:
-        ctor.deprecated = True
+
+    if klass.deprecated and not ctor.deprecated :
+        ctor.deprecated = klass.deprecated
 
     if not klass.is_abstract:
         klass.can_create = True
@@ -919,8 +920,9 @@ def _resolve_scope_overloads(spec, overloads, error_log, final_checks,
                     break
 
         if isinstance(scope, WrappedClass):
-            if scope.deprecated:
-                overload.deprecated = True
+
+            if scope.deprecated and not overload.deprecated :
+                overload.deprecated = scope.deprecated
 
             if overload.is_abstract:
                 scope.is_abstract = True
diff --git a/sipbuild/generator/specification.py b/sipbuild/generator/specification.py
index 3cfd119..512b458 100644
--- a/sipbuild/generator/specification.py
+++ b/sipbuild/generator/specification.py
@@ -5,7 +5,7 @@
 
 from dataclasses import dataclass, field
 from enum import auto, Enum
-from typing import Any, Dict, List, Optional, Union
+from typing import Any, Optional, Union
 
 from .scoped_name import ScopedName
 
@@ -575,14 +575,14 @@ class Argument:
     array: ArrayArgument = ArrayArgument.NONE
 
     # The optional default value.
-    default_value: Optional[List['Value']] = None
+    default_value: Optional[list['Value']] = None
 
     # The optional definition.  What this is depends on the type.
     definition: Any = None
 
     # The sequence of dereferences.  An element is True if the corresponding
     # dereference is const.
-    derefs: List[bool] = field(default_factory=list)
+    derefs: list[bool] = field(default_factory=list)
 
     # Set if /DisallowNone/ was specified.
     disallow_none: bool = False
@@ -689,8 +689,8 @@ class Constructor:
     # The C/C++ signature.  It will be none if /NoDerived/ was specified.
     cpp_signature: Optional['Signature'] = None
 
-    # Set if /Deprecated/ was specified.
-    deprecated: bool = False
+    # deprecated message if /Deprecated/ was specified.
+    deprecated: Optional[str] = None
 
     # The docstring.
     docstring: Optional['Docstring'] = None
@@ -762,7 +762,7 @@ class FunctionCall:
     result: Argument
 
     # The list of arguments.
-    args: List[List['Value']]
+    args: list[list['Value']]
 
 
 @dataclass
@@ -796,10 +796,10 @@ class IfaceFile:
     needed: bool = False
 
     # The %TypeHeaderCode.
-    type_header_code: List[CodeBlock] = field(default_factory=list)
+    type_header_code: list[CodeBlock] = field(default_factory=list)
 
     # The interface files used by this one (either directly or indirectly).
-    used: List['IfaceFile'] = field(default_factory=list)
+    used: list['IfaceFile'] = field(default_factory=list)
 
 
 @dataclass
@@ -845,7 +845,7 @@ class MappedType:
     instance_code: Optional[CodeBlock] = None
 
     # The member functions.
-    members: List['Member'] = field(default_factory=list)
+    members: list['Member'] = field(default_factory=list)
 
     # Set if the handwritten code requires user state information.
     needs_user_state: bool = False
@@ -863,7 +863,7 @@ class MappedType:
     no_release: bool = False
 
     # The overloaded member functions.
-    overloads: List['Overload'] = field(default_factory=list)
+    overloads: list['Overload'] = field(default_factory=list)
 
     # The Python name.  It will be None for mapped type templates.
     py_name: Optional[CachedName] = None
@@ -875,7 +875,7 @@ class MappedType:
     release_code: Optional[CodeBlock] = None
 
     # The %TypeCode.
-    type_code: List[CodeBlock] = field(default_factory=list)
+    type_code: list[CodeBlock] = field(default_factory=list)
 
     # The %TypeHintCode.
     type_hint_code: Optional[CodeBlock] = None
@@ -935,13 +935,13 @@ class Module:
     """ Encapsulate a module. """
 
     # The list of all (explicit and implied) imports. (resolver)
-    all_imports: List['Module'] = field(default_factory=list)
+    all_imports: list['Module'] = field(default_factory=list)
 
     # Set if wrapped ctors should support cooperative multi-inheritance.
     call_super_init: bool = False
 
     # The text specified by any %Copying directives.
-    copying: List[CodeBlock] = field(default_factory=list)
+    copying: list[CodeBlock] = field(default_factory=list)
 
     # The default docstring format.
     default_docstring_format: DocstringFormat = DocstringFormat.RAW
@@ -970,25 +970,25 @@ class Module:
     fq_py_name: Optional[CachedName] = None
 
     # The global functions.
-    global_functions: List[Member] = field(default_factory=list)
+    global_functions: list[Member] = field(default_factory=list)
 
     # Set if any class defined in the module has a delayed dtor.
     has_delayed_dtors: bool = False
 
     # The list of direct imports.
-    imports: List['Module'] = field(default_factory=list)
+    imports: list['Module'] = field(default_factory=list)
 
     # The code specified by any %InitialisationCode directives.
-    initialisation_code: List[CodeBlock] = field(default_factory=list)
+    initialisation_code: list[CodeBlock] = field(default_factory=list)
 
     # The software license.
     license: Optional[License] = None
 
     # The %ModuleCode.
-    module_code: List[CodeBlock] = field(default_factory=list)
+    module_code: list[CodeBlock] = field(default_factory=list)
 
     # The %ModuleHeaderCode.
-    module_header_code: List[CodeBlock] = field(default_factory=list)
+    module_header_code: list[CodeBlock] = field(default_factory=list)
 
     # The next key to auto-allocate.
     next_key: int = -1
@@ -997,7 +997,7 @@ class Module:
     nr_exceptions: int = 0
 
     # The generated types needed by this module. (resolver)
-    needed_types: List[Argument] = field(default_factory=list)
+    needed_types: list[Argument] = field(default_factory=list)
 
     # The number of typedefs defined in this module.
     nr_typedefs: int = 0
@@ -1006,16 +1006,16 @@ class Module:
     nr_virtual_error_handlers: int = 0
 
     # The overloaded global functions.
-    overloads: List['Overload'] = field(default_factory=list)
+    overloads: list['Overload'] = field(default_factory=list)
 
     # The code specified by any %PostInitialisationCode directives.
-    postinitialisation_code: List[CodeBlock] = field(default_factory=list)
+    postinitialisation_code: list[CodeBlock] = field(default_factory=list)
 
     # The proxy classes.
-    proxies: List['WrappedClass'] = field(default_factory=list)
+    proxies: list['WrappedClass'] = field(default_factory=list)
 
     # The code specified by any %PreInitialisationCode directives.
-    preinitialisation_code: List[CodeBlock] = field(default_factory=list)
+    preinitialisation_code: list[CodeBlock] = field(default_factory=list)
 
     # The name of the module. (resolver)
     py_name: Optional[str] = None
@@ -1024,16 +1024,16 @@ class Module:
     py_ssize_t_clean: bool = False
 
     # The list of qualifiers.
-    qualifiers: List['Qualifier'] = field(default_factory=list)
+    qualifiers: list['Qualifier'] = field(default_factory=list)
 
     # The %TypeHintCode.
-    type_hint_code: List[CodeBlock] = field(default_factory=list)
+    type_hint_code: list[CodeBlock] = field(default_factory=list)
 
     # The %UnitCode.
-    unit_code: List[CodeBlock] = field(default_factory=list)
+    unit_code: list[CodeBlock] = field(default_factory=list)
 
     # The %UnitPostIncludeCode.
-    unit_postinclude_code: List[CodeBlock] = field(default_factory=list)
+    unit_postinclude_code: list[CodeBlock] = field(default_factory=list)
 
     # Set if the actual argument names to wrapped callables should be used in
     # the generated bindings rather than automatically generated ones.
@@ -1043,7 +1043,7 @@ class Module:
     use_limited_api: bool = False
 
     # The interface files used by the module.
-    used: List[IfaceFile] = field(default_factory=list)
+    used: list[IfaceFile] = field(default_factory=list)
 
 
 @dataclass
@@ -1071,9 +1071,9 @@ class Overload:
     # Set if the overload is really protected.
     access_is_really_protected: bool = False
 
-    # Set if /Deprecated/ was specified.
-    deprecated: bool = False
-
+    # deprecated message if /Deprecated/ was specified.
+    deprecated: Optional[str] = None
+    
     # The docstring.
     docstring: Optional[Docstring] = None
 
@@ -1206,7 +1206,8 @@ class Qualifier:
     order: int = 0
 
     # The timeline number within the defining module if it is a TIME qualifier.
-    timeline: int = 0
+    # A negative value implies the SIP timeline.
+    timeline: int = -1
 
 
 @dataclass
@@ -1214,7 +1215,7 @@ class Signature:
     """ Encapsulate a signature (including the optional result). """
 
     # The list of arguments.
-    args: List[Argument] = field(default_factory=list)
+    args: list[Argument] = field(default_factory=list)
 
     # The type of the result.
     result: Optional[Argument] = None
@@ -1252,62 +1253,62 @@ class Specification:
     c_bindings: bool = False
 
     # The list of classes.
-    classes: List['WrappedClass'] = field(default_factory=list)
+    classes: list['WrappedClass'] = field(default_factory=list)
 
     # The list of enums.
-    enums: List['WrappedEnum'] = field(default_factory=list)
+    enums: list['WrappedEnum'] = field(default_factory=list)
 
     # The list of exceptions.
-    exceptions: List['WrappedException'] = field(default_factory=list)
+    exceptions: list['WrappedException'] = field(default_factory=list)
 
     # The %ExportedHeaderCode.
-    exported_header_code: List[CodeBlock] = field(default_factory=list)
+    exported_header_code: list[CodeBlock] = field(default_factory=list)
 
     # The %ExportedTypeHintCode.
-    exported_type_hint_code: List[CodeBlock] = field(default_factory=list)
+    exported_type_hint_code: list[CodeBlock] = field(default_factory=list)
 
     # The extracts.
-    extracts: List[Extract] = field(default_factory=list)
+    extracts: list[Extract] = field(default_factory=list)
 
     # The interface files.
-    iface_files: List[IfaceFile] = field(default_factory=list)
+    iface_files: list[IfaceFile] = field(default_factory=list)
 
     # Set if the specification is for a composite module.
     is_composite: bool = False
 
     # The mapped type templates.
-    mapped_type_templates: List[MappedTypeTemplate] = field(default_factory=list)
+    mapped_type_templates: list[MappedTypeTemplate] = field(default_factory=list)
 
     # The mapped types.
-    mapped_types: List[MappedType] = field(default_factory=list)
+    mapped_types: list[MappedType] = field(default_factory=list)
 
     # The module for which code is to be generated.
     module: Module = field(default_factory=Module)
 
     # The cache of names that may be required as strings in the generated code.
-    name_cache: Dict[int, List[CachedName]] = field(default_factory=dict)
+    name_cache: dict[int, list[CachedName]] = field(default_factory=dict)
 
     # The number of virtual handlers. (resolver)
     nr_virtual_handlers: int = 0
 
     # The list of plugins.  Note that these are PyQt-specific and will be
     # removed in SIP v7.
-    plugins: List[str] = field(default_factory=list)
+    plugins: list[str] = field(default_factory=list)
 
     # The QObject class.
     pyqt_qobject: Optional['WrappedClass'] = None
 
     # The list of typedefs.
-    typedefs: List['WrappedTypedef'] = field(default_factory=list)
+    typedefs: list['WrappedTypedef'] = field(default_factory=list)
 
     # The list of variables.
-    variables: List['WrappedVariable'] = field(default_factory=list)
+    variables: list['WrappedVariable'] = field(default_factory=list)
 
     # The list of virtual error handlers.
-    virtual_error_handlers: List['VirtualErrorHandler'] = field(default_factory=list)
+    virtual_error_handlers: list['VirtualErrorHandler'] = field(default_factory=list)
 
     # The list of virtual handlers. (resolver)
-    virtual_handlers: List['VirtualHandler'] = field(default_factory=list)
+    virtual_handlers: list['VirtualHandler'] = field(default_factory=list)
 
     def __hash__(self):
         """ Reimplemented so a Specification object can be used as a dict key.
@@ -1333,7 +1334,7 @@ class ThrowArguments:
 
     # The list of the argument names. If it is None then 'noexcept' was
     # specified, otherwise there will be at least one argument.
-    arguments: Optional[List['WrappedException']] = None
+    arguments: Optional[list['WrappedException']] = None
 
 
 @dataclass
@@ -1465,7 +1466,7 @@ class WrappedClass:
     cannot_copy: bool = False
 
     # The list of operator casts.
-    casts: List[Argument] = field(default_factory=list)
+    casts: list[Argument] = field(default_factory=list)
 
     # The specific type of class.  It will be None for namespaces.
     class_key: Optional[ClassKey] = None
@@ -1480,10 +1481,10 @@ class WrappedClass:
     convert_to_type_code: Optional[CodeBlock] = None
 
     # The constructors.
-    ctors: List[Constructor] = field(default_factory=list)
+    ctors: list[Constructor] = field(default_factory=list)
 
     # The dtor's %PreMethodCode and %MethodCode.
-    dealloc_code: List[CodeBlock] = field(default_factory=list)
+    dealloc_code: list[CodeBlock] = field(default_factory=list)
 
     # The constructor that has /Default/ specified.
     default_ctor: Optional[Constructor] = None
@@ -1491,8 +1492,8 @@ class WrappedClass:
     # Set if /DelayDtor/ was specified.
     delay_dtor: bool = False
 
-    # Set if /Deprecated/ was specified.
-    deprecated: bool = False
+    # deprecated message if /Deprecated/ was specified.
+    deprecated: Optional[str] = None
 
     # The docstring.
     docstring: Optional[Docstring] = None
@@ -1561,7 +1562,7 @@ class WrappedClass:
     len_cpp_name: Optional[str] = None
 
     # The methods.
-    members: List[Member] = field(default_factory=list)
+    members: list[Member] = field(default_factory=list)
 
     # The value of /Metatype/ if specified.
     metatype: Optional[CachedName] = None
@@ -1571,7 +1572,7 @@ class WrappedClass:
 
     # The list of all classes in the class hierarchy starting with itself.
     # (resolver)
-    mro: List['WrappedClass'] = field(default_factory=list)
+    mro: list['WrappedClass'] = field(default_factory=list)
 
     # Set if the class needs an array helper. (resolver)
     needs_array_helper: bool = False
@@ -1593,19 +1594,19 @@ class WrappedClass:
     no_type_name: bool = False
 
     # The overloaded methods.
-    overloads: List[Overload] = field(default_factory=list)
+    overloads: list[Overload] = field(default_factory=list)
 
     # The %PickleCode.
     pickle_code: Optional[CodeBlock] = None
 
     # The properties.
-    properties: List[Property] = field(default_factory=list)
+    properties: list[Property] = field(default_factory=list)
 
     # The /PyQtFlags/.
     pyqt_flags: int = 0
 
     # The /PyQtFlagsEnums/.
-    pyqt_flags_enums: Optional[List[str]] = None
+    pyqt_flags_enums: Optional[list[str]] = None
 
     # The /PyQtInterface/.
     pyqt_interface: Optional[str] = None
@@ -1620,7 +1621,7 @@ class WrappedClass:
     subclass_base: Optional['WrappedClass'] = None
 
     # The super-classes.
-    superclasses: List['WrappedClass'] = field(default_factory=list)
+    superclasses: list['WrappedClass'] = field(default_factory=list)
 
     # The value of /Supertype/ if specified.
     supertype: Optional[CachedName] = None
@@ -1629,7 +1630,7 @@ class WrappedClass:
     template: Optional[Template] = None
 
     # The %TypeCode.
-    type_code: List[CodeBlock] = field(default_factory=list)
+    type_code: list[CodeBlock] = field(default_factory=list)
 
     # The %TypeHintCode.
     type_hint_code: Optional[CodeBlock] = None
@@ -1641,10 +1642,10 @@ class WrappedClass:
     virtual_error_handler: Optional[str] = None
 
     # The virtual overloaded methods. (resolver)
-    virtual_overloads: List[VirtualOverload] = field(default_factory=list)
+    virtual_overloads: list[VirtualOverload] = field(default_factory=list)
 
     # The visible member functions. (resolver)
-    visible_members: List[VisibleMember] = field(default_factory=list)
+    visible_members: list[VisibleMember] = field(default_factory=list)
 
     def __hash__(self):
         """ Reimplemented so an Argument object can be used as a dict key. """
@@ -1675,7 +1676,7 @@ class WrappedEnum:
     is_scoped: bool = False
 
     # The members.
-    members: List['WrappedEnumMember'] = field(default_factory=list)
+    members: list['WrappedEnumMember'] = field(default_factory=list)
 
     # Set if this enum is needed by the module for which code is to be
     # generated. (resolver)
@@ -1688,7 +1689,7 @@ class WrappedEnum:
     no_type_hint: bool = False
 
     # The overloaded slot member functions. (resolver)
-    overloads: List['Overload'] = field(default_factory=list)
+    overloads: list['Overload'] = field(default_factory=list)
 
     # The Python name.
     py_name: Optional[CachedName] = None
@@ -1698,7 +1699,7 @@ class WrappedEnum:
 
     # The slot member functions.  These can only be created by global operators
     # being moved. (resolver)
-    slots: List[Member] = field(default_factory=list)
+    slots: list[Member] = field(default_factory=list)
 
     # The generated type number. (resolver)
     type_nr: int = -1
diff --git a/sipbuild/generator/utils.py b/sipbuild/generator/utils.py
index 17b1759..04d1cdc 100644
--- a/sipbuild/generator/utils.py
+++ b/sipbuild/generator/utils.py
@@ -485,3 +485,15 @@ def search_typedefs(spec, cpp_name, type):
     # Remember the original typedef.
     if type.original_typedef is None:
         type.original_typedef = typedef
+
+        
+def abi_version_check(spec, min_12, min_13):
+    """ Return True if the ABI version meets minimum version requirements. """
+
+    return spec.abi_version >= min_13 or (min_12 <= spec.abi_version < (13, 0))
+
+
+def abi_has_deprecated_message(spec):
+    """ Return True if the ABI implements sipDeprecated() with a message. """
+
+    return abi_version_check(spec, (12, 16), (13, 9))
diff --git a/sipbuild/module/abi_version.py b/sipbuild/module/abi_version.py
index a97195a..e7f5593 100644
--- a/sipbuild/module/abi_version.py
+++ b/sipbuild/module/abi_version.py
@@ -77,7 +77,9 @@ def resolve_abi_version(abi_version, module=True):
                         f"'{abi_version}' is not a valid ABI version")
     else:
         abi_major_version = sorted(os.listdir(_module_source_dir), key=int)[-1]
-        minimum_minor_version = 0
+        # v13.0 is deprecated so explicitly exclude it to avoid a later
+        # deprecation warning.
+        minimum_minor_version = 1 if abi_major_version == '13' else 0
 
     # Get the minor version of what we actually have.
     module_version = get_sip_module_version(abi_major_version)
diff --git a/sipbuild/module/source/12/apiversions.c b/sipbuild/module/source/12/apiversions.c
index f176463..3b11ed4 100644
--- a/sipbuild/module/source/12/apiversions.c
+++ b/sipbuild/module/source/12/apiversions.c
@@ -178,9 +178,6 @@ PyObject *sipGetAPI(PyObject *self, PyObject *args)
 
     (void)self;
 
-    if (sip_api_deprecated(NULL, "getapi") < 0)
-        return NULL;
-
     if (!PyArg_ParseTuple(args, "s:getapi", &api))
         return NULL;
 
@@ -205,9 +202,6 @@ PyObject *sipSetAPI(PyObject *self, PyObject *args)
 
     (void)self;
 
-    if (sip_api_deprecated(NULL, "setapi") < 0)
-        return NULL;
-
     if (!PyArg_ParseTuple(args, "si:setapi", &api, &version_nr))
         return NULL;
 
diff --git a/sipbuild/module/source/12/sip.h.in b/sipbuild/module/source/12/sip.h.in
index 9cc306f..4ff7695 100644
--- a/sipbuild/module/source/12/sip.h.in
+++ b/sipbuild/module/source/12/sip.h.in
@@ -14,8 +14,8 @@
 #include <Python.h>
 
 /* Sanity check on the Python version. */
-#if PY_VERSION_HEX < 0x03080000
-#error "This version of @_SIP_MODULE_FQ_NAME@ requires Python v3.8 or later"
+#if PY_VERSION_HEX < 0x03090000
+#error "This version of @_SIP_MODULE_FQ_NAME@ requires Python v3.9 or later"
 #endif
 
 
@@ -35,13 +35,18 @@ extern "C" {
 
 /* The version of the ABI. */
 #define SIP_ABI_MAJOR_VERSION       12
-#define SIP_ABI_MINOR_VERSION       15
+#define SIP_ABI_MINOR_VERSION       16
 #define SIP_MODULE_PATCH_VERSION    0
 
 
 /*
  * The change history of the ABI.
  *
+ * v12.16
+ *  - Python v3.9 or later is required.
+ *  - Added a new implementation of sipDeprecated() that takes an optional
+ *    supplementary message.
+ *
  * v12.15
  *  - Added the 'I' conversion character to the argument and result parsers.
  *
@@ -1676,6 +1681,12 @@ typedef struct _sipAPIDef {
      * The following are part of the public API.
      */
     PyObject *(*api_py_type_dict_ref)(PyTypeObject *);
+
+    /*
+     * The following are part of the private API.
+     */
+    int (*api_deprecated_12_16)(const char *classname, const char *method, const char *message);
+
 } sipAPIDef;
 
 const sipAPIDef *sip_init_library(PyObject *mod_dict);
diff --git a/sipbuild/module/source/12/sipint.h b/sipbuild/module/source/12/sipint.h
index a18485f..933a914 100644
--- a/sipbuild/module/source/12/sipint.h
+++ b/sipbuild/module/source/12/sipint.h
@@ -151,6 +151,7 @@ int sip_api_convert_from_slice_object(PyObject *slice, Py_ssize_t length,
         Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step,
         Py_ssize_t *slicelength);
 int sip_api_deprecated(const char *classname, const char *method);
+int sip_api_deprecated_12_16(const char *classname, const char *method, const char *message);
 
 
 /*
diff --git a/sipbuild/module/source/12/siplib.c b/sipbuild/module/source/12/siplib.c
index c31ec32..409bba8 100644
--- a/sipbuild/module/source/12/siplib.c
+++ b/sipbuild/module/source/12/siplib.c
@@ -223,9 +223,7 @@ static sipWrapperType sipWrapper_Type = {
         0,                  /* ht_slots */
         0,                  /* ht_qualname */
         0,                  /* ht_cached_keys */
-#if PY_VERSION_HEX >= 0x03090000
         0,                  /* ht_module */
-#endif
 #if !defined(STACKLESS)
     },
 #endif
@@ -606,6 +604,10 @@ static const sipAPIDef sip_api = {
      * The following are part of the public API.
      */
     sip_api_py_type_dict_ref,
+    /*
+     * The following are part of the private API.
+     */
+    sip_api_deprecated_12_16,
 };
 
 
@@ -2895,7 +2897,7 @@ static int parseResult(PyObject *method, PyObject *res,
             case 'a':
                 {
                     char *p = va_arg(va, char *);
-                    int enc;
+                    int enc = -1;
 
                     switch (*fmt++)
                     {
@@ -2910,9 +2912,6 @@ static int parseResult(PyObject *method, PyObject *res,
                     case '8':
                         enc = parseString_AsUTF8Char(arg, p);
                         break;
-
-                    default:
-                        enc = -1;
                     }
 
                     if (enc < 0)
@@ -3183,7 +3182,7 @@ static int parseResult(PyObject *method, PyObject *res,
                 {
                     int key = va_arg(va, int);
                     const char **p = va_arg(va, const char **);
-                    PyObject *keep;
+                    PyObject *keep = NULL;
 
                     switch (*fmt++)
                     {
@@ -3198,9 +3197,6 @@ static int parseResult(PyObject *method, PyObject *res,
                     case '8':
                         keep = parseString_AsUTF8String(arg, p);
                         break;
-
-                    default:
-                        keep = NULL;
                     }
 
                     if (keep == NULL)
@@ -5787,7 +5783,7 @@ static int parsePass2(sipSimpleWrapper *self, int selfarg, PyObject *sipArgs,
 
                 if (arg != NULL)
                 {
-                    int enc;
+                    int enc = -1;
 
                     switch (sub_fmt)
                     {
@@ -7785,6 +7781,16 @@ static void sip_api_abstract_method(const char *classname, const char *method)
  * Report a deprecated class or method.
  */
 int sip_api_deprecated(const char *classname, const char *method)
+{
+  return sip_api_deprecated_12_16(classname, method, NULL);
+}
+
+
+/*
+ * Report a deprecated class or method with an optional message.
+ */
+int sip_api_deprecated_12_16(const char *classname, const char *method,
+        const char *message)
 {
     char buf[100];
 
@@ -7797,6 +7803,9 @@ int sip_api_deprecated(const char *classname, const char *method)
         PyOS_snprintf(buf, sizeof (buf), "%s.%s() is deprecated", classname,
                 method);
 
+    if (message != NULL)
+        PyOS_snprintf(&buf[strlen(buf)], sizeof (buf), ": %s", message);
+
     return PyErr_WarnEx(PyExc_DeprecationWarning, buf, 1);
 }
 
@@ -10994,9 +11003,7 @@ sipWrapperType sipSimpleWrapper_Type = {
         0,                  /* ht_slots */
         0,                  /* ht_qualname */
         0,                  /* ht_cached_keys */
-#if PY_VERSION_HEX >= 0x03090000
         0,                  /* ht_module */
-#endif
 #if !defined(STACKLESS)
     },
 #endif
@@ -12827,28 +12834,22 @@ static int sip_api_is_user_type(const sipWrapperType *wt)
 
 
 /*
- * Return a frame from the execution stack.  Note that we use 'struct _frame'
- * rather than PyFrameObject because the latter wasn't exposed to the limited
- * API until Python v3.9.
+ * Return a frame from the execution stack.
  */
-static struct _frame *sip_api_get_frame(int depth)
+static PyFrameObject *sip_api_get_frame(int depth)
 {
 #if defined(PYPY_VERSION)
     /* PyPy only supports a depth of 0. */
     return NULL;
 #else
-    struct _frame *frame = PyEval_GetFrame();
+    PyFrameObject *frame = PyEval_GetFrame();
 
     while (frame != NULL && depth > 0)
     {
-#if PY_VERSION_HEX < 0x03090000
-        frame = frame->f_back;
-#else
         frame = PyFrame_GetBack(frame);
 
         /* Historically we return a borrowed reference. */
         Py_XDECREF(frame);
-#endif
         --depth;
     }
 
@@ -12971,7 +12972,6 @@ static void *sip_api_unicode_data(PyObject *obj, int *char_size,
  */
 static int sip_api_get_buffer_info(PyObject *obj, sipBufferInfoDef *bi)
 {
-    int rc;
     Py_buffer *buffer;
 
     if (!PyObject_CheckBuffer(obj))
@@ -12985,26 +12985,15 @@ static int sip_api_get_buffer_info(PyObject *obj, sipBufferInfoDef *bi)
 
     buffer = (Py_buffer *)bi->bi_internal;
 
-    if (PyObject_GetBuffer(obj, buffer, PyBUF_FORMAT) < 0)
+    if (PyObject_GetBuffer(obj, buffer, PyBUF_SIMPLE) < 0)
         return -1;
 
-    if (buffer->ndim == 1)
-    {
-        bi->bi_buf = buffer->buf;
-        bi->bi_obj = buffer->obj;
-        bi->bi_len = buffer->len;
-        bi->bi_format = buffer->format;
-
-        rc = 1;
-    }
-    else
-    {
-        PyErr_SetString(PyExc_TypeError, "a 1-dimensional buffer is required");
-        PyBuffer_Release(buffer);
-        rc = -1;
-    }
+    bi->bi_buf = buffer->buf;
+    bi->bi_obj = buffer->obj;
+    bi->bi_len = buffer->len;
+    bi->bi_format = buffer->format;
 
-    return rc;
+    return 1;
 }
 
 
diff --git a/sipbuild/module/source/13/sip.h.in b/sipbuild/module/source/13/sip.h.in
index 109453c..4cfbb24 100644
--- a/sipbuild/module/source/13/sip.h.in
+++ b/sipbuild/module/source/13/sip.h.in
@@ -14,8 +14,8 @@
 #include <Python.h>
 
 /* Sanity check on the Python version. */
-#if PY_VERSION_HEX < 0x03080000
-#error "This version of @_SIP_MODULE_FQ_NAME@ requires Python v3.8 or later"
+#if PY_VERSION_HEX < 0x03090000
+#error "This version of @_SIP_MODULE_FQ_NAME@ requires Python v3.9 or later"
 #endif
 
 
@@ -35,13 +35,18 @@ extern "C" {
 
 /* The version of the ABI. */
 #define SIP_ABI_MAJOR_VERSION       13
-#define SIP_ABI_MINOR_VERSION       8
+#define SIP_ABI_MINOR_VERSION       9
 #define SIP_MODULE_PATCH_VERSION    0
 
 
 /*
  * The change history of the ABI.
  *
+ * v13.9
+ *  - Python v3.9 or later is required.
+ *  - Added a new implementation of sipDeprecated() that takes an optional
+ *    supplementary message.
+ *
  * v13.8
  *  - Added the 'I' conversion character to the argument and result parsers.
  *
@@ -1425,7 +1430,7 @@ typedef struct _sipAPIDef {
     PyObject *(*api_is_py_method_12_8)(sip_gilstate_t *gil, char *pymc,
             sipSimpleWrapper **sipSelfp, const char *cname, const char *mname);
     sipExceptionHandler (*api_next_exception_handler)(void **statep);
-    void (*unused_private_2)(void);
+    int (*api_deprecated_13_9)(const char *classname, const char *method, const char *message);
     void (*unused_private_3)(void);
     void (*unused_private_4)(void);
     void (*unused_private_5)(void);
diff --git a/sipbuild/module/source/13/sip_core.c b/sipbuild/module/source/13/sip_core.c
index af629b7..f29485c 100644
--- a/sipbuild/module/source/13/sip_core.c
+++ b/sipbuild/module/source/13/sip_core.c
@@ -227,9 +227,7 @@ static sipWrapperType sipWrapper_Type = {
         0,                  /* ht_slots */
         0,                  /* ht_qualname */
         0,                  /* ht_cached_keys */
-#if PY_VERSION_HEX >= 0x03090000
         0,                  /* ht_module */
-#endif
 #if !defined(STACKLESS)
     },
 #endif
@@ -547,7 +545,7 @@ static const sipAPIDef sip_api = {
     sip_api_instance_destroyed_ex,
     sip_api_is_py_method_12_8,
     sip_api_next_exception_handler,
-    NULL,
+    sip_api_deprecated_13_9,
     NULL,
     NULL,
     NULL,
@@ -2591,7 +2589,7 @@ static int parseResult(PyObject *method, PyObject *res,
             case 'a':
                 {
                     char *p = va_arg(va, char *);
-                    int enc;
+                    int enc = -1;
 
                     switch (*fmt++)
                     {
@@ -2606,9 +2604,6 @@ static int parseResult(PyObject *method, PyObject *res,
                     case '8':
                         enc = parseString_AsUTF8Char(arg, p);
                         break;
-
-                    default:
-                        enc = -1;
                     }
 
                     if (enc < 0)
@@ -2845,7 +2840,7 @@ static int parseResult(PyObject *method, PyObject *res,
                 {
                     int key = va_arg(va, int);
                     const char **p = va_arg(va, const char **);
-                    PyObject *keep;
+                    PyObject *keep = NULL;
 
                     switch (*fmt++)
                     {
@@ -2860,9 +2855,6 @@ static int parseResult(PyObject *method, PyObject *res,
                     case '8':
                         keep = parseString_AsUTF8String(arg, p);
                         break;
-
-                    default:
-                        keep = NULL;
                     }
 
                     if (keep == NULL)
@@ -5068,7 +5060,7 @@ static int parsePass2(PyObject *self, int selfarg, PyObject *sipArgs,
 
                 if (arg != NULL)
                 {
-                    int enc;
+                    int enc = -1;
 
                     switch (sub_fmt)
                     {
@@ -6674,6 +6666,16 @@ static void sip_api_abstract_method(const char *classname, const char *method)
  * Report a deprecated class or method.
  */
 int sip_api_deprecated(const char *classname, const char *method)
+{
+    return sip_api_deprecated_13_9(classname, method, NULL);
+}
+
+
+/*
+ * Report a deprecated class or method with an optional message.
+ */
+int sip_api_deprecated_13_9(const char *classname, const char *method,
+        const char *message)
 {
     char buf[100];
 
@@ -6684,7 +6686,10 @@ int sip_api_deprecated(const char *classname, const char *method)
                 classname);
     else
         PyOS_snprintf(buf, sizeof (buf), "%s.%s() is deprecated", classname,
-                method);
+                method );
+
+    if (message != NULL)
+        PyOS_snprintf(&buf[strlen(buf)], sizeof (buf), ": %s", message);
 
     return PyErr_WarnEx(PyExc_DeprecationWarning, buf, 1);
 }
@@ -9720,9 +9725,7 @@ sipWrapperType sipSimpleWrapper_Type = {
         0,                  /* ht_slots */
         0,                  /* ht_qualname */
         0,                  /* ht_cached_keys */
-#if PY_VERSION_HEX >= 0x03090000
         0,                  /* ht_module */
-#endif
 #if !defined(STACKLESS)
     },
 #endif
@@ -11466,7 +11469,6 @@ static void *sip_api_unicode_data(PyObject *obj, int *char_size,
  */
 static int sip_api_get_buffer_info(PyObject *obj, sipBufferInfoDef *bi)
 {
-    int rc;
     Py_buffer *buffer;
 
     if (!PyObject_CheckBuffer(obj))
@@ -11480,27 +11482,16 @@ static int sip_api_get_buffer_info(PyObject *obj, sipBufferInfoDef *bi)
 
     buffer = (Py_buffer *)bi->bi_internal;
 
-    if (PyObject_GetBuffer(obj, buffer, PyBUF_FORMAT) < 0)
+    if (PyObject_GetBuffer(obj, buffer, PyBUF_SIMPLE) < 0)
         return -1;
 
-    if (buffer->ndim == 1)
-    {
-        bi->bi_buf = buffer->buf;
-        bi->bi_obj = buffer->obj;
-        bi->bi_len = buffer->len;
-        bi->bi_readonly = buffer->readonly;
-        bi->bi_format = buffer->format;
-
-        rc = 1;
-    }
-    else
-    {
-        PyErr_SetString(PyExc_TypeError, "a 1-dimensional buffer is required");
-        PyBuffer_Release(buffer);
-        rc = -1;
-    }
+    bi->bi_buf = buffer->buf;
+    bi->bi_obj = buffer->obj;
+    bi->bi_len = buffer->len;
+    bi->bi_readonly = buffer->readonly;
+    bi->bi_format = buffer->format;
 
-    return rc;
+    return 1;
 }
 
 
diff --git a/sipbuild/module/source/13/sip_core.h b/sipbuild/module/source/13/sip_core.h
index 8ad7a4e..cd429f4 100644
--- a/sipbuild/module/source/13/sip_core.h
+++ b/sipbuild/module/source/13/sip_core.h
@@ -120,7 +120,8 @@ void *sip_api_force_convert_to_type_us(PyObject *pyObj, const sipTypeDef *td,
 int sip_api_convert_from_slice_object(PyObject *slice, Py_ssize_t length,
         Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step,
         Py_ssize_t *slicelength);
-int sip_api_deprecated(const char *classname, const char *method);
+  int sip_api_deprecated(const char *classname, const char *method);
+  int sip_api_deprecated_13_9(const char *classname, const char *method, const char* message);
 const sipTypeDef *sip_api_type_scope(const sipTypeDef *td);
 
 
diff --git a/sipbuild/py_versions.py b/sipbuild/py_versions.py
index 863f801..93b64cd 100644
--- a/sipbuild/py_versions.py
+++ b/sipbuild/py_versions.py
@@ -4,4 +4,4 @@
 
 
 # The oldest supported minor version of Python v3.
-OLDEST_SUPPORTED_MINOR = 8
+OLDEST_SUPPORTED_MINOR = 9



More information about the Neon-commits mailing list