Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-PyQt6 for openSUSE:Factory checked in at 2023-07-25 11:50:19 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-PyQt6 (Old) and /work/SRC/openSUSE:Factory/.python-PyQt6.new.1467 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-PyQt6" Tue Jul 25 11:50:19 2023 rev:16 rq:1099759 version:6.5.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-PyQt6/python-PyQt6.changes 2023-06-12 15:26:55.195218298 +0200 +++ /work/SRC/openSUSE:Factory/.python-PyQt6.new.1467/python-PyQt6.changes 2023-07-25 11:51:32.229604917 +0200 @@ -1,0 +2,12 @@ +Thu Jul 20 10:57:34 UTC 2023 - Markéta Machová <mmach...@suse.com> + +- Update to 6.5.1 + * Added support for QPermission and related classes and methods. + * Added the max_workers argument to uic.compileUiDir() to specify + the maximum number of worker processes to use when compiling + the .ui files in a directory. + * pyuic6 will now compile all the .ui files in a directory if the + name of the directory is passed instead of a .ui file. + * Added the --max-workers command line option to pyuic6. + +------------------------------------------------------------------- Old: ---- PyQt6-6.5.0.tar.gz New: ---- PyQt6-6.5.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-PyQt6.spec ++++++ --- /var/tmp/diff_new_pack.bpJJFa/_old 2023-07-25 11:51:34.753619695 +0200 +++ /var/tmp/diff_new_pack.bpJJFa/_new 2023-07-25 11:51:34.757619719 +0200 @@ -21,7 +21,7 @@ %define pyqt_build_for_qt6 1 %{?sle15_python_module_pythons} Name: python-%{mname} -Version: 6.5.0 +Version: 6.5.1 Release: 0 Summary: Python bindings for Qt 6 License: GPL-3.0-only OR SUSE-GPL-2.0-with-FLOSS-exception OR NonFree ++++++ PyQt6-6.5.0.tar.gz -> PyQt6-6.5.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyQt6-6.5.0/ChangeLog new/PyQt6-6.5.1/ChangeLog --- old/PyQt6-6.5.0/ChangeLog 2023-04-05 14:16:47.435856000 +0200 +++ new/PyQt6-6.5.1/ChangeLog 2023-06-02 12:40:57.300344000 +0200 @@ -1,5 +1,50 @@ +2023-05-26 Phil Thompson <p...@riverbankcomputing.com> + + * NEWS, qpy/QtCore/qpycore_pyqtboundsignal.cpp: + Fixed a regression that broke pyqtSlot arguments that where + typedefs. + [443f350df939] [6.5.1] <6.5-maint> + +2023-05-25 Phil Thompson <p...@riverbankcomputing.com> + + * NEWS, project.py: + Ensure the permission plugins are built in. + [dea56beea30d] <6.5-maint> + +2023-05-23 Phil Thompson <p...@riverbankcomputing.com> + + * PyQt6.msp: + Fixed the conversion of QPermission. + [ba763ef07730] <6.5-maint> + +2023-05-22 Phil Thompson <p...@riverbankcomputing.com> + + * PyQt6.msp, config-tests/cfgtest_QtCore.cpp: + Fixed build issues with the permissions support. + [04c3b8f59a6c] <6.5-maint> + + * NEWS, PyQt6.msp, config-tests/cfgtest_QtCore.cpp: + Added support for QPermission et al. + [f673353b1366] <6.5-maint> + +2023-04-24 Phil Thompson <p...@riverbankcomputing.com> + + * extras/uic/pyuic.py: + pyuic6 doesn't, for consistency reasons, recurse when compiling a + directory. + [7f0f1196edd6] <6.5-maint> + + * NEWS, extras/uic/compile_ui.py, extras/uic/pyuic.py: + compileUiDir() can now compile muliple .ui files in parallel. pyuic6 + can now compile a directory contents. + [09ca845e4626] <6.5-maint> + 2023-04-05 Phil Thompson <p...@riverbankcomputing.com> + * .hgtags: + Added tag 6.5.0 for changeset db2b0386d4f1 + [a2256de71810] + * NEWS, qpy/QtCore/qpycore_chimera.cpp: Fixed a regression in the handling of QFlags as signal arguments. [db2b0386d4f1] [6.5.0] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyQt6-6.5.0/NEWS new/PyQt6-6.5.1/NEWS --- old/PyQt6-6.5.0/NEWS 2023-04-05 14:16:47.437537000 +0200 +++ new/PyQt6-6.5.1/NEWS 2023-06-02 12:40:57.301942800 +0200 @@ -1,3 +1,13 @@ +v6.5.1 26th May 2023 + - Added support for QPermission and related classes and methods. + - Added the max_workers argument to uic.compileUiDir() to specifiy the + maximum number of worker processes to use when compiling the .ui files in a + directory. + - pyuic6 will now compile all the .ui files in a directory if the name of the + directory is passed instead of a .ui file. + - Added the --max-workers command line option to pyuic6. + - Bug fixes. + v6.5.0 5th April 2023 - Added support for Qt v6.5 including the QtSpatialAudio module. - Bug fixes. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyQt6-6.5.0/PKG-INFO new/PyQt6-6.5.1/PKG-INFO --- old/PyQt6-6.5.0/PKG-INFO 2023-04-05 14:16:56.788123400 +0200 +++ new/PyQt6-6.5.1/PKG-INFO 2023-06-02 12:41:06.071410200 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: PyQt6 -Version: 6.5.0 +Version: 6.5.1 Requires-Python: >=3.6.1 Summary: Python bindings for the Qt cross platform application toolkit Home-Page: https://www.riverbankcomputing.com/software/pyqt/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyQt6-6.5.0/config-tests/cfgtest_QtCore.cpp new/PyQt6-6.5.1/config-tests/cfgtest_QtCore.cpp --- old/PyQt6-6.5.0/config-tests/cfgtest_QtCore.cpp 2023-04-05 14:16:47.446821000 +0200 +++ new/PyQt6-6.5.1/config-tests/cfgtest_QtCore.cpp 2023-06-02 12:40:57.311180600 +0200 @@ -31,5 +31,9 @@ if (sizeof (qreal) != sizeof (double)) out << "PyQt_qreal_double\n"; +#if !QT_CONFIG(permissions) + out << "PyQt_Permissions"; +#endif + return 0; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyQt6-6.5.0/project.py new/PyQt6-6.5.1/project.py --- old/PyQt6-6.5.0/project.py 2023-04-05 14:16:47.685877000 +0200 +++ new/PyQt6-6.5.1/project.py 2023-06-02 12:40:57.553051200 +0200 @@ -517,8 +517,43 @@ if not project.qt_shared and test_output[0] == 'shared': project.qt_shared = True + # If permissions are available make sure the static plugins get + # compiled in. + if 'PyQt_Permissions' not in test_output: + self.builder_settings.append('CONFIG += permssions') + self.builder_settings.append( + 'QMAKE_INFO_PLIST = ' + self._info_plist()) + return super().handle_test_output(test_output[1:]) + def _info_plist(self): + """ Create an Info.plist that contains entries for all supported + permissions and return the absolute name of the file. + """ + + keys = ('NSBluetoothAlwaysUsageDescription', + 'NSCalendarsUsageDescription', 'NSCameraUsageDescription', + 'NSContactsUsageDescription', 'NSLocationUsageDescription', + 'NSMicrophoneUsageDescription') + + content = '\n'.join( + ['<key>{}</key><string>Dummy</string>'.format(k) for k in keys]) + + info_plist = os.path.join(self.project.build_dir, 'Info.plist') + + with open(info_plist, 'w') as f: + f.write( +'''<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> +{} +</dict> +</plist> +'''.format(content)) + + return info_plist + class QtDBus(PyQtBindings): """ The QtDBus bindings. """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyQt6-6.5.0/pyproject.toml new/PyQt6-6.5.1/pyproject.toml --- old/PyQt6-6.5.0/pyproject.toml 2023-04-05 14:16:47.686265200 +0200 +++ new/PyQt6-6.5.1/pyproject.toml 2023-06-02 12:40:57.553341000 +0200 @@ -6,7 +6,7 @@ # Specify the PEP 566 metadata for the project. [tool.sip.metadata] name = "PyQt6" -version = "6.5.0" +version = "6.5.1" summary = "Python bindings for the Qt cross platform application toolkit" home-page = "https://www.riverbankcomputing.com/software/pyqt/" author = "Riverbank Computing Limited" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyQt6-6.5.0/qpy/QtCore/qpycore_pyqtboundsignal.cpp new/PyQt6-6.5.1/qpy/QtCore/qpycore_pyqtboundsignal.cpp --- old/PyQt6-6.5.0/qpy/QtCore/qpycore_pyqtboundsignal.cpp 2023-04-05 14:16:47.518521800 +0200 +++ new/PyQt6-6.5.1/qpy/QtCore/qpycore_pyqtboundsignal.cpp 2023-06-02 12:40:57.387069200 +0200 @@ -953,11 +953,13 @@ const Chimera *sig_arg = signal->parsed_arguments.at(a); const Chimera *slot_arg = slot->parsed_arguments.at(a); - // We simply compare the C++ names. We used to compare meta-types - // but this is unreliable as the signal's type may have been - // registered by Qt internally (and so given a new meta-type) after - // the slot was defined (and has the meta-type of PyQt_PyObject). - if (sig_arg->name() != slot_arg->name()) + // In the first instance we compare meta-types (which deals with + // typedefed types). However this can be unreliable as the + // signal's type may have been registered by Qt internally (and so + // given a new meta-type) after the slot was defined (and has the + // meta-type of PyQt_PyObject) so we also compare the C++ names. + if (sig_arg->metatype != slot_arg->metatype && + sig_arg->name() != slot_arg->name()) { slot = 0; break; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyQt6-6.5.0/sip/QtCore/QtCoremod.sip new/PyQt6-6.5.1/sip/QtCore/QtCoremod.sip --- old/PyQt6-6.5.0/sip/QtCore/QtCoremod.sip 2023-04-05 14:16:55.531872500 +0200 +++ new/PyQt6-6.5.1/sip/QtCore/QtCoremod.sip 2023-06-02 12:41:05.022572000 +0200 @@ -40,6 +40,7 @@ %Feature PyQt_Process %Feature PyQt_WebChannel %Feature PyQt_DTLS +%Feature PyQt_Permissions %Copying Copyright (c) 2023 Riverbank Computing Limited <i...@riverbankcomputing.com> @@ -74,8 +75,8 @@ const char *PYQT_VERSION_STR; %ModuleCode -static int PYQT_VERSION = 0x060500; -static const char *PYQT_VERSION_STR = "6.5.0"; +static int PYQT_VERSION = 0x060501; +static const char *PYQT_VERSION_STR = "6.5.1"; %End %Include qglobal.sip @@ -146,6 +147,7 @@ %Include qoperatingsystemversion.sip %Include qparallelanimationgroup.sip %Include qpauseanimation.sip +%Include qpermissions.sip %Include qpropertyanimation.sip %Include qpluginloader.sip %Include qpoint.sip diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyQt6-6.5.0/sip/QtCore/qcoreapplication.sip new/PyQt6-6.5.1/sip/QtCore/qcoreapplication.sip --- old/PyQt6-6.5.0/sip/QtCore/qcoreapplication.sip 2023-04-05 14:16:55.515679600 +0200 +++ new/PyQt6-6.5.1/sip/QtCore/qcoreapplication.sip 2023-06-02 12:41:05.008861800 +0200 @@ -132,6 +132,43 @@ // Make sure the QCoreApplication is destroyed. delete sipCpp; %End + +%If (Qt_6_5_0 -) +%If (PyQt_Permissions) + Qt::PermissionStatus checkPermission(const QPermission &permission); +%End +%End +%If (Qt_6_5_0 -) +%If (PyQt_Permissions) + void requestPermission(const QPermission &permission, SIP_PYCALLABLE handler /TypeHint="Callable[[QPermission], None]"/) /ReleaseGIL/; +%MethodCode + // Make sure the callable doesn't get garbage collected until it is invoked. + Py_INCREF(a1); + + Py_BEGIN_ALLOW_THREADS + + sipCpp->requestPermission(*a0, [a1](const QPermission &arg0) { + SIP_BLOCK_THREADS + + PyObject *res; + + res = sipCallMethod(NULL, a1, "N", new QPermission(arg0), sipType_QPermission, NULL); + + Py_DECREF(a1); + + if (!res) + pyqt6_err_print(); + else + Py_DECREF(res); + + SIP_UNBLOCK_THREADS + }); + + Py_END_ALLOW_THREADS +%End + +%End +%End }; void qAddPostRoutine(SIP_PYCALLABLE); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyQt6-6.5.0/sip/QtCore/qnamespace.sip new/PyQt6-6.5.1/sip/QtCore/qnamespace.sip --- old/PyQt6-6.5.0/sip/QtCore/qnamespace.sip 2023-04-05 14:16:55.559121800 +0200 +++ new/PyQt6-6.5.1/sip/QtCore/qnamespace.sip 2023-06-02 12:41:05.033792000 +0200 @@ -1552,6 +1552,16 @@ }; %End +%If (Qt_6_5_0 -) + + enum class PermissionStatus + { + Undetermined, + Granted, + Denied, + }; + +%End }; class QKeyCombination diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyQt6-6.5.0/sip/QtCore/qpermissions.sip new/PyQt6-6.5.1/sip/QtCore/qpermissions.sip --- old/PyQt6-6.5.0/sip/QtCore/qpermissions.sip 1970-01-01 01:00:00.000000000 +0100 +++ new/PyQt6-6.5.1/sip/QtCore/qpermissions.sip 2023-06-02 12:41:04.983158300 +0200 @@ -0,0 +1,292 @@ +// qpermissions.sip generated by MetaSIP +// +// This file is part of the QtCore Python extension module. +// +// Copyright (c) 2023 Riverbank Computing Limited <i...@riverbankcomputing.com> +// +// This file is part of PyQt6. +// +// This file may be used under the terms of the GNU General Public License +// version 3.0 as published by the Free Software Foundation and appearing in +// the file LICENSE included in the packaging of this file. Please review the +// following information to ensure the GNU General Public License version 3.0 +// requirements will be met: http://www.gnu.org/copyleft/gpl.html. +// +// If you do not wish to use this file under the terms of the GPL version 3.0 +// then you may purchase a commercial license. For more information contact +// i...@riverbankcomputing.com. +// +// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + + +%If (Qt_6_5_0 -) +%If (PyQt_Permissions) + +class QPermission /TypeHintIn="Union[QBluetoothPermission, QCalendarPermission, QCameraPermission, QContactsPermission, QLocationPermission, QMicrophonePermission]"/ +{ +%TypeHeaderCode +#include <qpermissions.h> +%End + +%ConvertToTypeCode +// Note that we don't allow sub-classes of the typed permissions. +const sipTypeDef *td = sipTypeFromPyTypeObject(Py_TYPE(sipPy)); + +if (!sipIsErr) + return (td == sipType_QBluetoothPermission || + td == sipType_QCalendarPermission || + td == sipType_QCameraPermission || + td == sipType_QContactsPermission || + td == sipType_QLocationPermission || + td == sipType_QMicrophonePermission); + +void *cpp = sipConvertToType(sipPy, td, sipTransferObj, SIP_NOT_NONE, NULL, sipIsErr); + +if (*sipIsErr) + return 0; + +if (td == sipType_QBluetoothPermission) + *sipCppPtr = new QPermission(*reinterpret_cast<QBluetoothPermission *>(cpp)); + +else if (td == sipType_QCalendarPermission) + *sipCppPtr = new QPermission(*reinterpret_cast<QCalendarPermission *>(cpp)); + +else if (td == sipType_QCameraPermission) + *sipCppPtr = new QPermission(*reinterpret_cast<QCameraPermission *>(cpp)); + +else if (td == sipType_QContactsPermission) + *sipCppPtr = new QPermission(*reinterpret_cast<QContactsPermission *>(cpp)); + +else if (td == sipType_QLocationPermission) + *sipCppPtr = new QPermission(*reinterpret_cast<QLocationPermission *>(cpp)); + +else if (td == sipType_QMicrophonePermission) + *sipCppPtr = new QPermission(*reinterpret_cast<QMicrophonePermission *>(cpp)); + +return sipGetState(sipTransferObj); +%End + +public: + QPermission(); + Qt::PermissionStatus status() const; + QMetaType type() const; + SIP_PYOBJECT value() const; +%MethodCode + const sipTypeDef *td = SIP_NULLPTR; + void *perm = SIP_NULLPTR; + QMetaType mt = sipCpp->type(); + + if (mt == QMetaType::fromType<QBluetoothPermission>()) + { + std::optional<QBluetoothPermission> opt_perm = sipCpp->value<QBluetoothPermission>(); + + if (opt_perm) + { + perm = new QBluetoothPermission(opt_perm.value()); + td = sipType_QBluetoothPermission; + } + } + else if (mt == QMetaType::fromType<QCalendarPermission>()) + { + std::optional<QCalendarPermission> opt_perm = sipCpp->value<QCalendarPermission>(); + + if (opt_perm) + { + perm = new QCalendarPermission(opt_perm.value()); + td = sipType_QCalendarPermission; + } + } + else if (mt == QMetaType::fromType<QCameraPermission>()) + { + std::optional<QCameraPermission> opt_perm = sipCpp->value<QCameraPermission>(); + + if (opt_perm) + { + perm = new QCameraPermission(opt_perm.value()); + td = sipType_QCameraPermission; + } + } + else if (mt == QMetaType::fromType<QContactsPermission>()) + { + std::optional<QContactsPermission> opt_perm = sipCpp->value<QContactsPermission>(); + + if (opt_perm) + { + perm = new QContactsPermission(opt_perm.value()); + td = sipType_QContactsPermission; + } + } + else if (mt == QMetaType::fromType<QLocationPermission>()) + { + std::optional<QLocationPermission> opt_perm = sipCpp->value<QLocationPermission>(); + + if (opt_perm) + { + perm = new QLocationPermission(opt_perm.value()); + td = sipType_QLocationPermission; + } + } + else if (mt == QMetaType::fromType<QMicrophonePermission>()) + { + std::optional<QMicrophonePermission> opt_perm = sipCpp->value<QMicrophonePermission>(); + + if (opt_perm) + { + perm = new QMicrophonePermission(opt_perm.value()); + td = sipType_QMicrophonePermission; + } + } + + if (perm) + { + sipRes = sipConvertFromNewType(perm, td, SIP_NULLPTR); + } + else + { + sipRes = Py_None; + Py_INCREF(sipRes); + } +%End +}; + +%End +%End +%If (Qt_6_5_0 -) +%If (PyQt_Permissions) + +class QLocationPermission +{ +%TypeHeaderCode +#include <qpermissions.h> +%End + +public: + QLocationPermission(); + QLocationPermission(const QLocationPermission &other); + ~QLocationPermission(); + + enum Accuracy + { + Approximate, + Precise, + }; + + void setAccuracy(QLocationPermission::Accuracy accuracy); + QLocationPermission::Accuracy accuracy() const; + + enum Availability + { + WhenInUse, + Always, + }; + + void setAvailability(QLocationPermission::Availability availability); + QLocationPermission::Availability availability() const; +}; + +%End +%End +%If (Qt_6_5_0 -) +%If (PyQt_Permissions) + +class QCalendarPermission +{ +%TypeHeaderCode +#include <qpermissions.h> +%End + +public: + QCalendarPermission(); + QCalendarPermission(const QCalendarPermission &other); + ~QCalendarPermission(); + + enum AccessMode + { + ReadOnly, + ReadWrite, + }; + + void setAccessMode(QCalendarPermission::AccessMode mode); + QCalendarPermission::AccessMode accessMode() const; +}; + +%End +%End +%If (Qt_6_5_0 -) +%If (PyQt_Permissions) + +class QContactsPermission +{ +%TypeHeaderCode +#include <qpermissions.h> +%End + +public: + QContactsPermission(); + QContactsPermission(const QContactsPermission &other); + ~QContactsPermission(); + + enum AccessMode + { + ReadOnly, + ReadWrite, + }; + + void setAccessMode(QContactsPermission::AccessMode mode); + QContactsPermission::AccessMode accessMode() const; +}; + +%End +%End +%If (Qt_6_5_0 -) +%If (PyQt_Permissions) + +class QCameraPermission +{ +%TypeHeaderCode +#include <qpermissions.h> +%End + +public: + QCameraPermission(); + QCameraPermission(const QCameraPermission &other); + ~QCameraPermission(); +}; + +%End +%End +%If (Qt_6_5_0 -) +%If (PyQt_Permissions) + +class QMicrophonePermission +{ +%TypeHeaderCode +#include <qpermissions.h> +%End + +public: + QMicrophonePermission(); + QMicrophonePermission(const QMicrophonePermission &other); + ~QMicrophonePermission(); +}; + +%End +%End +%If (Qt_6_5_0 -) +%If (PyQt_Permissions) + +class QBluetoothPermission +{ +%TypeHeaderCode +#include <qpermissions.h> +%End + +public: + QBluetoothPermission(); + QBluetoothPermission(const QBluetoothPermission &other); + ~QBluetoothPermission(); +}; + +%End +%End diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyQt6-6.5.0/uic/compile_ui.py new/PyQt6-6.5.1/uic/compile_ui.py --- old/PyQt6-6.5.0/uic/compile_ui.py 2023-04-05 14:16:47.672638700 +0200 +++ new/PyQt6-6.5.1/uic/compile_ui.py 2023-06-02 12:40:57.539929200 +0200 @@ -1,4 +1,4 @@ -# Copyright (c) 2020 Riverbank Computing Limited. +# Copyright (c) 2023 Riverbank Computing Limited. # Copyright (c) 2006 Thorsten Marek. # All right reserved. # @@ -60,7 +60,7 @@ sys.exit(app.exec())""" -def compileUiDir(dir, recurse=False, map=None, **compileUi_args): +def compileUiDir(dir, recurse=False, map=None, max_workers=0, **compileUi_args): """compileUiDir(dir, recurse=False, map=None, **compileUi_args) Creates Python modules from Qt Designer .ui files in a directory or @@ -76,14 +76,20 @@ created. The callable should return a tuple of the name of the directory in which the Python module will be created and the (possibly modified) name of the module. The default is None. + max_workers is the maximum number of worker processes to use. A value of 0 + means only the current process is used. A value of None means that the + number of processors on the machine is used. compileUi_args are any additional keyword arguments that are passed to the compileUi() function that is called to create each Python module. """ + from functools import partial import os - # Compile a single .ui file. - def compile_ui(ui_dir, ui_file): + jobs = [] + + # Add a compilation job. + def add_job(ui_dir, ui_file): # Ignore if it doesn't seem to be a .ui file. if ui_file.endswith('.ui'): py_dir = ui_dir @@ -94,30 +100,46 @@ if map is not None: py_dir, py_file = map(py_dir, py_file) - # Make sure the destination directory exists. - try: - os.makedirs(py_dir) - except: - pass - ui_path = os.path.join(ui_dir, ui_file) - py_path = os.path.join(py_dir, py_file) - py_file = open(py_path, 'w', encoding='utf-8') - - try: - compileUi(ui_path, py_file, **compileUi_args) - finally: - py_file.close() + jobs.append((ui_path, py_dir, py_file)) if recurse: for root, _, files in os.walk(dir): for ui in files: - compile_ui(root, ui) + add_job(root, ui) else: for ui in os.listdir(dir): if os.path.isfile(os.path.join(dir, ui)): - compile_ui(dir, ui) + add_job(dir, ui) + + if jobs and max_workers != 0: + from concurrent.futures import ProcessPoolExecutor + + with ProcessPoolExecutor(max_workers=max_workers) as executor: + executor.map(partial(_run_job, **compileUi_args), jobs) + else: + for job in jobs: + _run_job(job, **compileUi_args) + + +def _run_job(job, **compileUi_args): + """ Run a job to compile a single .ui file. """ + + import os + + ui_path, py_dir, py_file = job + + # Make sure the destination directory exists. + try: + os.makedirs(py_dir) + except: + pass + + py_path = os.path.join(py_dir, py_file) + + with open(py_path, 'w', encoding='utf-8') as py_f: + compileUi(ui_path, py_f, **compileUi_args) def compileUi(uifile, pyfile, execute=False, indent=4): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyQt6-6.5.0/uic/pyuic.py new/PyQt6-6.5.1/uic/pyuic.py --- old/PyQt6-6.5.0/uic/pyuic.py 2023-04-05 14:16:47.664907200 +0200 +++ new/PyQt6-6.5.1/uic/pyuic.py 2023-06-02 12:40:57.532554100 +0200 @@ -16,6 +16,7 @@ # WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +import os import sys @@ -52,7 +53,11 @@ parser.add_argument('-i', '--indent', dest='indent', action='store', type=int, default=4, metavar="N", help="set indent width to N spaces, tab if N is 0 [default: 4]") - parser.add_argument('ui', help="the .ui file created by Qt Designer") + parser.add_argument('-w', '--max-workers', dest='max_workers', + action='store', type=int, default=0, metavar="N", + help="use a maximum of N worker processes when converting a directory [default: 0]") + parser.add_argument('ui', + help="the .ui file created by Qt Designer or a directory containing .ui files") args = parser.parse_args() @@ -64,9 +69,13 @@ try: if args.preview: - exit_status = preview(args.ui) + if os.path.isfile(args.ui): + exit_status = preview(args.ui) + else: + raise UIFileException(args.ui, "must be a file") else: - generate(args.ui, args.output, args.indent, args.execute) + generate(args.ui, args.output, args.indent, args.execute, + args.max_workers) exit_status = 0 except IOError as e: @@ -109,24 +118,44 @@ logger.setLevel(logging.DEBUG) -def generate(ui_file, output, indent, execute): +def generate(ui_file, output, indent, execute, max_workers): """ Generate the Python code. """ - from .compile_ui import compileUi + from .exceptions import UIFileException - if output == '-': - import io + if os.path.isdir(ui_file): + if output == '-': + map = None + elif os.path.isdir(output) or not os.path.exists(output): + map = lambda d, f: (output, f) + else: + raise UIFileException(output, + f"must be a directory as {ui_file} is a directory") + + from .compile_ui import compileUiDir + + compileUiDir(ui_file, recurse=False, map=map, max_workers=max_workers, + indent=indent, execute=execute) - pyfile = io.TextIOWrapper(sys.stdout.buffer, encoding='utf8') - needs_close = False + elif os.path.isdir(output): + raise UIFileException(output, + f"cannot be a directory unless {ui_file} is a directory") else: - pyfile = open(output, 'wt', encoding='utf8') - needs_close = True + from .compile_ui import compileUi + + if output == '-': + import io + + pyfile = io.TextIOWrapper(sys.stdout.buffer, encoding='utf8') + needs_close = False + else: + pyfile = open(output, 'wt', encoding='utf8') + needs_close = True - compileUi(ui_file, pyfile, execute, indent) + compileUi(ui_file, pyfile, execute, indent) - if needs_close: - pyfile.close() + if needs_close: + pyfile.close() def preview(ui_file):