aboutsummaryrefslogtreecommitdiffstats
path: root/tools/qtpy2cpp_lib
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2022-06-01 15:21:36 +0200
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2022-06-04 11:07:36 +0200
commit987cf3c516f06b269b60364222ff97be7a7f3755 (patch)
tree8974f44063b7599e241d946c6bf634fa9f9d6cab /tools/qtpy2cpp_lib
parentfba2f8dad8e1d313b4bab13950bb7607b2b8e1da (diff)
qtpy2cpp: Improve function definitions
- Handle type annotations in function definitions with some heuristics how to pass typical Qt classes. - Fix the formatting of default parameters. - Handle Slot decorators. - Ignore the above elements later when the parser traverses them Introduce concenience functions for checking visitor scope. Pick-to: 6.3 Task-number: PYSIDE-1945 Change-Id: I489088025b0d6a76d43da6154af4db58b748adbe Reviewed-by: Christian Tismer <tismer@stackless.com>
Diffstat (limited to 'tools/qtpy2cpp_lib')
-rw-r--r--tools/qtpy2cpp_lib/formatter.py42
-rw-r--r--tools/qtpy2cpp_lib/visitor.py53
2 files changed, 83 insertions, 12 deletions
diff --git a/tools/qtpy2cpp_lib/formatter.py b/tools/qtpy2cpp_lib/formatter.py
index 9b454af80..07f733424 100644
--- a/tools/qtpy2cpp_lib/formatter.py
+++ b/tools/qtpy2cpp_lib/formatter.py
@@ -6,9 +6,29 @@
import ast
+from .qt import ClassFlag, qt_class_flags
+
CLOSING = {"{": "}", "(": ")", "[": "]"} # Closing parenthesis for C++
+def _fix_function_argument_type(type, for_return):
+ """Fix function argument/return qualifiers using some heuristics for Qt."""
+ if type == "float":
+ return "double"
+ if type == "str":
+ type = "QString"
+ if not type.startswith("Q"):
+ return type
+ flags = qt_class_flags(type)
+ if flags & ClassFlag.PASS_BY_VALUE:
+ return type
+ if flags & ClassFlag.PASS_BY_CONSTREF:
+ return type if for_return else f"const {type} &"
+ if flags & ClassFlag.PASS_BY_REF:
+ return type if for_return else f"{type} &"
+ return type + " *" # Assume pointer by default
+
+
def to_string(node):
"""Helper to retrieve a string from the (Lists of)Name/Attribute
aggregated into some nodes"""
@@ -70,8 +90,17 @@ def format_for_loop(f_node):
return result
+def format_name_constant(node):
+ """Format a ast.NameConstant."""
+ if node.value is None:
+ return "nullptr"
+ return "true" if node.value else "false"
+
+
def format_literal(node):
"""Returns the value of number/string literals"""
+ if isinstance(node, ast.NameConstant):
+ return format_name_constant(node)
if isinstance(node, ast.Num):
return str(node.n)
if isinstance(node, ast.Str):
@@ -125,10 +154,16 @@ def format_function_def_arguments(function_def_node):
if result:
result += ', '
if a.arg != 'self':
+ if a.annotation and isinstance(a.annotation, ast.Name):
+ result += _fix_function_argument_type(a.annotation.id, False) + ' '
result += a.arg
if default_values[i]:
result += ' = '
- result += format_literal(default_values[i])
+ default_value = default_values[i]
+ if isinstance(default_value, ast.Attribute):
+ result += format_reference(default_value)
+ else:
+ result += format_literal(default_value)
return result
@@ -218,7 +253,10 @@ class CppFormatter(Indenter):
name = '~' + class_context
warn = False
else:
- name = 'void ' + f_node.name
+ return_type = "void"
+ if f_node.returns and isinstance(f_node.returns, ast.Name):
+ return_type = _fix_function_argument_type(f_node.returns.id, True)
+ name = return_type + " " + f_node.name
self.indent_string(f'{name}({arguments})')
if warn:
self._output_file.write(' /* FIXME: types */')
diff --git a/tools/qtpy2cpp_lib/visitor.py b/tools/qtpy2cpp_lib/visitor.py
index 5bb02db31..688e8ae3d 100644
--- a/tools/qtpy2cpp_lib/visitor.py
+++ b/tools/qtpy2cpp_lib/visitor.py
@@ -9,6 +9,7 @@ import tokenize
import warnings
from .formatter import (CppFormatter, format_for_loop, format_literal,
+ format_name_constant,
format_reference, format_start_function_call,
write_import, write_import_from)
from .nodedump import debug_format_node
@@ -128,6 +129,9 @@ class ConvertVisitor(ast.NodeVisitor, CppFormatter):
def visit_Attribute(self, node):
"""Format a variable reference (cf visit_Name)"""
+ # Default parameter (like Qt::black)?
+ if self._ignore_function_def_node(node):
+ return
self._output_file.write(format_reference(node))
def visit_BinOp(self, node):
@@ -146,7 +150,9 @@ class ConvertVisitor(ast.NodeVisitor, CppFormatter):
self._output_file.write(" | ")
def _format_call(self, node):
-
+ # Decorator list?
+ if self._ignore_function_def_node(node):
+ return
f = node.func
if isinstance(f, ast.Name):
self._output_file.write(f.id)
@@ -181,7 +187,7 @@ class ConvertVisitor(ast.NodeVisitor, CppFormatter):
def visit_Call(self, node):
self._format_call(node)
# Context manager expression?
- if self._stack and isinstance(self._stack[-1], ast.withitem):
+ if self._within_context_manager():
self._output_file.write(";\n")
def _write_function_args(self, args_node):
@@ -232,7 +238,18 @@ class ConvertVisitor(ast.NodeVisitor, CppFormatter):
def visit_FunctionDef(self, node):
class_context = self._class_scope[-1] if self._class_scope else None
+ for decorator in node.decorator_list:
+ func = decorator.func # (Call)
+ if isinstance(func, ast.Name) and func.id == "Slot":
+ self._output_file.write("\npublic slots:")
self.write_function_def(node, class_context)
+ # Find stack variables
+ for arg in node.args.args:
+ if arg.annotation and isinstance(arg.annotation, ast.Name):
+ type_name = arg.annotation.id
+ flags = qt_class_flags(type_name)
+ if flags & ClassFlag.PASS_ON_STACK_MASK:
+ self._stack_variables.append(arg.arg)
self.indent()
self.generic_visit(node)
self.dedent()
@@ -302,21 +319,37 @@ class ConvertVisitor(ast.NodeVisitor, CppFormatter):
self.generic_visit(node)
self._output_file.write(' * ')
+ def _within_context_manager(self):
+ """Return whether we are within a context manager (with)."""
+ parent = self._stack[-1] if self._stack else None
+ return parent and isinstance(parent, ast.withitem)
+
+ def _ignore_function_def_node(self, node):
+ """Should this node be ignored within a FunctionDef."""
+ if not self._stack:
+ return False
+ parent = self._stack[-1]
+ # A type annotation or default value of an argument?
+ if isinstance(parent, (ast.arguments, ast.arg)):
+ return True
+ if not isinstance(parent, ast.FunctionDef):
+ return False
+ # Return type annotation or decorator call
+ return node == parent.returns or node in parent.decorator_list
+
def visit_Name(self, node):
"""Format a variable reference (cf visit_Attribute)"""
- # Context manager variable?
- if self._stack and isinstance(self._stack[-1], ast.withitem):
+ # Skip Context manager variables, return or argument type annotation
+ if self._within_context_manager() or self._ignore_function_def_node(node):
return
self._output_file.write(format_reference(node))
def visit_NameConstant(self, node):
+ # Default parameter?
+ if self._ignore_function_def_node(node):
+ return
self.generic_visit(node)
- if node.value is None:
- self._output_file.write('nullptr')
- elif not node.value:
- self._output_file.write('false')
- else:
- self._output_file.write('true')
+ self._output_file.write(format_name_constant(node))
def visit_Not(self, node):
self.generic_visit(node)