[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