Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-sip6 for openSUSE:Factory checked in at 2025-10-18 14:36:03 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-sip6 (Old) and /work/SRC/openSUSE:Factory/.python-sip6.new.18484 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-sip6" Sat Oct 18 14:36:03 2025 rev:24 rq:1311795 version:6.13.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-sip6/python-sip6.changes 2025-08-27 21:34:27.062110132 +0200 +++ /work/SRC/openSUSE:Factory/.python-sip6.new.18484/python-sip6.changes 2025-10-18 14:36:57.332262284 +0200 @@ -1,0 +2,38 @@ +Thu Oct 16 19:15:45 UTC 2025 - Ben Greiner <[email protected]> + +- Add sip-issue95.patch + gh#Python-SIP/sip#95 +- Declare just BSD-2-Clause in specfile (see v6.10) + +------------------------------------------------------------------- +Sat Oct 11 17:15:59 UTC 2025 - Ben Greiner <[email protected]> + +- Update to 6.13.1 + * Fixed a regression in v6.13.0 in the handling of mapped types + for C++ templates with `typedef`ed arguments. + +------------------------------------------------------------------- +Thu Oct 9 14:11:43 UTC 2025 - Ben Greiner <[email protected]> + +- Revert to 6.12.0: Regression, failing to compile PyQt6-3D + https://www.riverbankcomputing.com/pipermail/pyqt/2025-October/046338.html + +------------------------------------------------------------------- +Thu Oct 9 04:24:13 UTC 2025 - Steve Kowalik <[email protected]> + +- Update to 6.13.0: + * `/ExportDerivedLocally/` class annotation + * `%TypeDerivedCode` directive + * Support for SBOMs + * Support for bool-based enums + * Non-public super-classes not supported + * Fixed the code generated for operator casts. + * Fixed the handling of mapped types for C++ templates with `typedef`ed + arguments. + * Fixed the test for the `/Movable/` annotation so that it works with + Python v3.14. + * A mis-named enum member was corrected. + * Specifying `%Docstring` as a sub-directive of the `%Module` directive + generated invalid code. + +------------------------------------------------------------------- Old: ---- sip-6.12.0-gh.tar.gz New: ---- sip-6.13.1-gh.tar.gz sip-issue95.patch ----------(New B)---------- New: - Add sip-issue95.patch gh#Python-SIP/sip#95 ----------(New E)---------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-sip6.spec ++++++ --- /var/tmp/diff_new_pack.lEURfO/_old 2025-10-18 14:36:57.888285544 +0200 +++ /var/tmp/diff_new_pack.lEURfO/_new 2025-10-18 14:36:57.888285544 +0200 @@ -23,13 +23,15 @@ %bcond_with libalternatives %endif Name: python-sip6 -Version: 6.12.0 +Version: 6.13.1 Release: 0 Summary: A Python bindings generator for C/C++ libraries -License: BSD-2-Clause AND BSD-3-Clause +License: BSD-2-Clause Group: Development/Libraries/Python URL: https://github.com/Python-SIP/sip Source0: https://github.com/Python-SIP/sip/archive/refs/tags/%{version}.tar.gz#/sip-%{version}-gh.tar.gz +# PATCH-FIX-UPSTREAM sip-issue95.patch gh#Python-SIP/sip#95 +Patch0: https://github.com/philthompson10/sip/commit/b6a7ec2453899b878ffe0b2b5b1f642a0992aa6d.patch#/sip-issue95.patch BuildRequires: %{python_module base >= 3.9} BuildRequires: %{python_module packaging >= 24.2} BuildRequires: %{python_module pip} @@ -129,7 +131,7 @@ %python_uninstall_alternative sip-build %files %{python_files devel} -%license LICENSE* +%license LICENSE %python_alternative %{_bindir}/sip-build %python_alternative %{_bindir}/sip-distinfo %python_alternative %{_bindir}/sip-install ++++++ sip-6.12.0-gh.tar.gz -> sip-6.13.1-gh.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/.git_archival.txt new/sip-6.13.1/.git_archival.txt --- old/sip-6.12.0/.git_archival.txt 2025-06-03 15:59:00.000000000 +0200 +++ new/sip-6.13.1/.git_archival.txt 2025-10-10 17:26:04.000000000 +0200 @@ -1,3 +1,3 @@ -node: 4f0ca7fd43b941311e070051f637d931e17fa5d7 -node-date: 2025-06-03T14:59:00+01:00 -describe-name: 6.12.0 +node: bc5fb1968bf3b6177fda61308f0a4ed2e58472c2 +node-date: 2025-10-10T16:26:04+01:00 +describe-name: 6.13.1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/docs/annotations.rst new/sip-6.13.1/docs/annotations.rst --- old/sip-6.12.0/docs/annotations.rst 2025-06-03 15:59:00.000000000 +0200 +++ new/sip-6.13.1/docs/annotations.rst 2025-10-10 17:26:04.000000000 +0200 @@ -425,12 +425,22 @@ This string annotation is used to specify the filename extension to be used for the file containing the generated code for this class. + .. class-annotation:: ExportDerived In many cases SIP generates a derived class for each class being wrapped (see :ref:`ref-derived-classes`). Normally this is used internally. This boolean annotation specifies that the declaration of the class is exported - and able to be used by handwritten code. + and able to be used by handwritten code of any module. + + +.. class-annotation:: ExportDerivedLocally + + .. versionadded:: 6.13 + + This boolean annotation is similar to the :canno:`ExportDerived` class + annotation except that the declaration of the derived class is only + exported to handwritten code in the same module that the class is defined. .. class-annotation:: External diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/docs/command_line_tools.rst new/sip-6.13.1/docs/command_line_tools.rst --- old/sip-6.12.0/docs/command_line_tools.rst 2025-06-03 15:59:00.000000000 +0200 +++ new/sip-6.13.1/docs/command_line_tools.rst 2025-10-10 17:26:04.000000000 +0200 @@ -225,6 +225,14 @@ used to specify a particular version of a package project's :mod:`sip` module. This option may be specified multiple times. +.. option:: --sbom FILE + + .. versionadded:: 6.13 + + ``FILE`` is copied to the :file:`sboms` subdirectory of the + :file:`.dist-info` directory as defined in PEP 770. ``FILE`` may be a + glob-style pattern. This option may be specified multiple times. + .. option:: --wheel-tag TAG ``TAG`` is written as the ``Tag`` in the :file:`WHEEL` file in the diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/docs/conf.py new/sip-6.13.1/docs/conf.py --- old/sip-6.12.0/docs/conf.py 2025-06-03 15:59:00.000000000 +0200 +++ new/sip-6.13.1/docs/conf.py 2025-10-10 17:26:04.000000000 +0200 @@ -13,7 +13,7 @@ copyright = '{0} Phil Thompson <[email protected]>'.format( date.today().year) author = 'Phil Thompson' -version = 'v6.12.0' +version = 'v6.13.1' # -- General configuration --------------------------------------------------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/docs/directives.rst new/sip-6.13.1/docs/directives.rst --- old/sip-6.12.0/docs/directives.rst 2025-06-03 15:59:00.000000000 +0200 +++ new/sip-6.13.1/docs/directives.rst 2025-10-10 17:26:04.000000000 +0200 @@ -2235,6 +2235,49 @@ option is used. +.. directive:: %TypeDerivedCode + +:directive:`%TypeDerivedCode` +----------------------------- + +.. parsed-literal:: + %TypeDerivedCode + *code* + %End + +.. versionadded:: 6.13 + +In many cases SIP generates a derived class for each class being wrapped (see +:ref:`ref-derived-classes`). This directive is used to specify handwritten +code, typically the declaration of additional class members, to be included in +the class's declaration. The code is placed at the start of the declaration in +a public section. + +:directive:`%TypeCode` may be used to define the corresponding implementation +of the new member if necessary. + +For example:: + + class Klass + { + %TypeDerivedCode + // Print the instance on stderr for debugging purposes. + void dump() const; + %End + + %TypeCode + // The implementation. + void Klass::dump() const + { + fprintf(stderr,"Klass %s at %p\n", name(), this); + } + %End + + // The rest of the class specification. + + }; + + .. directive:: %TypeHeaderCode :directive:`%TypeHeaderCode` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/docs/pyproject_toml.rst new/sip-6.13.1/docs/pyproject_toml.rst --- old/sip-6.12.0/docs/pyproject_toml.rst 2025-06-03 15:59:00.000000000 +0200 +++ new/sip-6.13.1/docs/pyproject_toml.rst 2025-10-10 17:26:04.000000000 +0200 @@ -225,6 +225,11 @@ target directory. By default the directory containing the Python interpreter is used. There is also a corresponding command line option. +**sbom-files** + The value is a list of files that are copied to the :file:`sboms` + subdirectory of the :file:`.dist-info` directory as defined in PEP 770. A + file may be a glob-style pattern. + **sdist-excludes** The value is a list of files and directories, expressed as *glob* patterns and relative to the directory containing the :file:`pyproject.toml` file, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/docs/releases.md new/sip-6.13.1/docs/releases.md --- old/sip-6.12.0/docs/releases.md 2025-06-03 15:59:00.000000000 +0200 +++ new/sip-6.13.1/docs/releases.md 2025-10-10 17:26:04.000000000 +0200 @@ -1,5 +1,67 @@ # Release Notes +## v6.13.1 + +### Bug fixes + +Fixed a regression in v6.13.0 in the handling of mapped types for C++ +templates with `typedef`ed arguments. + + +## v6.13.0 + +### `/ExportDerivedLocally/` class annotation + +The new `/ExportDerivedLocally/` class annotation is similar to the +`/ExportDerived/` class annotation except that it only makes the derived +class declaration available to handwritten code in the module in which the +class was defined. + +### `%TypeDerivedCode` directive + +The new `%TypeDerivedCode` directive allows handwritten code to be specified +that is included at the start of the declaration of a derived class. + +### Support for SBOMs + +The `tools.sip.project.sbom-files` key in `pyproject.toml` is used to +specify a list of files (and glob-style patterns) that will be copied to +the `.dist-info/sboms` directory as described in PEP 770. + +Resolves [#83](https://github.com/Python-SIP/sip/issues/83) + +### Improved error reporting + +When a build fails the last 100 lines of the build output will be +displayed. As with previous versions the `--verbose` option must be +specified to see the full build output. + +Resolves [#84](https://github.com/Python-SIP/sip/issues/84) + +### Support for bool-based enums + +Enums can now have `bool` as a base type. + +Resolves [#88](https://github.com/Python-SIP/sip/issues/88) + +### Non-public super-classes not supported + +An attempt to use non-public super-classes will now result in a deprecation +message rather than being silently ignored. The super-class should simply +be removed. + +### Bug fixes + +- Fixed the code generated for operator casts. +- Fixed the handling of mapped types for C++ templates with `typedef`ed + arguments. +- Fixed the test for the `/Movable/` annotation so that it works with + Python v3.14. Resolves [#82](https://github.com/Python-SIP/sip/issues/82) +- A mis-named enum member was corrected. Resolves [#86](https://github.com/Python-SIP/sip/issues/86) +- Specifying `%Docstring` as a sub-directive of the `%Module` directive + generated invalid code. Resolves [#81](https://github.com/Python-SIP/sip/issues/81) + + ## v6.12.0 ### Support for C++11 enum base types diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/docs/specification_files.rst new/sip-6.13.1/docs/specification_files.rst --- old/sip-6.12.0/docs/specification_files.rst 2025-06-03 15:59:00.000000000 +0200 +++ new/sip-6.13.1/docs/specification_files.rst 2025-10-10 17:26:04.000000000 +0200 @@ -88,6 +88,7 @@ :directive:`%InstanceCode` | :directive:`%PickleCode` | :directive:`%TypeCode` | + :directive:`%TypeDerivedCode` | :directive:`%TypeHeaderCode` | :directive:`%TypeHintCode` | *constructor* | diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/sipbuild/configurable.py new/sip-6.13.1/sipbuild/configurable.py --- old/sip-6.12.0/sipbuild/configurable.py 2025-06-03 15:59:00.000000000 +0200 +++ new/sip-6.13.1/sipbuild/configurable.py 2025-10-10 17:26:04.000000000 +0200 @@ -219,8 +219,8 @@ attribute of a Configurable object. The value of the attribute can be set either by __init__(), the pyproject.toml file and by the user using a command line argument (in that order). Once the value is set it cannot be - changed subsequently. For example, if an attribute is set - in pyproject.toml then the user will not then be able to modify it from the + changed subsequently. For example, if an attribute is set in + pyproject.toml then the user will not then be able to modify it from the command line. The value can only be changed from the command line if the Option object has help text specified. """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/sipbuild/distinfo/distinfo.py new/sip-6.13.1/sipbuild/distinfo/distinfo.py --- old/sip-6.12.0/sipbuild/distinfo/distinfo.py 2025-06-03 15:59:00.000000000 +0200 +++ new/sip-6.13.1/sipbuild/distinfo/distinfo.py 2025-10-10 17:26:04.000000000 +0200 @@ -19,9 +19,9 @@ WHEEL_VERSION = '1.0' -def distinfo(name, console_scripts, gui_scripts, generator, generator_version, - inventory, metadata_overrides, prefix, project_root, requires_dists, - wheel_tag): +def distinfo(name, console_scripts, gui_scripts, sbom_files, generator, + generator_version, inventory, metadata_overrides, prefix, project_root, + requires_dists, wheel_tag): """ Create and populate a .dist-info directory from an inventory file. """ if prefix is None: @@ -50,12 +50,13 @@ # Create the directory. create_distinfo(name, wheel_tag, installed, metadata, requires_dists, - project_root, console_scripts, gui_scripts, prefix_dir=prefix, - generator=generator, generator_version=generator_version) + project_root, console_scripts, gui_scripts, sbom_files, + prefix_dir=prefix, generator=generator, + generator_version=generator_version) def create_distinfo(distinfo_dir, wheel_tag, installed, metadata, - requires_dists, project_root, console_scripts, gui_scripts, + requires_dists, project_root, console_scripts, gui_scripts, sbom_files, prefix_dir='', generator=None, generator_version=None): """ Create and populate a .dist-info directory. """ @@ -87,26 +88,20 @@ real_distinfo_dir), str(e)) - # Reproducable builds. - installed.sort() - - # Copy any license files. + # Copy any license and sbom files. saved = os.getcwd() os.chdir(project_root) - license_root = os.path.join(distinfo_dir, 'licenses') - - for license_patt in metadata.get('license-file', []): - for license_file in glob.glob(license_patt): - license_fn = os.path.join(license_root, license_file) - installed.append(license_fn) - - real_license_dir = prefix_dir + os.path.dirname(license_fn) - os.makedirs(real_license_dir, exist_ok=True) - shutil.copy(license_file, real_license_dir) + _install_files(metadata.get('license-file'), prefix_dir, + os.path.join(distinfo_dir, 'licenses'), installed) + _install_files(sbom_files, prefix_dir, os.path.join(distinfo_dir, 'sboms'), + installed) os.chdir(saved) + # Reproducable builds. + installed.sort() + if wheel_tag is None: # Create the INSTALLER file. installer_fn = os.path.join(distinfo_dir, 'INSTALLER') @@ -234,6 +229,22 @@ metadata_f.write(description_f.read()) +def _install_files(files, prefix_dir, target_dir, installed): + """ Install a list of files into a subdirectory of the .dist-info + directory and update the list of installed files. + """ + + if files: + for patt in files: + for file in glob.glob(patt): + file_fn = os.path.join(target_dir, file) + installed.append(file_fn) + + real_dir = prefix_dir + os.path.dirname(file_fn) + os.makedirs(real_dir, exist_ok=True) + shutil.copy(file, real_dir) + + def _write_metadata_item(name, value, metadata_f): """ Write a single metadata item. """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/sipbuild/distutils_builder.py new/sip-6.13.1/sipbuild/distutils_builder.py --- old/sip-6.12.0/sipbuild/distutils_builder.py 2025-06-03 15:59:00.000000000 +0200 +++ new/sip-6.13.1/sipbuild/distutils_builder.py 2025-10-10 17:26:04.000000000 +0200 @@ -83,7 +83,7 @@ create_distinfo(project.get_distinfo_dir(target_dir), wheel_tag, installed, project.metadata, project.get_requires_dists(), project.root_dir, project.console_scripts, - project.gui_scripts) + project.gui_scripts, project.sbom_files) def _build_extension_module(self, buildable): """ Build an extension module from the sources. """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/sipbuild/generator/outputs/code/code.py new/sip-6.13.1/sipbuild/generator/outputs/code/code.py --- old/sip-6.12.0/sipbuild/generator/outputs/code/code.py 2025-06-03 15:59:00.000000000 +0200 +++ new/sip-6.13.1/sipbuild/generator/outputs/code/code.py 2025-10-10 17:26:04.000000000 +0200 @@ -3399,7 +3399,7 @@ # Any shadow code. if klass.has_shadow: - if not klass.export_derived: + if not (klass.export_derived or klass.export_derived_locally): _shadow_class_declaration(sf, spec, bindings, klass) _shadow_code(sf, spec, bindings, klass) @@ -4833,7 +4833,7 @@ if arg.type is ArgumentType.PYOBJECT: return 'O' - if arg.type in (ArgumentType.PYTUPLE, ArgumentType.PYLIST, ArgumentType.PYDICT, ArgumentType.SLICE, ArgumentType.PYTYPE): + if arg.type in (ArgumentType.PYTUPLE, ArgumentType.PYLIST, ArgumentType.PYDICT, ArgumentType.PYSLICE, ArgumentType.PYTYPE): return 'N' if arg.allow_none else 'T' if arg.type is ArgumentType.PYBUFFER: @@ -5075,6 +5075,10 @@ if klass.iface_file.module is module: _class_api(sf, spec, klass) + if klass.export_derived_locally: + sf.write_code(klass.iface_file.type_header_code) + _shadow_class_declaration(sf, spec, bindings, klass) + if klass.export_derived: sf.write_code(klass.iface_file.type_header_code) _shadow_class_declaration(sf, spec, bindings, klass) @@ -5224,6 +5228,8 @@ public: ''') + sf.write_code(klass.type_derived_code) + # Define a shadow class for any protected classes we have. for protected_klass in spec.classes: if not protected_klass.is_protected: @@ -7375,7 +7381,9 @@ return f'(*sipCpp)[{_get_slot_arg(spec, overload, 0)}]' if py_slot in (PySlot.INT, PySlot.FLOAT): - return '*sipCpp' + cpp_type = fmt_argument_as_cpp_type(spec, + overload.cpp_signature.result) + return cpp_type + '(*sipCpp)' if py_slot is PySlot.ADD: return _get_number_slot_call(spec, overload, '+') @@ -8566,7 +8574,7 @@ if module.docstring is not None: sf.write( f''' -"PyDoc_STRVAR(doc_mod_{module.py_name}, "{_docstring_text(module.docstring)}"); +PyDoc_STRVAR(doc_mod_{module.py_name}, "{_docstring_text(module.docstring)}"); ''') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/sipbuild/generator/parser/annotations.py new/sip-6.13.1/sipbuild/generator/parser/annotations.py --- old/sip-6.12.0/sipbuild/generator/parser/annotations.py 2025-06-03 15:59:00.000000000 +0200 +++ new/sip-6.13.1/sipbuild/generator/parser/annotations.py 2025-10-10 17:26:04.000000000 +0200 @@ -218,6 +218,7 @@ 'DelayDtor': boolean(), 'DisallowNone': boolean(), 'ExportDerived': boolean(), + 'ExportDerivedLocally': boolean(), 'External': boolean(), 'Encoding': string(), 'Factory': boolean(), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/sipbuild/generator/parser/parser_manager.py new/sip-6.13.1/sipbuild/generator/parser/parser_manager.py --- old/sip-6.12.0/sipbuild/generator/parser/parser_manager.py 2025-06-03 15:59:00.000000000 +0200 +++ new/sip-6.13.1/sipbuild/generator/parser/parser_manager.py 2025-10-10 17:26:04.000000000 +0200 @@ -123,6 +123,8 @@ klass.supertype = cached_name(self.spec, supertype) klass.export_derived = annotations.get('ExportDerived', False) + klass.export_derived_locally = annotations.get('ExportDerivedLocally', + False) klass.mixin = annotations.get('Mixin', False) file_extension = annotations.get('FileExtension') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/sipbuild/generator/parser/rules.py new/sip-6.13.1/sipbuild/generator/parser/rules.py --- old/sip-6.12.0/sipbuild/generator/parser/rules.py 2025-06-03 15:59:00.000000000 +0200 +++ new/sip-6.13.1/sipbuild/generator/parser/rules.py 2025-10-10 17:26:04.000000000 +0200 @@ -1427,6 +1427,19 @@ pm.scope.type_code.append(p[2]) +# %TypeDerivedCode ############################################################ + +def p_type_derived_code(p): + "type_derived_code : TypeDerivedCode CODE_BLOCK" + + pm = p.parser.pm + + if pm.skipping: + return + + pm.scope.type_derived_code.append(p[2]) + + # %TypeHeaderCode ############################################################# def p_type_header_code(p): @@ -1741,6 +1754,7 @@ 'DelayDtor', 'Deprecated', 'ExportDerived', + 'ExportDerivedLocally', 'External', 'FileExtension', 'Metatype', @@ -1882,6 +1896,9 @@ return if p[1] != 'public': + # In SIP v7 this will be an error. + pm.deprecated(p, 1) + p[0] = None return @@ -1990,6 +2007,7 @@ | property | release_buffer_code | type_code + | type_derived_code | type_header_code | type_hint_code | deprecated_code_directives CODE_BLOCK""" @@ -3058,7 +3076,6 @@ """opt_namespace_body : '{' namespace_body '}' | empty""" - def p_namespace_body(p): """namespace_body : namespace_statement | namespace_body namespace_statement""" @@ -3153,7 +3170,7 @@ 'AllowNone', 'DelayDtor', 'Deprecated', - 'ExportDerived', + 'ExportDerivedLocally', 'External', 'FileExtension', 'Metatype', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/sipbuild/generator/parser/tokens.py new/sip-6.13.1/sipbuild/generator/parser/tokens.py --- old/sip-6.12.0/sipbuild/generator/parser/tokens.py 2025-06-03 15:59:00.000000000 +0200 +++ new/sip-6.13.1/sipbuild/generator/parser/tokens.py 2025-10-10 17:26:04.000000000 +0200 @@ -43,8 +43,8 @@ 'InitialisationCode', 'InstanceCode', 'MethodCode', 'ModuleCode', 'ModuleHeaderCode', 'PickleCode', 'PostInitialisationCode', 'PreInitialisationCode', 'PreMethodCode', 'RaiseCode', 'ReleaseCode', - 'SetCode', 'TypeCode', 'TypeHeaderCode', 'TypeHintCode', 'UnitCode', - 'UnitPostIncludeCode', 'VirtualCallCode', 'VirtualCatcherCode', + 'SetCode', 'TypeCode', 'TypeDerivedCode', 'TypeHeaderCode', 'TypeHintCode', + 'UnitCode', 'UnitPostIncludeCode', 'VirtualCallCode', 'VirtualCatcherCode', 'VirtualErrorHandler', # Remove in SIP v7. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/sipbuild/generator/resolver/resolver.py new/sip-6.13.1/sipbuild/generator/resolver/resolver.py --- old/sip-6.12.0/sipbuild/generator/resolver/resolver.py 2025-06-03 15:59:00.000000000 +0200 +++ new/sip-6.13.1/sipbuild/generator/resolver/resolver.py 2025-10-10 17:26:04.000000000 +0200 @@ -943,6 +943,7 @@ ArgumentType.USHORT, ArgumentType.INT, ArgumentType.UINT, + ArgumentType.BOOL, ) def _resolve_enums(spec, error_log): @@ -1933,7 +1934,7 @@ def _name_lookup(spec, mod, scoped_name, type): - """ Look up a name and resole the corresponding type. """ + """ Look up a name and resolve the corresponding type. """ _search_mapped_types(spec, mod, type, scoped_name) if type.type is not ArgumentType.NONE: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/sipbuild/generator/specification.py new/sip-6.13.1/sipbuild/generator/specification.py --- old/sip-6.12.0/sipbuild/generator/specification.py 2025-06-03 15:59:00.000000000 +0200 +++ new/sip-6.13.1/sipbuild/generator/specification.py 2025-10-10 17:26:04.000000000 +0200 @@ -1516,6 +1516,9 @@ # Set if /ExportDerived/ was specified. export_derived: bool = False + # Set if /ExportDerivedLocally/ was specified. + export_derived_locally: bool = False + # Set if /External/ was specified. external: bool = False @@ -1636,6 +1639,9 @@ # The %TypeCode. type_code: list[CodeBlock] = field(default_factory=list) + # The %TypeDerivedCode. + type_derived_code: list[CodeBlock] = field(default_factory=list) + # The %TypeHintCode. type_hint_code: Optional[CodeBlock] = None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/sipbuild/generator/utils.py new/sip-6.13.1/sipbuild/generator/utils.py --- old/sip-6.12.0/sipbuild/generator/utils.py 2025-06-03 15:59:00.000000000 +0200 +++ new/sip-6.13.1/sipbuild/generator/utils.py 2025-10-10 17:26:04.000000000 +0200 @@ -355,12 +355,18 @@ # The types must be the same. if type1.type is not type2.type: + # Compare original typedef names if appropriate. + if type1.original_typedef is not None and type2.type is ArgumentType.DEFINED: + return type1.original_typedef.fq_cpp_name.matches(type2.definition) + + if type1.type is ArgumentType.DEFINED and type2.original_typedef is not None: + return type2.original_typedef.fq_cpp_name.matches(type1.definition) + # If we are comparing a template with those that have already been used # to instantiate a class or mapped type then we need to compare with # the class or mapped type name. if type1.type is ArgumentType.CLASS and type2.type is ArgumentType.DEFINED: - return type1.definition.iface_file.fq_cpp_name.matches(type2.definition) if type1.type is ArgumentType.DEFINED and type2.type is ArgumentType.CLASS: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/sipbuild/project.py new/sip-6.13.1/sipbuild/project.py --- old/sip-6.12.0/sipbuild/project.py 2025-06-03 15:59:00.000000000 +0200 +++ new/sip-6.13.1/sipbuild/project.py 2025-10-10 17:26:04.000000000 +0200 @@ -4,6 +4,7 @@ import collections +import locale import os import packaging import shutil @@ -70,16 +71,20 @@ # The minor version number of the target Python installation. Option('py_minor_version', option_type=int), - # The name of the directory containing the .sip files. If the sip - # module is shared then each set of bindings is in its own - # sub-directory. - Option('sip_files_dir', default='.'), + # The list of files and directories, specified as glob patterns, that + # should be included in the dist-info/sboms directory of a wheel. + Option('sbom_files', option_type=list), # The list of files and directories, specified as glob patterns # relative to the project directory, that should be excluded from an # sdist. Option('sdist_excludes', option_type=list), + # The name of the directory containing the .sip files. If the sip + # module is shared then each set of bindings is in its own + # sub-directory. + Option('sip_files_dir', default='.'), + # The list of additional directories to search for .sip files. Option('sip_include_dirs', option_type=list), @@ -463,7 +468,11 @@ for rd in self.get_requires_dists(): args.append('--requires-dist') - args.append('\\"{}\\"'.format(rd)) + args.append(f'\\"{rd}\\"') + + for sbom_file in self.sbom_files: + args.append('--sbom') + args.append(sbom_file) for metadata, value in self._metadata_overrides.items(): if value: @@ -594,7 +603,7 @@ with subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=stderr) as pipe: for line in pipe.stdout: - yield str(line, encoding=sys.stdout.encoding) + yield str(line, encoding=locale.getpreferredencoding(), errors="ignore") if pipe.returncode != 0 and fatal: raise UserException( @@ -603,10 +612,29 @@ def run_command(self, args, *, fatal=True): """ Run a command and display the output if requested. """ + deck = None if self.verbose else collections.deque((), 100) + # Read stdout and stderr until there is no more output. - for line in self.read_command_pipe(args, and_stderr=True, fatal=fatal): - if self.verbose: - sys.stdout.write(line) + nr_lines = 0 + + try: + for line in self.read_command_pipe(args, and_stderr=True, fatal=fatal): + nr_lines += 1 + + if deck is None: + sys.stdout.write(line) + else: + deck.append(line) + except UserException: + if deck is not None: + for line in deck: + sys.stdout.write(line) + + if nr_lines > deck.maxlen: + sys.stdout.write( + "To see the full output use the --verbose option.\n") + + raise def setup(self, pyproject, tool, tool_description): """ Complete the configuration of the project. """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/sipbuild/setuptools_builder.py new/sip-6.13.1/sipbuild/setuptools_builder.py --- old/sip-6.12.0/sipbuild/setuptools_builder.py 2025-06-03 15:59:00.000000000 +0200 +++ new/sip-6.13.1/sipbuild/setuptools_builder.py 2025-10-10 17:26:04.000000000 +0200 @@ -80,7 +80,7 @@ create_distinfo(project.get_distinfo_dir(target_dir), wheel_tag, installed, project.metadata, project.get_requires_dists(), project.root_dir, project.console_scripts, - project.gui_scripts) + project.gui_scripts, project.sbom_files) def _build_extension_module(self, buildable): """ Build an extension module from the sources. """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/sipbuild/tools/distinfo.py new/sip-6.13.1/sipbuild/tools/distinfo.py --- old/sip-6.12.0/sipbuild/tools/distinfo.py 2025-06-03 15:59:00.000000000 +0200 +++ new/sip-6.13.1/sipbuild/tools/distinfo.py 2025-10-10 17:26:04.000000000 +0200 @@ -1,6 +1,6 @@ # SPDX-License-Identifier: BSD-2-Clause -# Copyright (c) 2024 Phil Thompson <[email protected]> +# Copyright (c) 2025 Phil Thompson <[email protected]> from ..argument_parser import ArgumentParser @@ -47,6 +47,9 @@ parser.add_argument('--requires-dist', dest='requires_dists', action='append', help="additional Requires-Dist", metavar="EXPR") + parser.add_argument('--sbom', dest='sbom_files', action='append', + help="the name of an SBOM file", metavar='FILE') + parser.add_argument('--wheel-tag', help="the tag if a wheel is being created", metavar="TAG") @@ -57,7 +60,8 @@ try: distinfo(name=args.names[0], console_scripts=args.console_scripts, - gui_scripts=args.gui_scripts, generator=args.generator, + gui_scripts=args.gui_scripts, sbom_files=args.sbom_files, + generator=args.generator, generator_version=args.generator_version, inventory=args.inventory, metadata_overrides=args.metadata_overrides, prefix=args.prefix, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/test/README.md new/sip-6.13.1/test/README.md --- old/sip-6.12.0/test/README.md 2025-06-03 15:59:00.000000000 +0200 +++ new/sip-6.13.1/test/README.md 2025-10-10 17:26:04.000000000 +0200 @@ -17,8 +17,8 @@ ## Writing Unit Tests -Each test sub-directory should contain an empty `__init__.py` file, a Python -test script with the prefix `test_` and one or more `.sip` files. +Each test sub-directory should contain a Python test script with the prefix +`test_` and one or more `.sip` files. The test script should contain a sub-class of `SIPTestCase`, which is imported from the `utils` module. This will automatically build any modules (defined by diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/test/docstrings/docstrings_module.sip new/sip-6.13.1/test/docstrings/docstrings_module.sip --- old/sip-6.12.0/test/docstrings/docstrings_module.sip 1970-01-01 01:00:00.000000000 +0100 +++ new/sip-6.13.1/test/docstrings/docstrings_module.sip 2025-10-10 17:26:04.000000000 +0200 @@ -0,0 +1,9 @@ +// The SIP implementation of the docstrings_module test module. + + +%Module(name=docstrings_module) +{ +%Docstring +Module +%End +}; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/test/docstrings/test_docstrings.py new/sip-6.13.1/test/docstrings/test_docstrings.py --- old/sip-6.12.0/test/docstrings/test_docstrings.py 1970-01-01 01:00:00.000000000 +0100 +++ new/sip-6.13.1/test/docstrings/test_docstrings.py 2025-10-10 17:26:04.000000000 +0200 @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: BSD-2-Clause + +# Copyright (c) 2025 Phil Thompson <[email protected]> + + +from utils import SIPTestCase + + +class DocstringsTestCase(SIPTestCase): + """ Test the support for docstrings. """ + + def test_ModuleDocstrings(self): + """ Test the support for timelines. """ + + import docstrings_module as dm + + self.assertEqual(dm.__doc__, 'Module') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/test/gen_classes/gen_nonpublic_superclasses_module.sip new/sip-6.13.1/test/gen_classes/gen_nonpublic_superclasses_module.sip --- old/sip-6.12.0/test/gen_classes/gen_nonpublic_superclasses_module.sip 1970-01-01 01:00:00.000000000 +0100 +++ new/sip-6.13.1/test/gen_classes/gen_nonpublic_superclasses_module.sip 2025-10-10 17:26:04.000000000 +0200 @@ -0,0 +1,13 @@ +// The SIP implementation of the gen_nonpublic_superclasses_module test module. + + +%Module(name=gen_nonpublic_superclasses_module) + + +class Bar +{ +}; + +class Foo : protected Bar +{ +}; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/test/gen_classes/test_gen_classes.py new/sip-6.13.1/test/gen_classes/test_gen_classes.py --- old/sip-6.12.0/test/gen_classes/test_gen_classes.py 1970-01-01 01:00:00.000000000 +0100 +++ new/sip-6.13.1/test/gen_classes/test_gen_classes.py 2025-10-10 17:26:04.000000000 +0200 @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: BSD-2-Clause + +# Copyright (c) 2025 Phil Thompson <[email protected]> + + +from utils import SIPGeneratorTestCase + + +class GenerateClassesTestCase(SIPGeneratorTestCase): + """ Test the generator support for classes. """ + + def test_Nonpublic_Superclasses(self): + """ Test the support non-public super-classes. """ + + # Check that the use of non-public super-classes fails. + self.assertFalse( + self.run_generator_test( + 'gen_nonpublic_superclasses_module.sip')) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/test/movable/test_movable.py new/sip-6.13.1/test/movable/test_movable.py --- old/sip-6.12.0/test/movable/test_movable.py 2025-06-03 15:59:00.000000000 +0200 +++ new/sip-6.13.1/test/movable/test_movable.py 2025-10-10 17:26:04.000000000 +0200 @@ -22,26 +22,30 @@ ao = AnObject(3) ow = ObjectWrapper() + # As of Python v3.14 we can't make assumptions about initial + # reference counts so we test for increases and decreases rather than + # absolute values. + ao_base_refcount = getrefcount(ao) + # Test the value of the object. self.assertEqual(ao.getValue(), 3) - self.assertEqual(getrefcount(ao), 2) + self.assertEqual(getrefcount(ao), ao_base_refcount) # Test an empty wrapper. self.assertEqual(ow.getObjectValue(), -1000) # Test an non-empty wrapper. ow.setObject(ao) - self.assertEqual(getrefcount(ao), 3) + self.assertEqual(getrefcount(ao), ao_base_refcount + 1) self.assertEqual(ow.getObjectValue(), 4) # Unwrap the object and test the wrapper. ao2 = ow.takeObject() - self.assertEqual(getrefcount(ao2), 2) self.assertEqual(ow.getObjectValue(), -1000) # Re-test the value of the object. self.assertEqual(ao2.getValue(), 4) # Check that the original Python object no longer wraps the C++ object. - self.assertEqual(getrefcount(ao), 2) + self.assertEqual(getrefcount(ao), ao_base_refcount) self.assertRaises(RuntimeError, ao.getValue) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/test/template_typedef/template_typedef_module.sip new/sip-6.13.1/test/template_typedef/template_typedef_module.sip --- old/sip-6.12.0/test/template_typedef/template_typedef_module.sip 1970-01-01 01:00:00.000000000 +0100 +++ new/sip-6.13.1/test/template_typedef/template_typedef_module.sip 2025-10-10 17:26:04.000000000 +0200 @@ -0,0 +1,172 @@ +// The SIP implementation of the typedefs_module test module. + + +%Module(name=template_typedef_module) + + +%ExportedHeaderCode +// The need for this, ie. to resolve the type as a typedef, is a SIP bug. +#define getSipType(sipTypename) (sipResolveTypedef(sipTypename) ? sipFindType(sipResolveTypedef(sipTypename)) : sipFindType(sipTypename)) +%End + + +template<TYPE> +%MappedType std::vector<TYPE> /TypeHint="List[TYPE]"/ { +%TypeHeaderCode +#include <vector> +%End + +%ConvertFromTypeCode + //const sipTypeDef *kpTypeDef = sipFindType("TYPE"); + const sipTypeDef *kpTypeDef = getSipType("TYPE"); + + if (!kpTypeDef) { + return NULL; + } + + // Create the Python list of the correct length. + PyObject *l; + + if ((l = PyList_New(sipCpp->size())) == NULL) { + return NULL; + } + + // Go through each element in the C++ instance and convert it to a SIP + // wrapper. + for (size_t i = 0; i < sipCpp->size(); ++i) { + TYPE *cpp = new TYPE(sipCpp->at(i)); + PyObject *pobj; + + // Get the Python wrapper for the Type instance, creating a new one if + // necessary, and handle any ownership transfer. + if ((pobj = sipConvertFromNewType(cpp, kpTypeDef, sipTransferObj)) == NULL) { + // There was an error so garbage collect the Python list. + Py_XDECREF(l); + return NULL; + } + + // Add the wrapper to the list. + PyList_SET_ITEM(l, i, pobj); + } + + // Return the Python list. + return l; +%End + +%ConvertToTypeCode + //const sipTypeDef *kpTypeDef = sipFindType("TYPE"); + const sipTypeDef *kpTypeDef = getSipType("TYPE"); + + if (!kpTypeDef) { + return 0; + } + + // Check if type is compatible + if (sipIsErr == NULL) { + return PyList_Check(sipPy); + } + + // Convert Python list of TYPE to std::vector<TYPE> + std::vector<TYPE> *v = new std::vector<TYPE>(); + v->reserve(PyList_GET_SIZE(sipPy)); + + for (Py_ssize_t i = 0; i < PyList_GET_SIZE(sipPy); ++i) { + int state; + TYPE *p = static_cast<TYPE *>(sipConvertToType( + PyList_GET_ITEM(sipPy, i), kpTypeDef, sipTransferObj, + SIP_NOT_NONE, &state, sipIsErr)); + + if (*sipIsErr) { + sipReleaseType(p, kpTypeDef, state); + delete v; + return 0; + } + + v->push_back(*p); + sipReleaseType(p, kpTypeDef, state); + } + + *sipCppPtr = v; + + return sipGetState(sipTransferObj); +%End +}; + + +%MappedType std::vector<int> /TypeHint="List[int]"/ { +%TypeHeaderCode +#include <vector> +%End + +%ConvertFromTypeCode + PyObject *l; + + // Create the Python list of the correct length. + if ((l = PyList_New(sipCpp->size())) == NULL) { + return NULL; + } + + // Go through each element in the C++ instance and convert it to a wrapped + // object. + for (size_t i = 0; i < sipCpp->size(); ++i) { + // Add the wrapper to the list. + PyList_SET_ITEM(l, i, PyLong_FromLong(sipCpp->at(i))); + } + + // Return the Python list. + return l; +%End + +%ConvertToTypeCode + // Check if type is compatible + if (sipIsErr == NULL) { + return PyList_Check(sipPy); + } + + // Convert Python list of integers to a std::vector<int> + std::vector<int> *v = new std::vector<int>(); + v->reserve(PyList_GET_SIZE(sipPy)); + + for (Py_ssize_t i = 0; i < PyList_GET_SIZE(sipPy); ++i) { + v->push_back(int(PyLong_AsLong(PyList_GET_ITEM(sipPy, i)))); + } + + *sipCppPtr = v; + + return sipGetState(sipTransferObj); +%End +}; + + +%ModuleHeaderCode +#include <vector> +std::vector<int> incrementVectorInt(const std::vector<int> &v); +std::vector<std::vector<int> > incrementVectorVectorInt( + const std::vector<std::vector<int> > &v); +typedef std::vector<int> VecInt; +%End + + +%ModuleCode +std::vector<int> incrementVectorInt(const std::vector<int> &v) { + std::vector<int> ret; + for (std::vector<int>::const_iterator i = v.cbegin(); i != v.cend(); ++i) { + ret.push_back(*i+1); + } + return ret; +} + +std::vector<std::vector<int> > incrementVectorVectorInt( + const std::vector<std::vector<int> > &v) { + std::vector<std::vector<int> > ret; + for (std::vector<std::vector<int> >::const_iterator i = v.cbegin(); i != v.cend(); ++i) { + ret.push_back(incrementVectorInt(*i)); + } + return ret; +} +%End + + +typedef std::vector<int> VecInt; +std::vector<int> incrementVectorInt(const std::vector<int> &v); +std::vector<VecInt> incrementVectorVectorInt(const std::vector<VecInt> &v); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/test/template_typedef/test_template_typedef.py new/sip-6.13.1/test/template_typedef/test_template_typedef.py --- old/sip-6.12.0/test/template_typedef/test_template_typedef.py 1970-01-01 01:00:00.000000000 +0100 +++ new/sip-6.13.1/test/template_typedef/test_template_typedef.py 2025-10-10 17:26:04.000000000 +0200 @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: BSD-2-Clause + +# Copyright (c) 2025 Phil Thompson <[email protected]> + + +from utils import SIPTestCase + + +class TypedefsTestCase(SIPTestCase): + """ Test the support for template typedef. """ + + def test_ModuleTemplateTypedef(self): + """ Test the support for template typedef. """ + + import template_typedef_module as ttdm + + self.assertEqual(ttdm.incrementVectorInt([1, 2, 3]), [2, 3, 4]) + self.assertEqual(ttdm.incrementVectorVectorInt([[1, 2, 3], [4, 5, 6]]), + [[2, 3, 4], [5, 6, 7]]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/test/utils/__init__.py new/sip-6.13.1/test/utils/__init__.py --- old/sip-6.12.0/test/utils/__init__.py 2025-06-03 15:59:00.000000000 +0200 +++ new/sip-6.13.1/test/utils/__init__.py 2025-10-10 17:26:04.000000000 +0200 @@ -1,6 +1,7 @@ # SPDX-License-Identifier: BSD-2-Clause -# Copyright (c) 2024 Phil Thompson <[email protected]> +# Copyright (c) 2025 Phil Thompson <[email protected]> +from .sip_generator_test_case import SIPGeneratorTestCase from .sip_test_case import SIPTestCase diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/test/utils/sip_base_test_case.py new/sip-6.13.1/test/utils/sip_base_test_case.py --- old/sip-6.12.0/test/utils/sip_base_test_case.py 1970-01-01 01:00:00.000000000 +0100 +++ new/sip-6.13.1/test/utils/sip_base_test_case.py 2025-10-10 17:26:04.000000000 +0200 @@ -0,0 +1,162 @@ +# SPDX-License-Identifier: BSD-2-Clause + +# Copyright (c) 2025 Phil Thompson <[email protected]> + + +import glob +import inspect +import os +import shutil +import subprocess +import sys +import unittest + + +class SIPBaseTestCase(unittest.TestCase): + """ The base class for all test cases. """ + + # The ABI version to use. None implies the latest major version. + abi_version = None + + # Set if exception support should be enabled. + exceptions = False + + # The list of tags to be used to configure the test modules. + tags = None + + @classmethod + def get_test_root_directory(cls): + """ Get the name of the test's root directory from the file + implementing the test case. + """ + + return os.path.dirname(inspect.getfile(cls)) + + @classmethod + def build_test_module(cls, sip_file, root_dir, use_sip_module=False, + no_compile=False): + """ Build a test extension module and return its name. """ + + build_dir = sip_file[:-4] + module_name = os.path.basename(build_dir) + + # Remove any previous build directory. + shutil.rmtree(build_dir, ignore_errors=True) + + os.mkdir(build_dir) + + # Create a pyproject.toml file. + pyproject_toml = os.path.join(build_dir, 'pyproject.toml') + + with open(pyproject_toml, 'w') as f: + f.write(_PYPROJECT_TOML.format(module_name=module_name)) + + if cls.abi_version is not None: + f.write(_ABI_VERSION.format(abi_version=cls.abi_version)) + + if use_sip_module: + f.write(_SIP_MODULE) + + if cls.tags is not None or cls.exceptions: + f.write(f'\n[tool.sip.bindings.{module_name}]\n') + + if cls.tags is not None: + tags_s = ', '.join([f'"{t}"' for t in cls.tags]) + f.write(f'tags = [{tags_s}]\n') + + if cls.exceptions: + f.write('exceptions = true\n') + + # Build and move the test module. + cls.build_module(module_name, + ['-m', 'sipbuild.tools.build', '--verbose'], build_dir, + root_dir, impl_subdirs=[module_name, 'build'], + no_compile=no_compile) + + return module_name + + @classmethod + def build_module(cls, module_name, build_args, src_dir, root_dir, + impl_subdirs=None, no_compile=False): + """ Build a module and move any implementation to the test's root + directory. + """ + + cwd = os.getcwd() + os.chdir(src_dir) + + # Do the build. + args = [sys.executable] + build_args + + if no_compile: + args.append('--no-compile') + + subprocess.run(args).check_returncode() + + if no_compile: + return + + # Find the implementation. Note that we only support setuptools and + # not distutils. + impl_pattern = ['build'] + + if impl_subdirs is not None: + impl_pattern.extend(impl_subdirs) + + impl_pattern.append('lib*') + impl_pattern.append( + module_name + '*.pyd' if sys.platform == 'win32' else '*.so') + + impl_paths = glob.glob(os.path.join(*impl_pattern)) + if len(impl_paths) == 0: + raise Exception( + "no '{0}' extension module was built".format(module_name)) + + if len(impl_paths) != 1: + raise Exception( + "unable to determine file name of the '{0}' extension module".format(module_name)) + + impl_path = os.path.abspath(impl_paths[0]) + impl = os.path.basename(impl_path) + + os.chdir(root_dir) + + try: + os.remove(impl) + except: + pass + + os.rename(impl_path, impl) + + os.chdir(cwd) + + @classmethod + def get_test_root_directory(cls): + """ Get the name of the test's root directory from the file + implementing the test case. + """ + + return os.path.dirname(inspect.getfile(cls)) + + +# The prototype pyproject.toml file. +_PYPROJECT_TOML = """ +[build-system] +requires = ["sip >=6"] +build-backend = "sipbuild.api" + +[project] +name = "{module_name}" + +[tool.sip.project] +minimum-macos-version = "10.9" +sip-files-dir = ".." +""" + +_ABI_VERSION = """ +abi-version = "{abi_version}" +""" + +_SIP_MODULE = """ +sip-module = "sip" +""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/test/utils/sip_generator_test_case.py new/sip-6.13.1/test/utils/sip_generator_test_case.py --- old/sip-6.12.0/test/utils/sip_generator_test_case.py 1970-01-01 01:00:00.000000000 +0100 +++ new/sip-6.13.1/test/utils/sip_generator_test_case.py 2025-10-10 17:26:04.000000000 +0200 @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: BSD-2-Clause + +# Copyright (c) 2025 Phil Thompson <[email protected]> + + +import os +import subprocess + + +from .sip_base_test_case import SIPBaseTestCase + + +class SIPGeneratorTestCase(SIPBaseTestCase): + """ Encapsulate a test case that tests the generation (but not the build or + execution) of a number of test modules. + """ + + @classmethod + def run_generator_test(cls, sip_file): + """ Run a test that generates a test module and return True if it was + successfully generated. + """ + + root_dir = cls.get_test_root_directory() + + try: + cls.build_test_module(os.path.join(root_dir, sip_file), root_dir, + no_compile=True) + except subprocess.CalledProcessError: + return False + + return True diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sip-6.12.0/test/utils/sip_test_case.py new/sip-6.13.1/test/utils/sip_test_case.py --- old/sip-6.12.0/test/utils/sip_test_case.py 2025-06-03 15:59:00.000000000 +0200 +++ new/sip-6.13.1/test/utils/sip_test_case.py 2025-10-10 17:26:04.000000000 +0200 @@ -4,26 +4,16 @@ import glob -import inspect import os -import shutil import subprocess import sys import tarfile -import unittest +from .sip_base_test_case import SIPBaseTestCase -class SIPTestCase(unittest.TestCase): - """ Encapsulate a test case that tests a set of standalone bindings. """ - # The ABI version to use. None implies the latest major version. - abi_version = None - - # Set if exception support should be enabled. - exceptions = False - - # The list of tags to be used to configure the test modules. - tags = None +class SIPTestCase(SIPBaseTestCase): + """ Encapsulate a test case that tests the execution of bindings. """ # Set if a separate sip module should be generated. It will be built # automatically if more than one module is being built. @@ -33,9 +23,7 @@ def setUpClass(cls): """ Build a test extension module. """ - # Get the name of the test's root directory from the file implementing - # the test case. - root_dir = os.path.dirname(inspect.getfile(cls)) + root_dir = cls.get_test_root_directory() cls._modules = [] @@ -48,7 +36,8 @@ for sip_file in sip_files: cls._modules.append( - cls._build_test_module(sip_file, use_sip_module, root_dir)) + cls.build_test_module(sip_file, root_dir, + use_sip_module=use_sip_module)) # Fix the path. sys.path.insert(0, root_dir) @@ -95,47 +84,6 @@ self._exc = xvalue @classmethod - def _build_test_module(cls, sip_file, use_sip_module, root_dir): - """ Build a test extension module and return its name. """ - - build_dir = sip_file[:-4] - module_name = os.path.basename(build_dir) - - # Remove any previous build directory. - shutil.rmtree(build_dir, ignore_errors=True) - - os.mkdir(build_dir) - - # Create a pyproject.toml file. - pyproject_toml = os.path.join(build_dir, 'pyproject.toml') - - with open(pyproject_toml, 'w') as f: - f.write(_PYPROJECT_TOML.format(module_name=module_name)) - - if cls.abi_version is not None: - f.write(_ABI_VERSION.format(abi_version=cls.abi_version)) - - if use_sip_module: - f.write(_SIP_MODULE) - - if cls.tags is not None or cls.exceptions: - f.write(f'\n[tool.sip.bindings.{module_name}]\n') - - if cls.tags is not None: - tags_s = ', '.join([f'"{t}"' for t in cls.tags]) - f.write(f'tags = [{tags_s}]\n') - - if cls.exceptions: - f.write('exceptions = true\n') - - # Build and move the test module. - cls._build_module(module_name, - ['-m', 'sipbuild.tools.build', '--verbose'], build_dir, - root_dir, impl_subdirs=[module_name, 'build']) - - return module_name - - @classmethod def _build_sip_module(cls, root_dir): """ Build the sip module and return its name. """ @@ -170,78 +118,7 @@ # Build and move the module. src_dir = os.path.join(root_dir, os.path.basename(sdist)[:-7]) - cls._build_module(sip_module_name, ['setup.py', 'build'], src_dir, + cls.build_module(sip_module_name, ['setup.py', 'build'], src_dir, root_dir) return sip_module_name - - @classmethod - def _build_module(cls, module_name, build_args, src_dir, root_dir, - impl_subdirs=None): - """ Build a module and move the implementation to the test's root - directory. - """ - - cwd = os.getcwd() - os.chdir(src_dir) - - # Do the build. - args = [sys.executable] + build_args - subprocess.run(args).check_returncode() - - # Find the implementation. Note that we only support setuptools and - # not distutils. - impl_pattern = ['build'] - - if impl_subdirs is not None: - impl_pattern.extend(impl_subdirs) - - impl_pattern.append('lib*') - impl_pattern.append( - module_name + '*.pyd' if sys.platform == 'win32' else '*.so') - - impl_paths = glob.glob(os.path.join(*impl_pattern)) - if len(impl_paths) == 0: - raise Exception( - "no '{0}' extension module was built".format(module_name)) - - if len(impl_paths) != 1: - raise Exception( - "unable to determine file name of the '{0}' extension module".format(module_name)) - - impl_path = os.path.abspath(impl_paths[0]) - impl = os.path.basename(impl_path) - - os.chdir(root_dir) - - try: - os.remove(impl) - except: - pass - - os.rename(impl_path, impl) - - os.chdir(cwd) - - -# The prototype pyproject.toml file. -_PYPROJECT_TOML = """ -[build-system] -requires = ["sip >=6"] -build-backend = "sipbuild.api" - -[project] -name = "{module_name}" - -[tool.sip.project] -minimum-macos-version = "10.9" -sip-files-dir = ".." -""" - -_ABI_VERSION = """ -abi-version = "{abi_version}" -""" - -_SIP_MODULE = """ -sip-module = "sip" -""" ++++++ sip-issue95.patch ++++++ >From b6a7ec2453899b878ffe0b2b5b1f642a0992aa6d Mon Sep 17 00:00:00 2001 From: Phil Thompson <[email protected]> Date: Thu, 16 Oct 2025 13:16:47 +0100 Subject: [PATCH] Bug fixes Fixed a regression in v6.13.1 in the handling of composite modules. Resolves #95 --- sipbuild/generator/resolver/resolver.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sipbuild/generator/resolver/resolver.py b/sipbuild/generator/resolver/resolver.py index 9dbc2e9e..492b2e30 100644 --- a/sipbuild/generator/resolver/resolver.py +++ b/sipbuild/generator/resolver/resolver.py @@ -38,6 +38,10 @@ def resolve(spec, modules): # Set the base name of the module. This is done for efficiency. mod.py_name = mod.fq_py_name.name.split('.')[-1] + # There is nothing else that needs doing for composite modules. + if spec.is_composite: + return + # Set the default meta-type for the main module if it doesn't have one # explicitly set. if spec.module.default_metatype is None:
