aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2022-09-03 03:01:04 +0000
committerQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2022-09-03 03:01:04 +0000
commit58cd9330b8a41cbf39d17ba38ad98e0483ee999c (patch)
treee8d06bba7afde437eab9eac6f9a05397a2846678
parentc73824dd388c7932e45c40d29bf60822ea6f3528 (diff)
parenta0438c482354ca909efc1fd94e8810c2d5b185d3 (diff)
Merge branch 6.3 into wip/6.3_pypy
-rw-r--r--doc/changelogs/changes-6.3.218
-rw-r--r--sources/pyside6/PySide6/QtCore/typesystem_core_common.xml17
-rw-r--r--sources/pyside6/PySide6/glue/qtcore.cpp8
-rw-r--r--sources/pyside6/PySide6/support/deprecated.py30
-rw-r--r--sources/pyside6/tests/pysidetest/qvariant_test.py15
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetatype.cpp12
-rw-r--r--sources/shiboken6/generator/shiboken/cppgenerator.cpp9
-rw-r--r--sources/shiboken6/libshiboken/basewrapper_p.h2
-rw-r--r--sources/shiboken6/libshiboken/sbkenum.cpp13
-rw-r--r--sources/shiboken6/libshiboken/sbkfeature_base.cpp12
-rw-r--r--sources/shiboken6/shibokenmodule/Shiboken.pyi16
-rw-r--r--sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/parser.py12
-rw-r--r--sources/shiboken6/shibokenmodule/typesystem_shiboken.xml21
-rw-r--r--tools/snippets_translate/converter.py101
-rw-r--r--tools/snippets_translate/handlers.py157
-rw-r--r--tools/snippets_translate/tests/test_converter.py4
16 files changed, 268 insertions, 179 deletions
diff --git a/doc/changelogs/changes-6.3.2 b/doc/changelogs/changes-6.3.2
index 18573e329..c2a40a854 100644
--- a/doc/changelogs/changes-6.3.2
+++ b/doc/changelogs/changes-6.3.2
@@ -17,6 +17,9 @@ information about a particular change.
* PySide6 *
****************************************************************************
+ - [PYSIDE-841] New examples have been added (SpinBoxDelegate,
+ Bluetooth heart rate).
+ - [PYSIDE-1312] QFormLayout::takeRow() has been added.
- [PYSIDE-1735] The duplication of enum values into the enclosing scope,
allowing to write Qt.AlignLeft instead of Qt.Alignment.AlignLeft,
is now implemented differently and no longer advertized in PYI
@@ -25,13 +28,19 @@ information about a particular change.
as possible. It is again allowed to use Qt.Alignment()
instead of Qt.AlignmentFlag(0), and a default of 0 is
always allowed but also not advertized.
+ Qt.Modifiers/Qt.KeyboardModifierflags can also still be used
+ interchangeably.
- [PYSIDE-1735] Most former IntEnum/IntFlag are replaced by pure Enum/Flag
classes in a generally compatible way to other implementations.
+ - [PYSIDE-1735] PyEnum handles QKeyCombination correctly with "|" or (deprecated)
+ "+" operators, without falling back to using IntEnum.
- [PYSIDE-1735] Python Enums use the newest implementation for Python (3.10)
for compatibility and speed.
- [PYSIDE-1735] A weird build problem on macOS and other platforms was fixed.
- [PYSide-1735] The cleanup calls by were sped up by using PyName for
staticMetaObject.
+ - [PYSIDE-1984] The code snippets in the modelview tutorial have been
+ improved.
- [PYSIDE-1930] Returning enums from QAbstractItemModel.flags() and
QAbstractItemModel.data() has been fixed.
- [PYSIDE-1934] The type hinting for the return value of
@@ -43,8 +52,15 @@ information about a particular change.
has been fixed.
Also, the error message about using the wrong signal overload
has been improved.
+ - [PYSIDE-2030] The converter tools for Python code snippets for the
+ documentation has been improved.
- [PYSIDE-2019] Crashes related to QtDataVisualization'QValue3DAxisFormatter
- have been fixed
+ have been fixed.
+ - [PYSIDE-2034] The static functions of QMessageBox have been fixed to not
+ block threads.
+ - [PYSIDE-2039] A regression affecting setting override cursors without
+ context manager has been fixed.
+
****************************************************************************
* Shiboken6 *
diff --git a/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml b/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml
index 2942e3d38..6f6484cbf 100644
--- a/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml
+++ b/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml
@@ -328,7 +328,20 @@
</conversion-rule>
</primitive-type>
- <value-type name="QKeyCombination"/>
+ <value-type name="QKeyCombination">
+ <!-- The following do-nothing function is needed for coercion of constructs like
+ QKeyCombination(Qt.CTRL | Qt.Key_B)
+ -->
+ <add-function signature="QKeyCombination(QKeyCombination)">
+ <inject-code class="target" position="beginning" file="../glue/qtcore.cpp"
+ snippet="keycombination-from-keycombination"/>
+ </add-function>
+ <!-- This is just a copy of KeyModifier to handle Modifier the same -->
+ <add-function signature="QKeyCombination(Qt::Modifier @modifiers@, Qt::Key @key@)">
+ <inject-code class="target" position="beginning" file="../glue/qtcore.cpp"
+ snippet="keycombination-from-modifier"/>
+ </add-function>
+ </value-type>
<value-type name="QMetaType">
<enum-type name="Type" python-type="IntEnum"/>
@@ -578,7 +591,7 @@
<enum-type name="LayoutDirection"/>
<enum-type name="MaskMode"/>
<enum-type name="MatchFlag" flags="MatchFlags"/>
- <enum-type name="Modifier"/>
+ <enum-type name="Modifier" python-type="Flag"/>
<enum-type name="MouseButton" flags="MouseButtons"/>
<enum-type name="MouseEventFlag" flags="MouseEventFlags" since="5.3"/>
<enum-type name="MouseEventSource" since="5.3"/>
diff --git a/sources/pyside6/PySide6/glue/qtcore.cpp b/sources/pyside6/PySide6/glue/qtcore.cpp
index b0af6c354..a1ab063af 100644
--- a/sources/pyside6/PySide6/glue/qtcore.cpp
+++ b/sources/pyside6/PySide6/glue/qtcore.cpp
@@ -1628,3 +1628,11 @@ if (Shiboken::Enum::check(%PYARG_0)) {
pythonToCpp(pyResult, &cppResult);
}
// @snippet qabstractitemmodel_data
+
+// @snippet keycombination-from-keycombination
+cptr = new ::%TYPE(%1);
+// @snippet keycombination-from-keycombination
+
+// @snippet keycombination-from-modifier
+cptr = new ::%TYPE(%1, %2);
+// @snippet keycombination-from-modifier
diff --git a/sources/pyside6/PySide6/support/deprecated.py b/sources/pyside6/PySide6/support/deprecated.py
index cd620eb05..a0df14715 100644
--- a/sources/pyside6/PySide6/support/deprecated.py
+++ b/sources/pyside6/PySide6/support/deprecated.py
@@ -50,6 +50,9 @@ Functions that are to be called for
Note that this fixing code is run after all initializations, but before the
import is finished. But that is no problem since the module is passed in.
+
+PYSIDE-1735: This is also used now for missing other functions (overwriting __or__
+ in Qt.(Keyboard)Modifier).
"""
import warnings
@@ -75,4 +78,31 @@ def _unused_fix_for_QtGui(QtGui):
if name.startswith("QMatrix") and "data" in cls.__dict__:
cls.constData = constData
+# PYSIDE-1735: Fix for a special enum function
+def fix_for_QtCore(QtCore):
+ from enum import Flag
+ Qt = QtCore.Qt
+ flag_or = Flag.__or__
+
+ def func_or(self, other):
+ if isinstance(self, Flag) and isinstance(other, Flag):
+ # this is normal or-ing flags together
+ return Qt.KeyboardModifier(self.value | other.value)
+ return QtCore.QKeyCombination(self, other)
+
+ def func_add(self, other):
+ warnings.warn(dedent(f"""
+ The "+" operator is deprecated in Qt For Python 6.0 .
+ Please use "|" instead."""), PySideDeprecationWarningRemovedInQt6, stacklevel=2)
+ return func_or(self, other)
+
+ Qt.KeyboardModifier.__or__ = func_or
+ Qt.KeyboardModifier.__ror__ = func_or
+ Qt.Modifier.__or__ = func_or
+ Qt.Modifier.__ror__ = func_or
+ Qt.KeyboardModifier.__add__ = func_add
+ Qt.KeyboardModifier.__radd__ = func_add
+ Qt.Modifier.__add__ = func_add
+ Qt.Modifier.__radd__ = func_add
+
# eof
diff --git a/sources/pyside6/tests/pysidetest/qvariant_test.py b/sources/pyside6/tests/pysidetest/qvariant_test.py
index c3d2614df..e6977082e 100644
--- a/sources/pyside6/tests/pysidetest/qvariant_test.py
+++ b/sources/pyside6/tests/pysidetest/qvariant_test.py
@@ -36,8 +36,8 @@ from init_paths import init_test_paths
init_test_paths(True)
from testbinding import TestObject
-from PySide6.QtCore import Qt
-from PySide6.QtGui import QKeySequence
+from PySide6.QtCore import Qt, QKeyCombination
+from PySide6.QtGui import QKeySequence, QAction
from helper.usesqapplication import UsesQApplication
@@ -49,6 +49,17 @@ class QVariantTest(UsesQApplication):
ks = QKeySequence(Qt.ShiftModifier, Qt.ControlModifier, Qt.Key_P, Qt.Key_R)
self.assertEqual(TestObject.checkType(ks), 4107)
+ # PYSIDE-1735: Test the new way to address QKeyCombination after moving IntEnum to Enum
+ @unittest.skipUnless(sys.pyside63_option_python_enum, "only implemented for new enums")
+ def testQKeySequenceMoreVariations(self):
+ QAction().setShortcut(Qt.CTRL | Qt.Key_B)
+ QAction().setShortcut(Qt.CTRL | Qt.ALT | Qt.Key_B)
+ QAction().setShortcut(Qt.CTRL | Qt.AltModifier | Qt.Key_B)
+ QAction().setShortcut(QKeySequence(QKeyCombination(Qt.CTRL | Qt.Key_B)))
+ QKeySequence(Qt.CTRL | Qt.Key_Q)
+ # Issues a warning but works as well
+ QKeySequence(Qt.CTRL + Qt.Key_Q)
+
if __name__ == '__main__':
unittest.main()
diff --git a/sources/shiboken6/ApiExtractor/abstractmetatype.cpp b/sources/shiboken6/ApiExtractor/abstractmetatype.cpp
index d47b25ef2..b5fa4100c 100644
--- a/sources/shiboken6/ApiExtractor/abstractmetatype.cpp
+++ b/sources/shiboken6/ApiExtractor/abstractmetatype.cpp
@@ -668,16 +668,8 @@ QString AbstractMetaTypeData::formatPythonSignature() const
result += TypeInfo::indirectionKeyword(i);
// If it is a flags type, we replace it with the full name:
// "PySide6.QtCore.Qt.ItemFlags" instead of "PySide6.QtCore.QFlags<Qt.ItemFlag>"
- if (m_typeEntry->isFlags()) {
- // PYSIDE-1735: We need to provide both the flags type and the original enum type
- // as a choice at runtime.
- auto flagsTypeEntry = static_cast<const FlagsTypeEntry *>(m_typeEntry);
- auto enumTypeEntry = flagsTypeEntry->originator();
- result = m_typeEntry->targetLangPackage() + u".^^"_s
- + flagsTypeEntry->targetLangName() + u"^^"_s
- + enumTypeEntry->targetLangName() + u"^^"_s;
- }
-
+ if (m_typeEntry->isFlags())
+ result = m_typeEntry->qualifiedTargetLangName();
result.replace(u"::"_s, u"."_s);
return result;
}
diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.cpp b/sources/shiboken6/generator/shiboken/cppgenerator.cpp
index b165b6076..61d5de7cc 100644
--- a/sources/shiboken6/generator/shiboken/cppgenerator.cpp
+++ b/sources/shiboken6/generator/shiboken/cppgenerator.cpp
@@ -416,6 +416,7 @@ static QSet<QString> useIntSet()
/* IntEnum */ u"PySide6.QtCore.Qt.GestureType"_s,
/* IntEnum */ u"PySide6.QtCore.Qt.ItemDataRole"_s,
/* IntEnum */ u"PySide6.QtCore.Qt.Key"_s,
+ /* Flag */ u"PySide6.QtCore.Qt.Modifier"_s,
// note: "Qt::TextFlag" is set as IntFlag without flags
/* IntFlag */ u"PySide6.QtCore.Qt.TextFlag"_s,
/* IntFlag */ u"PySide6.QtCore.Qt.WindowType"_s,
@@ -474,12 +475,8 @@ static QString BuildEnumFlagInfo(const AbstractMetaEnum &cppEnum)
if (decision != TypeSystem::PythonEnumType::Unspecified) {
_int = decision == TypeSystem::PythonEnumType::IntEnum ||
decision == TypeSystem::PythonEnumType::IntFlag;
- if (!flags && decision == TypeSystem::PythonEnumType::IntFlag) {
- qWarning() << "\nnote: " << enumType->name() << "is set as IntFlag without flags\n";
- _flag = true;
- }
- if (flags && decision == TypeSystem::PythonEnumType::IntEnum)
- qWarning() << "\n*** The expression " << enumType->name() << "should be a flag!\n";
+ _flag = decision == TypeSystem::PythonEnumType::Flag ||
+ decision == TypeSystem::PythonEnumType::IntFlag;
}
result += _flag ? (_int ? u":IntFlag"_s : u":Flag"_s)
: (_int ? u":IntEnum"_s : u":Enum"_s);
diff --git a/sources/shiboken6/libshiboken/basewrapper_p.h b/sources/shiboken6/libshiboken/basewrapper_p.h
index 3e9ff17b0..8d7e0b7a9 100644
--- a/sources/shiboken6/libshiboken/basewrapper_p.h
+++ b/sources/shiboken6/libshiboken/basewrapper_p.h
@@ -151,7 +151,7 @@ struct SbkObjectTypePrivate
const char **propertyStrings;
const char **enumFlagInfo;
PyObject *enumFlagsDict;
- PyObject *enumIntSet;
+ PyObject *enumTypeDict;
};
diff --git a/sources/shiboken6/libshiboken/sbkenum.cpp b/sources/shiboken6/libshiboken/sbkenum.cpp
index 58dafa497..3de052236 100644
--- a/sources/shiboken6/libshiboken/sbkenum.cpp
+++ b/sources/shiboken6/libshiboken/sbkenum.cpp
@@ -430,6 +430,7 @@ static PyMethodDef SbkEnumObject_Methods[] = {
{nullptr, nullptr, 0, nullptr} // Sentinel
};
+static PyObject *PyEnumModule{};
static PyObject *PyEnumMeta{};
static PyObject *PyEnum{};
static PyObject *PyIntEnum{};
@@ -444,6 +445,7 @@ PyTypeObject *getPyEnumMeta()
static auto *mod = PyImport_ImportModule("enum");
if (mod) {
+ PyEnumModule = mod;
PyEnumMeta = PyObject_GetAttrString(mod, "EnumMeta");
if (PyEnumMeta && PyType_Check(PyEnumMeta))
PyEnum = PyObject_GetAttrString(mod, "Enum");
@@ -1030,16 +1032,14 @@ PyTypeObject *morphLastEnumToPython()
}
auto *scopeOrModule = lec.scopeOrModule;
- bool useInt = true;
-
+ static PyObject *enumName = String::createStaticString("IntEnum");
if (PyType_Check(scopeOrModule)) {
// For global objects, we have no good solution, yet where to put the int info.
auto type = reinterpret_cast<PyTypeObject *>(scopeOrModule);
auto *sotp = PepType_SOTP(type);
if (!sotp->enumFlagsDict)
initEnumFlagsDict(type);
- if (!PySet_Contains(sotp->enumIntSet, String::fromCString(lec.name)))
- useInt = false;
+ enumName = PyDict_GetItem(sotp->enumTypeDict, String::fromCString(lec.name));
}
PyObject *key, *value;
@@ -1048,9 +1048,10 @@ PyTypeObject *morphLastEnumToPython()
if (!values)
return nullptr;
+ AutoDecRef PyEnumType(PyObject_GetAttr(PyEnumModule, enumName));
+ assert(PyEnumType.object());
+
// Walk the values dict and create a Python enum type.
- auto *PyEnumType = lec.flagsType ? (useInt ? PyIntFlag : PyFlag)
- : (useInt ? PyIntEnum : PyEnum);
AutoDecRef name(PyUnicode_FromString(lec.name));
AutoDecRef args(PyList_New(0));
auto *pyName = name.object();
diff --git a/sources/shiboken6/libshiboken/sbkfeature_base.cpp b/sources/shiboken6/libshiboken/sbkfeature_base.cpp
index 096568f4e..797780b48 100644
--- a/sources/shiboken6/libshiboken/sbkfeature_base.cpp
+++ b/sources/shiboken6/libshiboken/sbkfeature_base.cpp
@@ -211,14 +211,14 @@ static bool currentOpcode_Is_CallMethNoArgs()
void initEnumFlagsDict(PyTypeObject *type)
{
- // We create a dict for all flag enums that holds the original C++ name.
- // We create a set for all int enums or flags.
+ // We create a dict for all flag enums that holds the original C++ name
+ // and a dict that gives every enum/flag type name.
static PyObject *const split = Shiboken::String::createStaticString("split");
static PyObject *const colon = Shiboken::String::createStaticString(":");
auto sotp = PepType_SOTP(type);
auto **enumFlagInfo = sotp->enumFlagInfo;
auto *dict = PyDict_New();
- auto *set = PySet_New(nullptr);
+ auto *typeDict = PyDict_New();
for (; *enumFlagInfo; ++enumFlagInfo) {
AutoDecRef line(PyUnicode_FromString(*enumFlagInfo));
AutoDecRef parts(PyObject_CallMethodObjArgs(line, split, colon, nullptr));
@@ -229,12 +229,10 @@ void initEnumFlagsDict(PyTypeObject *type)
PyDict_SetItem(dict, key, value);
}
auto *typeName = PyList_GetItem(parts, 1);
- bool intFlag = strncmp(String::toCString(typeName), "Int", 3) == 0;
- if (intFlag)
- PySet_Add(set, name);
+ PyDict_SetItem(typeDict, name, typeName);
}
sotp->enumFlagsDict = dict;
- sotp->enumIntSet = set;
+ sotp->enumTypeDict = typeDict;
}
static PyObject *replaceNoArgWithZero(PyObject *callable)
diff --git a/sources/shiboken6/shibokenmodule/Shiboken.pyi b/sources/shiboken6/shibokenmodule/Shiboken.pyi
index e312f6912..c0bfe3907 100644
--- a/sources/shiboken6/shibokenmodule/Shiboken.pyi
+++ b/sources/shiboken6/shibokenmodule/Shiboken.pyi
@@ -59,15 +59,15 @@ class VoidPtr(object): ...
def _unpickle_enum(arg__1: object, arg__2: object) -> object: ...
-def createdByPython(arg__1: object) -> bool: ...
-def delete(arg__1: object) -> None: ...
-def dump(arg__1: object) -> object: ...
-def getAllValidWrappers() -> object: ...
-def getCppPointer(arg__1: object) -> object: ...
-def invalidate(arg__1: object) -> None: ...
+def createdByPython(arg__1: Shiboken.Object) -> bool: ...
+def delete(arg__1: Shiboken.Object) -> None: ...
+def dump(arg__1: object) -> str: ...
+def getAllValidWrappers() -> list[Shiboken.Object]: ...
+def getCppPointer(arg__1: Shiboken.Object) -> tuple[int, ...]: ...
+def invalidate(arg__1: Shiboken.Object) -> None: ...
def isValid(arg__1: object) -> bool: ...
-def ownedByPython(arg__1: object) -> bool: ...
-def wrapInstance(arg__1: int, arg__2: type) -> object: ...
+def ownedByPython(arg__1: Shiboken.Object) -> bool: ...
+def wrapInstance(arg__1: int, arg__2: type) -> Shiboken.Object: ...
# eof
diff --git a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/parser.py b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/parser.py
index 1d4dadc55..c877f1ee4 100644
--- a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/parser.py
+++ b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/parser.py
@@ -434,18 +434,6 @@ def handle_retvar(obj):
def calculate_props(line):
- # PYSIDE-1735: QFlag is now divided into fields for future Python Enums, like
- # "PySide.QtCore.^^Qt.ItemFlags^^Qt.ItemFlag^^"
- # Resolve that until Enum is finally settled.
- while "^^" in line:
- parts = line.split("^^", 3)
- selected = EnumSelect.SELECTION
- line = parts[0] + parts[selected.value] + parts[3]
- if selected is EnumSelect.NEW:
- _old, _new = EnumSelect.OLD.value, EnumSelect.NEW.value
- line = re.sub(rf"\b{parts[_old]}\b", parts[_new], line)
- type_map[parts[_old]] = parts[_new]
-
parsed = SimpleNamespace(**_parse_line(line.strip()))
arglist = parsed.arglist
annotations = {}
diff --git a/sources/shiboken6/shibokenmodule/typesystem_shiboken.xml b/sources/shiboken6/shibokenmodule/typesystem_shiboken.xml
index 5bc361644..34079eb44 100644
--- a/sources/shiboken6/shibokenmodule/typesystem_shiboken.xml
+++ b/sources/shiboken6/shibokenmodule/typesystem_shiboken.xml
@@ -29,7 +29,7 @@
</inject-code>
</add-function>
- <add-function signature="getCppPointer(PyObject*)" return-type="PyObject*">
+ <add-function signature="getCppPointer(PyObject*)" return-type="PySequence*">
<inject-code>
if (Shiboken::Object::checkType(%1)) {
std::vector&lt;void*> ptrs = Shiboken::Object::cppPointers(reinterpret_cast&lt;SbkObject *&gt;(%1));
@@ -74,7 +74,7 @@
</inject-code>
</add-function>
- <add-function signature="dump(PyObject*)" return-type="PyObject*">
+ <add-function signature="dump(PyObject*)" return-type="const char *">
<inject-code>
if (!Shiboken::Object::checkType(%1)) {
%PYARG_0 = Shiboken::String::fromCString("Ordinary Python type.");
@@ -85,19 +85,18 @@
</inject-code>
</add-function>
- <add-function signature="getAllValidWrappers(void)" return-type="PyObject*">
+ <add-function signature="getAllValidWrappers(void)" return-type="PySequence*">
<inject-code>
- std::set&lt;PyObject*&gt; setAll = Shiboken::BindingManager::instance().getAllPyObjects();
+ const auto setAll = Shiboken::BindingManager::instance().getAllPyObjects();
PyObject* listAll = PyList_New(0);
- if (listAll == NULL)
- return NULL;
+ if (listAll == nullptr)
+ return nullptr;
- const std::set&lt;PyObject*&gt;::iterator end = setAll.end();
- for (std::set&lt;PyObject*&gt;::iterator iter = setAll.begin(); iter != end; ++iter) {
- if (*iter != NULL) {
- if (PyList_Append(listAll, *iter) != 0) {
+ for (auto *o : setAll) {
+ if (o != nullptr) {
+ if (PyList_Append(listAll, o) != 0) {
Py_DECREF(listAll);
- return NULL;
+ return nullptr;
}
}
}
diff --git a/tools/snippets_translate/converter.py b/tools/snippets_translate/converter.py
index 77250a857..92c89bdf6 100644
--- a/tools/snippets_translate/converter.py
+++ b/tools/snippets_translate/converter.py
@@ -50,6 +50,31 @@ from handlers import (handle_array_declarations, handle_casts, handle_class,
from parse_utils import dstrip, get_indent, remove_ref
+VOID_METHOD_PATTERN = re.compile(r"^ *void *[\w\_]+(::)?[\w\d\_]+\(")
+QT_QUALIFIER_PATTERN = re.compile(r"Q[\w]+::")
+TERNARY_OPERATOR_PATTERN = re.compile(r"^.* \? .+ : .+$")
+COUT_PATTERN = re.compile("^ *(std::)?cout")
+FOR_PATTERN = re.compile(r"^ *for *\(")
+FOREACH_PATTERN = re.compile(r"^ *foreach *\(")
+ELSE_PATTERN = re.compile(r"^ *}? *else *{?")
+ELSE_REPLACEMENT_PATTERN = re.compile(r"}? *else *{?")
+CLASS_PATTERN = re.compile(r"^ *class ")
+STRUCT_PATTERN = re.compile(r"^ *struct ")
+DELETE_PATTERN = re.compile(r"^ *delete ")
+PUBLIC_PATTERN = re.compile(r"^public:$")
+PRIVATE_PATTERN = re.compile(r"^private:$")
+VAR1_PATTERN = re.compile(r"^[a-zA-Z0-9]+(<.*?>)? [\w\*\&]+(\(.*?\))? ?(?!.*=|:).*$")
+VAR2_PATTERN = re.compile(r"^[a-zA-Z0-9]+(<.*?>)? [\w]+::[\w\*\&]+\(.*\)$")
+VAR3_PATTERN = re.compile(r"^[a-zA-Z0-9]+(<.*?>)? [\w\*]+ *= *[\w\.\"\']*(\(.*?\))?")
+VAR4_PATTERN = re.compile(r"\w+ = [A-Z]{1}\w+")
+CONSTRUCTOR_PATTERN = re.compile(r"^ *\w+::\w+\(.*?\)")
+ARRAY_VAR_PATTERN = re.compile(r"^[a-zA-Z0-9]+(<.*?>)? [\w\*]+\[?\]? * =? *\{")
+RETURN_TYPE_PATTERN = re.compile(r"^[a-zA-Z0-9]+(<.*?>)? [\w]+::[\w\*\&]+\(.*\)$")
+FUNCTION_PATTERN = re.compile(r"^[a-zA-Z0-9]+(<.*?>)? [\w\*\&]+\(.*\)$")
+ITERATOR_PATTERN = re.compile(r"(std::)?[\w]+<[\w]+>::(const_)?iterator")
+SCOPE_PATTERN = re.compile(r"[\w]+::")
+
+
def snippet_translate(x):
## Cases which are not C++
@@ -64,15 +89,16 @@ def snippet_translate(x):
x = x[:-1]
# Remove lines with only '{' or '}'
- if x.strip() == "{" or x.strip() == "}":
+ xs = x.strip()
+ if xs == "{" or xs == "}":
return ""
# Skip lines with the snippet related identifier '//!'
- if x.strip().startswith("//!"):
+ if xs.startswith("//!"):
return x
# handle lines with only comments using '//'
- if x.lstrip().startswith("//"):
+ if xs.startswith("//"):
x = x.replace("//", "#", 1)
return x
@@ -144,11 +170,11 @@ def snippet_translate(x):
x = handle_keywords(x, "throw", "raise")
# handle 'void Class::method(...)' and 'void method(...)'
- if re.search(r"^ *void *[\w\_]+(::)?[\w\d\_]+\(", x):
+ if VOID_METHOD_PATTERN.search(x):
x = handle_void_functions(x)
# 'Q*::' -> 'Q*.'
- if re.search(r"Q[\w]+::", x):
+ if QT_QUALIFIER_PATTERN.search(x):
x = x.replace("::", ".")
# handle 'nullptr'
@@ -156,77 +182,79 @@ def snippet_translate(x):
x = x.replace("nullptr", "None")
## Special Cases Rules
-
+ xs = x.strip()
# Special case for 'main'
- if x.strip().startswith("int main("):
+ if xs.startswith("int main("):
return f'{get_indent(x)}if __name__ == "__main__":'
- if x.strip().startswith("QApplication app(argc, argv)"):
+ if xs.startswith("QApplication app(argc, argv)"):
return f"{get_indent(x)}app = QApplication([])"
# Special case for 'return app.exec()'
- if x.strip().startswith("return app.exec"):
+ if xs.startswith("return app.exec"):
return x.replace("return app.exec()", "sys.exit(app.exec())")
# Handle includes -> import
- if x.strip().startswith("#include"):
+ if xs.startswith("#include"):
x = handle_include(x)
return dstrip(x)
- if x.strip().startswith("emit "):
+ if xs.startswith("emit "):
x = handle_emit(x)
return dstrip(x)
# *_cast
if "_cast<" in x:
x = handle_casts(x)
+ xs = x.strip()
# Handle Qt classes that needs to be removed
x = handle_useless_qt_classes(x)
# Handling ternary operator
- if re.search(r"^.* \? .+ : .+$", x.strip()):
+ if TERNARY_OPERATOR_PATTERN.search(xs):
x = x.replace(" ? ", " if ")
x = x.replace(" : ", " else ")
+ xs = x.strip()
# Handle 'while', 'if', and 'else if'
# line might end in ')' or ") {"
- if x.strip().startswith(("while", "if", "else if", "} else if")):
+ if xs.startswith(("while", "if", "else if", "} else if")):
x = handle_conditions(x)
return dstrip(x)
- elif re.search("^ *}? *else *{?", x):
- x = re.sub(r"}? *else *{?", "else:", x)
+ elif ELSE_PATTERN.search(x):
+ x = ELSE_REPLACEMENT_PATTERN.sub("else:", x)
return dstrip(x)
# 'cout' and 'endl'
- if re.search("^ *(std::)?cout", x) or ("endl" in x) or x.lstrip().startswith("qDebug()"):
+ if COUT_PATTERN.search(x) or ("endl" in x) or xs.startswith("qDebug()"):
x = handle_cout_endl(x)
return dstrip(x)
# 'for' loops
- if re.search(r"^ *for *\(", x.strip()):
+ if FOR_PATTERN.search(xs):
return dstrip(handle_for(x))
# 'foreach' loops
- if re.search(r"^ *foreach *\(", x.strip()):
+ if FOREACH_PATTERN.search(xs):
return dstrip(handle_foreach(x))
# 'class' and 'structs'
- if re.search(r"^ *class ", x) or re.search(r"^ *struct ", x):
+ if CLASS_PATTERN.search(x) or STRUCT_PATTERN.search(x):
if "struct " in x:
x = x.replace("struct ", "class ")
return handle_class(x)
# 'delete'
- if re.search(r"^ *delete ", x):
+ if DELETE_PATTERN.search(x):
return x.replace("delete", "del")
# 'public:'
- if re.search(r"^public:$", x.strip()):
+ if PUBLIC_PATTERN.search(xs):
return x.replace("public:", "# public")
# 'private:'
- if re.search(r"^private:$", x.strip()):
+ if PRIVATE_PATTERN.search(xs):
return x.replace("private:", "# private")
# For expressions like: `Type var`
@@ -242,9 +270,9 @@ def snippet_translate(x):
# At the end we skip methods with the form:
# QStringView Message::body()
# to threat them as methods.
- if (re.search(r"^[a-zA-Z0-9]+(<.*?>)? [\w\*\&]+(\(.*?\))? ?(?!.*=|:).*$", x.strip())
- and x.strip().split()[0] not in ("def", "return", "and", "or")
- and not re.search(r"^[a-zA-Z0-9]+(<.*?>)? [\w]+::[\w\*\&]+\(.*\)$", x.strip())
+ if (VAR1_PATTERN.search(xs)
+ and xs.split()[0] not in ("def", "return", "and", "or")
+ and not VAR2_PATTERN.search(xs)
and ("{" not in x and "}" not in x)):
# FIXME: this 'if' is a hack for a function declaration with this form:
@@ -261,7 +289,7 @@ def snippet_translate(x):
# QSome thing = b(...)
# float v = 0.1
# QSome *thing = ...
- if (re.search(r"^[a-zA-Z0-9]+(<.*?>)? [\w\*]+ *= *[\w\.\"\']*(\(.*?\))?", x.strip())
+ if (VAR3_PATTERN.search(xs)
and ("{" not in x and "}" not in x)):
left, right = x.split("=", 1)
var_name = " ".join(left.strip().split()[1:])
@@ -272,23 +300,23 @@ def snippet_translate(x):
# layout = QVBoxLayout
# so we need to add '()' at the end if it's just a word
# with only alpha numeric content
- if re.search(r"\w+ = [A-Z]{1}\w+", x.strip()) and not x.strip().endswith(")"):
+ if VAR4_PATTERN.search(xs) and not xs.endswith(")"):
x = f"{x.rstrip()}()"
return dstrip(x)
# For constructors, that we now the shape is:
# ClassName::ClassName(...)
- if re.search(r"^ *\w+::\w+\(.*?\)", x.strip()):
+ if CONSTRUCTOR_PATTERN.search(xs):
x = handle_constructors(x)
return dstrip(x)
# For base object constructor:
# : QWidget(parent)
if (
- x.strip().startswith(": ")
+ xs.startswith(": ")
and ("<<" not in x)
and ("::" not in x)
- and not x.strip().endswith(";")
+ and not xs.endswith(";")
):
return handle_constructor_default_values(x)
@@ -297,27 +325,30 @@ def snippet_translate(x):
# type var_name[] = {...
# type var_name {...
# if re.search(r"^[a-zA-Z0-9]+(<.*?>)? [\w\*]+\[\] * = *\{", x.strip()):
- if re.search(r"^[a-zA-Z0-9]+(<.*?>)? [\w\*]+\[?\]? * =? *\{", x.strip()):
+ if ARRAY_VAR_PATTERN.search(xs):
x = handle_array_declarations(x)
+ xs = x.strip()
# Methods with return type
# int Class::method(...)
# QStringView Message::body()
- if re.search(r"^[a-zA-Z0-9]+(<.*?>)? [\w]+::[\w\*\&]+\(.*\)$", x.strip()):
+ if RETURN_TYPE_PATTERN.search(xs):
# We just need to capture the 'method name' and 'arguments'
x = handle_methods_return_type(x)
+ xs = x.strip()
# Handling functions
# By this section of the function, we cover all the other cases
# So we can safely assume it's not a variable declaration
- if re.search(r"^[a-zA-Z0-9]+(<.*?>)? [\w\*\&]+\(.*\)$", x.strip()):
+ if FUNCTION_PATTERN.search(xs):
x = handle_functions(x)
+ xs = x.strip()
# if it is a C++ iterator declaration, then ignore it due to dynamic typing in Python
# eg: std::vector<int> it;
# the case of iterator being used inside a for loop is already handed in handle_for(..)
# TODO: handle iterator initialization statement like it = container.begin();
- if re.search(r"(std::)?[\w]+<[\w]+>::(const_)?iterator", x):
+ if ITERATOR_PATTERN.search(x):
x = ""
return x
@@ -325,7 +356,7 @@ def snippet_translate(x):
# 'Namespace*::' -> 'Namespace*.'
# TODO: In the case where a C++ class function is defined outside the class, this would be wrong
# but we do not have such a code snippet yet
- if re.search(r"[\w]+::", x):
+ if SCOPE_PATTERN.search(x):
x = x.replace("::", ".")
# General return for no special cases
diff --git a/tools/snippets_translate/handlers.py b/tools/snippets_translate/handlers.py
index 72c70eef8..26633fc50 100644
--- a/tools/snippets_translate/handlers.py
+++ b/tools/snippets_translate/handlers.py
@@ -44,8 +44,37 @@ from parse_utils import (dstrip, get_indent, get_qt_module_class,
parse_arguments, remove_ref, replace_main_commas)
IF_PATTERN = re.compile(r'^\s*if\s*\(')
+PARENTHESES_NONEMPTY_CONTENT_PATTERN = re.compile(r"\((.+)\)")
+LOCAL_INCLUDE_PATTERN = re.compile(r'"(.*)"')
+GLOBAL_INCLUDE_PATTERN = re.compile(r"<(.*)>")
+IF_CONDITION_PATTERN = PARENTHESES_NONEMPTY_CONTENT_PATTERN
ELSE_IF_PATTERN = re.compile(r'^\s*}?\s*else if\s*\(')
WHILE_PATTERN = re.compile(r'^\s*while\s*\(')
+CAST_PATTERN = re.compile(r"[a-z]+_cast<(.*?)>\((.*?)\)") # Non greedy match of <>
+ITERATOR_LOOP_PATTERN = re.compile(r"= *(.*)egin\(")
+REMOVE_TEMPLATE_PARAMETER_PATTERN = re.compile("<.*>")
+PARENTHESES_CONTENT_PATTERN = re.compile(r"\((.*)\)")
+CONSTRUCTOR_BODY_PATTERN = re.compile(".*{ *}.*")
+CONSTRUCTOR_BODY_REPLACEMENT_PATTERN = re.compile("{ *}")
+CONSTRUCTOR_BASE_PATTERN = re.compile("^ *: *")
+NEGATE_PATTERN = re.compile(r"!(.)")
+CLASS_TEMPLATE_PATTERN = re.compile(r".*<.*>")
+EMPTY_CLASS_PATTERN = re.compile(r".*{.*}")
+EMPTY_CLASS_REPLACEMENT_PATTERN = re.compile(r"{.*}")
+FUNCTION_BODY_PATTERN = re.compile(r"\{(.*)\}")
+ARRAY_DECLARATION_PATTERN = re.compile(r"^[a-zA-Z0-9\<\>]+ ([\w\*]+) *\[?\]?")
+RETURN_TYPE_PATTERN = re.compile(r"^ *[a-zA-Z0-9]+ [\w]+::([\w\*\&]+\(.*\)$)")
+CAPTURE_PATTERN = re.compile(r"^ *([a-zA-Z0-9]+) ([\w\*\&]+\(.*\)$)")
+USELESS_QT_CLASSES_PATTERNS = [
+ re.compile(r"QLatin1String\((.*)\)"),
+ re.compile(r"QLatin1Char\((.*)\)")
+]
+COMMENT1_PATTERN = re.compile(r" *# *[\w\ ]+$")
+COMMENT2_PATTERN = re.compile(r" *# *(.*)$")
+COUT_ENDL_PATTERN = re.compile(r"cout *<<(.*)<< *.*endl")
+COUT1_PATTERN = re.compile(r" *<< *")
+COUT2_PATTERN = re.compile(r".*cout *<<")
+COUT_ENDL2_PATTERN = re.compile(r"<< +endl")
def handle_condition(x, name):
@@ -62,10 +91,9 @@ def handle_condition(x, name):
comment = f" #{comment_content[-1]}"
x = x.replace(f"//{comment_content[-1]}", "")
- re_par = re.compile(r"\((.+)\)")
- match = re_par.search(x)
+ match = IF_CONDITION_PATTERN.search(x)
if match:
- condition = re_par.search(x).group(1)
+ condition = match.group(1)
return f"{get_indent(x)}{name} {condition.strip()}:{comment}"
else:
print(f'snippets_translate: Warning "{x}" does not match condition pattern',
@@ -93,34 +121,23 @@ def handle_inc_dec(x, operator):
def handle_casts(x):
- re_type = re.compile(r"<(.*)>")
- re_data = re.compile(r"_cast<.*>\((.*)\)")
- type_name = re_type.search(x)
- data_name = re_data.search(x)
-
- if type_name and data_name:
- type_name = type_name.group(1).replace("*", "")
- data_name = data_name.group(1)
- new_value = f"{type_name}({data_name})"
-
- if "static_cast" in x:
- x = re.sub(r"static_cast<.*>\(.*\)", new_value, x)
- elif "dynamic_cast" in x:
- x = re.sub(r"dynamic_cast<.*>\(.*\)", new_value, x)
- elif "const_cast" in x:
- x = re.sub(r"const_cast<.*>\(.*\)", new_value, x)
- elif "reinterpret_cast" in x:
- x = re.sub(r"reinterpret_cast<.*>\(.*\)", new_value, x)
- elif "qobject_cast" in x:
- x = re.sub(r"qobject_cast<.*>\(.*\)", new_value, x)
+ while True:
+ match = CAST_PATTERN.search(x)
+ if not match:
+ break
+ type_name = match.group(1).strip()
+ while type_name.endswith("*") or type_name.endswith("&") or type_name.endswith(" "):
+ type_name = type_name[:-1]
+ data_name = match.group(2).strip()
+ python_cast = f"{type_name}({data_name})"
+ x = x[0:match.start(0)] + python_cast + x[match.end(0):]
return x
def handle_include(x):
if '"' in x:
- re_par = re.compile(r'"(.*)"')
- header = re_par.search(x)
+ header = LOCAL_INCLUDE_PATTERN.search(x)
if header:
header_name = header.group(1).replace(".h", "")
module_name = header_name.replace('/', '.')
@@ -130,8 +147,7 @@ def handle_include(x):
# besides '"something.h"'
x = ""
elif "<" in x and ">" in x:
- re_par = re.compile(r"<(.*)>")
- name = re_par.search(x).group(1)
+ name = GLOBAL_INCLUDE_PATTERN.search(x).group(1)
t = get_qt_module_class(name)
# if it's not a Qt module or class, we discard it.
if t is None:
@@ -159,8 +175,7 @@ def handle_conditions(x):
def handle_for(x):
- re_content = re.compile(r"\((.*)\)")
- content = re_content.search(x)
+ content = PARENTHESES_CONTENT_PATTERN.search(x)
new_x = x
if content:
@@ -175,7 +190,7 @@ def handle_for(x):
# iterators
if "begin(" in x.lower() and "end(" in x.lower():
- name = re.search(r"= *(.*)egin\(", start)
+ name = ITERATOR_LOOP_PATTERN.search(start)
iterable = None
iterator = None
if name:
@@ -256,23 +271,22 @@ def handle_for(x):
def handle_foreach(x):
- re_content = re.compile(r"\((.*)\)")
- content = re_content.search(x)
+ content = PARENTHESES_CONTENT_PATTERN.search(x)
if content:
parenthesis = content.group(1)
iterator, iterable = parenthesis.split(",", 1)
# remove iterator type
it = dstrip(iterator.split()[-1])
# remove <...> from iterable
- value = re.sub("<.*>", "", iterable)
+ value = REMOVE_TEMPLATE_PARAMETER_PATTERN.sub("", iterable)
return f"{get_indent(x)}for {it} in {value}:"
def handle_type_var_declaration(x):
# remove content between <...>
if "<" in x and ">" in x:
- x = " ".join(re.sub("<.*>", "", i) for i in x.split())
- content = re.search(r"\((.*)\)", x)
+ x = " ".join(REMOVE_TEMPLATE_PARAMETER_PATTERN.sub("", i) for i in x.split())
+ content = PARENTHESES_CONTENT_PATTERN.search(x)
if content:
# this means we have something like:
# QSome thing(...)
@@ -288,8 +302,7 @@ def handle_type_var_declaration(x):
def handle_constructors(x):
- re_content = re.compile(r"\((.*)\)")
- arguments = re_content.search(x).group(1)
+ arguments = PARENTHESES_CONTENT_PATTERN.search(x).group(1)
class_method = x.split("(")[0].split("::")
if len(class_method) == 2:
# Equal 'class name' and 'method name'
@@ -307,8 +320,8 @@ def handle_constructor_default_values(x):
# we discard that section completely, since even with a single
# value, we don't need to take care of it, for example:
# ' : a(1) { } -> self.a = 1
- if re.search(".*{ *}.*", x):
- x = re.sub("{ *}", "", x)
+ if CONSTRUCTOR_BODY_PATTERN.search(x):
+ x = CONSTRUCTOR_BODY_REPLACEMENT_PATTERN.sub("", x)
values = "".join(x.split(":", 1))
# Check the commas that are not inside round parenthesis
@@ -323,26 +336,24 @@ def handle_constructor_default_values(x):
if "@" in values:
return_values = ""
for arg in values.split("@"):
- arg = re.sub("^ *: *", "", arg).strip()
+ arg = CONSTRUCTOR_BASE_PATTERN.sub("", arg).strip()
if arg.startswith("Q"):
class_name = arg.split("(")[0]
content = arg.replace(class_name, "")[1:-1]
return_values += f" {class_name}.__init__(self, {content})\n"
elif arg:
var_name = arg.split("(")[0]
- re_par = re.compile(r"\((.+)\)")
- content = re_par.search(arg).group(1)
+ content = PARENTHESES_NONEMPTY_CONTENT_PATTERN.search(arg).group(1)
return_values += f" self.{var_name} = {content}\n"
else:
- arg = re.sub("^ *: *", "", values).strip()
+ arg = CONSTRUCTOR_BASE_PATTERN.sub("", values).strip()
if arg.startswith("Q"):
class_name = arg.split("(")[0]
content = arg.replace(class_name, "")[1:-1]
return f" {class_name}.__init__(self, {content})"
elif arg:
var_name = arg.split("(")[0]
- re_par = re.compile(r"\((.+)\)")
- match = re_par.search(arg)
+ match = PARENTHESES_NONEMPTY_CONTENT_PATTERN.search(arg)
if match:
content = match.group(1)
return f" self.{var_name} = {content}"
@@ -356,27 +367,27 @@ def handle_constructor_default_values(x):
def handle_cout_endl(x):
# if comment at the end
comment = ""
- if re.search(r" *# *[\w\ ]+$", x):
- comment = f' # {re.search(" *# *(.*)$", x).group(1)}'
+ if COMMENT1_PATTERN.search(x):
+ match = COMMENT2_PATTERN.search(x).group(1)
+ comment = f' # {match}'
x = x.split("#")[0]
if "qDebug()" in x:
x = x.replace("qDebug()", "cout")
if "cout" in x and "endl" in x:
- re_cout_endl = re.compile(r"cout *<<(.*)<< *.*endl")
- data = re_cout_endl.search(x)
+ data = COUT_ENDL_PATTERN.search(x)
if data:
data = data.group(1)
- data = re.sub(" *<< *", ", ", data)
+ data = COUT1_PATTERN.sub(", ", data)
x = f"{get_indent(x)}print({data}){comment}"
elif "cout" in x:
- data = re.sub(".*cout *<<", "", x)
- data = re.sub(" *<< *", ", ", data)
+ data = COUT2_PATTERN.sub("", x)
+ data = COUT1_PATTERN.sub(", ", data)
x = f"{get_indent(x)}print({data}){comment}"
elif "endl" in x:
- data = re.sub("<< +endl", "", x)
- data = re.sub(" *<< *", ", ", data)
+ data = COUT_ENDL2_PATTERN.sub("", x)
+ data = COUT1_PATTERN.sub(", ", data)
x = f"{get_indent(x)}print({data}){comment}"
x = x.replace("( ", "(").replace(" )", ")").replace(" ,", ",").replace("(, ", "(")
@@ -392,8 +403,7 @@ def handle_negate(x):
elif "/*" in x:
if x.index("/*") < x.index("!"):
return x
- re_negate = re.compile(r"!(.)")
- next_char = re_negate.search(x).group(1)
+ next_char = NEGATE_PATTERN.search(x).group(1)
if next_char not in ("=", '"'):
x = x.replace("!", "not ")
return x
@@ -401,8 +411,7 @@ def handle_negate(x):
def handle_emit(x):
function_call = x.replace("emit ", "").strip()
- re_content = re.compile(r"\((.*)\)")
- match = re_content.search(function_call)
+ match = PARENTHESES_CONTENT_PATTERN.search(function_call)
if not match:
stmt = x.strip()
print(f'snippets_translate: Warning "{stmt}" does not match function call',
@@ -425,15 +434,14 @@ def handle_void_functions(x):
# if the arguments are in the same line:
arguments = None
if ")" in x:
- re_content = re.compile(r"\((.*)\)")
- parenthesis = re_content.search(x).group(1)
+ parenthesis = PARENTHESES_CONTENT_PATTERN.search(x).group(1)
arguments = dstrip(parse_arguments(parenthesis))
elif "," in x:
arguments = dstrip(parse_arguments(x.split("(")[-1]))
# check if includes a '{ ... }' after the method signature
after_signature = x.split(")")[-1]
- re_decl = re.compile(r"\{(.*)\}").search(after_signature)
+ re_decl = FUNCTION_BODY_PATTERN.search(after_signature)
extra = ""
if re_decl:
extra = re_decl.group(1)
@@ -469,13 +477,13 @@ def handle_class(x):
bases_name = ""
# Check if the class_name is templated, then remove it
- if re.search(r".*<.*>", class_name):
+ if CLASS_TEMPLATE_PATTERN.search(class_name):
class_name = class_name.split("<")[0]
# Special case: invalid notation for an example:
# class B() {...} -> clas B(): pass
- if re.search(r".*{.*}", class_name):
- class_name = re.sub(r"{.*}", "", class_name).rstrip()
+ if EMPTY_CLASS_PATTERN.search(class_name):
+ class_name = EMPTY_CLASS_REPLACEMENT_PATTERN.sub("", class_name).rstrip()
return f"{class_name}(): pass"
# Special case: check if the line ends in ','
@@ -491,8 +499,7 @@ def handle_class(x):
def handle_array_declarations(x):
- re_varname = re.compile(r"^[a-zA-Z0-9\<\>]+ ([\w\*]+) *\[?\]?")
- content = re_varname.search(x.strip())
+ content = ARRAY_DECLARATION_PATTERN.search(x.strip())
if content:
var_name = content.group(1)
rest_line = "".join(x.split("{")[1:])
@@ -501,13 +508,11 @@ def handle_array_declarations(x):
def handle_methods_return_type(x):
- re_capture = re.compile(r"^ *[a-zA-Z0-9]+ [\w]+::([\w\*\&]+\(.*\)$)")
- capture = re_capture.search(x)
+ capture = RETURN_TYPE_PATTERN.search(x)
if capture:
content = capture.group(1)
method_name = content.split("(")[0]
- re_par = re.compile(r"\((.+)\)")
- par_capture = re_par.search(x)
+ par_capture = PARENTHESES_NONEMPTY_CONTENT_PATTERN.search(x)
arguments = "(self)"
if par_capture:
arguments = f"(self, {par_capture.group(1)})"
@@ -516,16 +521,14 @@ def handle_methods_return_type(x):
def handle_functions(x):
- re_capture = re.compile(r"^ *([a-zA-Z0-9]+) ([\w\*\&]+\(.*\)$)")
- capture = re_capture.search(x)
+ capture = CAPTURE_PATTERN.search(x)
if capture:
return_type = capture.group(1)
if return_type == "return": # "return QModelIndex();"
return x
content = capture.group(2)
function_name = content.split("(")[0]
- re_par = re.compile(r"\((.+)\)")
- par_capture = re_par.search(x)
+ par_capture = PARENTHESES_NONEMPTY_CONTENT_PATTERN.search(x)
arguments = ""
if par_capture:
for arg in par_capture.group(1).split(","):
@@ -538,10 +541,8 @@ def handle_functions(x):
def handle_useless_qt_classes(x):
- _classes = ("QLatin1String", "QLatin1Char")
- for i in _classes:
- re_content = re.compile(fr"{i}\((.*)\)")
- content = re_content.search(x)
+ for c in USELESS_QT_CLASSES_PATTERNS:
+ content = c.search(x)
if content:
x = x.replace(content.group(0), content.group(1))
return x
diff --git a/tools/snippets_translate/tests/test_converter.py b/tools/snippets_translate/tests/test_converter.py
index 2c127a7ba..0057159c3 100644
--- a/tools/snippets_translate/tests/test_converter.py
+++ b/tools/snippets_translate/tests/test_converter.py
@@ -127,6 +127,10 @@ def test_cast():
st("elapsed = (elapsed + qobject_cast<QTimer*>(sender())->interval()) % 1000;")
== "elapsed = (elapsed + QTimer(sender()).interval()) % 1000"
)
+ assert (
+ st("a = qobject_cast<type*>(data) * 9 + static_cast<int>(42)")
+ == "a = type(data) * 9 + int(42)"
+ )
def test_double_colon():