aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2022-09-02 09:43:41 +0200
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2022-09-02 12:16:45 +0200
commit065766883f5e34d20ca88084cd747813a84ed2d3 (patch)
tree2bca2c34d113f0198564155ac21789466d4b8b96 /tools
parent4cfa700d5965922b5156517a64790369deac354f (diff)
Move qtpy2cpp to sources/pyside-tools
Preparing the entry point. Task-number: PYSIDE-1945 Change-Id: I4a2fbe6d35b4f97bf0ab7cfc2085b86a40bc2558 Reviewed-by: Christian Tismer <tismer@stackless.com>
Diffstat (limited to 'tools')
-rw-r--r--tools/qtpy2cpp.py62
-rw-r--r--tools/qtpy2cpp.pyproject7
-rw-r--r--tools/qtpy2cpp_lib/astdump.py111
-rw-r--r--tools/qtpy2cpp_lib/formatter.py265
-rw-r--r--tools/qtpy2cpp_lib/nodedump.py50
-rw-r--r--tools/qtpy2cpp_lib/qt.py56
-rw-r--r--tools/qtpy2cpp_lib/tests/baseline/basic_test.cpp62
-rw-r--r--tools/qtpy2cpp_lib/tests/baseline/basic_test.py44
-rw-r--r--tools/qtpy2cpp_lib/tests/test_qtpy2cpp.py54
-rw-r--r--tools/qtpy2cpp_lib/tokenizer.py55
-rw-r--r--tools/qtpy2cpp_lib/visitor.py443
11 files changed, 0 insertions, 1209 deletions
diff --git a/tools/qtpy2cpp.py b/tools/qtpy2cpp.py
deleted file mode 100644
index 857b12b67..000000000
--- a/tools/qtpy2cpp.py
+++ /dev/null
@@ -1,62 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-import logging
-import os
-import sys
-from argparse import ArgumentParser, RawTextHelpFormatter
-from pathlib import Path
-
-from qtpy2cpp_lib.visitor import ConvertVisitor
-
-DESCRIPTION = "Tool to convert Python to C++"
-
-
-def create_arg_parser(desc):
- parser = ArgumentParser(description=desc,
- formatter_class=RawTextHelpFormatter)
- parser.add_argument("--debug", "-d", action="store_true",
- help="Debug")
- parser.add_argument("--stdout", "-s", action="store_true",
- help="Write to stdout")
- parser.add_argument("--force", "-f", action="store_true",
- help="Force overwrite of existing files")
- parser.add_argument("files", type=str, nargs="+", help="Python source file(s)")
- return parser
-
-
-if __name__ == "__main__":
- logging.basicConfig(level=logging.INFO)
- logger = logging.getLogger(__name__)
- arg_parser = create_arg_parser(DESCRIPTION)
- args = arg_parser.parse_args()
- ConvertVisitor.debug = args.debug
-
- for input_file_str in args.files:
- input_file = Path(input_file_str)
- if not input_file.is_file():
- logger.error(f"{input_file_str} does not exist or is not a file.")
- sys.exit(-1)
- file_root, ext = os.path.splitext(input_file)
- if input_file.suffix != ".py":
- logger.error(f"{input_file_str} does not appear to be a Python file.")
- sys.exit(-1)
-
- ast_tree = ConvertVisitor.create_ast(input_file_str)
- if args.stdout:
- sys.stdout.write(f"// Converted from {input_file.name}\n")
- ConvertVisitor(input_file, sys.stdout).visit(ast_tree)
- else:
- target_file = input_file.parent / (input_file.stem + ".cpp")
- if target_file.exists():
- if not target_file.is_file():
- logger.error(f"{target_file} exists and is not a file.")
- sys.exit(-1)
- if not args.force:
- logger.error(f"{target_file} exists. Use -f to overwrite.")
- sys.exit(-1)
-
- with target_file.open("w") as file:
- file.write(f"// Converted from {input_file.name}\n")
- ConvertVisitor(input_file, file).visit(ast_tree)
- logger.info(f"Wrote {target_file}.")
diff --git a/tools/qtpy2cpp.pyproject b/tools/qtpy2cpp.pyproject
deleted file mode 100644
index a059aebca..000000000
--- a/tools/qtpy2cpp.pyproject
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "files": ["qtpy2cpp.py",
- "qtpy2cpp_lib/formatter.py", "qtpy2cpp_lib/visitor.py", "qtpy2cpp_lib/nodedump.py",
- "qtpy2cpp_lib/astdump.py", "qtpy2cpp_lib/tokenizer.py", "qtpy2cpp_lib/qt.py",
- "qtpy2cpp_lib/tests/test_qtpy2cpp.py",
- "qtpy2cpp_lib/tests/baseline/basic_test.py", "qtpy2cpp_lib/tests/baseline/uic.py"]
-}
diff --git a/tools/qtpy2cpp_lib/astdump.py b/tools/qtpy2cpp_lib/astdump.py
deleted file mode 100644
index d92fb7589..000000000
--- a/tools/qtpy2cpp_lib/astdump.py
+++ /dev/null
@@ -1,111 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-"""Tool to dump a Python AST"""
-
-
-import ast
-import tokenize
-from argparse import ArgumentParser, RawTextHelpFormatter
-from enum import Enum
-
-from nodedump import debug_format_node
-
-DESCRIPTION = "Tool to dump a Python AST"
-
-
-_source_lines = []
-_opt_verbose = False
-
-
-def first_non_space(s):
- for i, c in enumerate(s):
- if c != ' ':
- return i
- return 0
-
-
-class NodeType(Enum):
- IGNORE = 1
- PRINT_ONE_LINE = 2 # Print as a one liner, do not visit children
- PRINT = 3 # Print with opening closing tag, visit children
- PRINT_WITH_SOURCE = 4 # Like PRINT, but print source line above
-
-
-def get_node_type(node):
- if isinstance(node, (ast.Load, ast.Store, ast.Delete)):
- return NodeType.IGNORE
- if isinstance(node, (ast.Add, ast.alias, ast.arg, ast.Eq, ast.Gt, ast.Lt,
- ast.Mult, ast.Name, ast.NotEq, ast.NameConstant, ast.Not,
- ast.Num, ast.Str)):
- return NodeType.PRINT_ONE_LINE
- if not hasattr(node, 'lineno'):
- return NodeType.PRINT
- if isinstance(node, (ast.Attribute)):
- return NodeType.PRINT_ONE_LINE if isinstance(node.value, ast.Name) else NodeType.PRINT
- return NodeType.PRINT_WITH_SOURCE
-
-
-class DumpVisitor(ast.NodeVisitor):
- def __init__(self):
- ast.NodeVisitor.__init__(self)
- self._indent = 0
- self._printed_source_lines = {-1}
-
- def generic_visit(self, node):
- node_type = get_node_type(node)
- if _opt_verbose and node_type in (NodeType.IGNORE, NodeType.PRINT_ONE_LINE):
- node_type = NodeType.PRINT
- if node_type == NodeType.IGNORE:
- return
- self._indent = self._indent + 1
- indent = ' ' * self._indent
-
- if node_type == NodeType.PRINT_WITH_SOURCE:
- line_number = node.lineno - 1
- if line_number not in self._printed_source_lines:
- self._printed_source_lines.add(line_number)
- line = _source_lines[line_number]
- non_space = first_non_space(line)
- print('{:04d} {}{}'.format(line_number, '_' * non_space,
- line[non_space:]))
-
- if node_type == NodeType.PRINT_ONE_LINE:
- print(indent, debug_format_node(node))
- else:
- print(indent, '>', debug_format_node(node))
- ast.NodeVisitor.generic_visit(self, node)
- print(indent, '<', type(node).__name__)
-
- self._indent = self._indent - 1
-
-
-def parse_ast(filename):
- node = None
- with tokenize.open(filename) as f:
- global _source_lines
- source = f.read()
- _source_lines = source.split('\n')
- node = ast.parse(source, mode="exec")
- return node
-
-
-def create_arg_parser(desc):
- parser = ArgumentParser(description=desc,
- formatter_class=RawTextHelpFormatter)
- parser.add_argument('--verbose', '-v', action='store_true',
- help='Verbose')
- parser.add_argument('source', type=str, help='Python source')
- return parser
-
-
-if __name__ == '__main__':
- arg_parser = create_arg_parser(DESCRIPTION)
- options = arg_parser.parse_args()
- _opt_verbose = options.verbose
- title = f'AST tree for {options.source}'
- print('=' * len(title))
- print(title)
- print('=' * len(title))
- tree = parse_ast(options.source)
- DumpVisitor().visit(tree)
diff --git a/tools/qtpy2cpp_lib/formatter.py b/tools/qtpy2cpp_lib/formatter.py
deleted file mode 100644
index 9a38e803d..000000000
--- a/tools/qtpy2cpp_lib/formatter.py
+++ /dev/null
@@ -1,265 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-"""C++ formatting helper functions and formatter class"""
-
-
-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"""
- if isinstance(node, ast.Name):
- return node.id
- if isinstance(node, ast.Attribute):
- return node.attr
- return ''
-
-
-def format_inheritance(class_def_node):
- """Returns inheritance specification of a class"""
- result = ''
- for base in class_def_node.bases:
- name = to_string(base)
- if name != 'object':
- result += ', public ' if result else ' : public '
- result += name
- return result
-
-
-def format_for_target(target_node):
- if isinstance(target_node, ast.Tuple): # for i,e in enumerate()
- result = ''
- for i, el in enumerate(target_node.elts):
- if i > 0:
- result += ', '
- result += format_reference(el)
- return result
- return format_reference(target_node)
-
-
-def format_for_loop(f_node):
- """Format a for loop
- This applies some heuristics to detect:
- 1) "for a in [1,2])" -> "for (f: {1, 2}) {"
- 2) "for i in range(5)" -> "for (i = 0; i < 5; ++i) {"
- 3) "for i in range(2,5)" -> "for (i = 2; i < 5; ++i) {"
-
- TODO: Detect other cases, maybe including enumerate().
- """
- loop_vars = format_for_target(f_node.target)
- result = 'for (' + loop_vars
- if isinstance(f_node.iter, ast.Call):
- f = format_reference(f_node.iter.func)
- if f == 'range':
- start = 0
- end = -1
- if len(f_node.iter.args) == 2:
- start = format_literal(f_node.iter.args[0])
- end = format_literal(f_node.iter.args[1])
- elif len(f_node.iter.args) == 1:
- end = format_literal(f_node.iter.args[0])
- result += f' = {start}; {loop_vars} < {end}; ++{loop_vars}'
- elif isinstance(f_node.iter, ast.List):
- # Range based for over list
- result += ': ' + format_literal_list(f_node.iter)
- elif isinstance(f_node.iter, ast.Name):
- # Range based for over variable
- result += ': ' + f_node.iter.id
- result += ') {'
- 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):
- # Fixme: escaping
- return f'"{node.s}"'
- return ''
-
-
-def format_literal_list(l_node, enclosing='{'):
- """Formats a list/tuple of number/string literals as C++ initializer list"""
- result = enclosing
- for i, el in enumerate(l_node.elts):
- if i > 0:
- result += ', '
- result += format_literal(el)
- result += CLOSING[enclosing]
- return result
-
-
-def format_member(attrib_node, qualifier_in='auto'):
- """Member access foo->member() is expressed as an attribute with
- further nested Attributes/Names as value"""
- n = attrib_node
- result = ''
- # Black magic: Guess '::' if name appears to be a class name
- qualifier = qualifier_in
- if qualifier_in == 'auto':
- qualifier = '::' if n.attr[0:1].isupper() else '->'
- while isinstance(n, ast.Attribute):
- result = n.attr if not result else n.attr + qualifier + result
- n = n.value
- if isinstance(n, ast.Name) and n.id != 'self':
- if qualifier_in == 'auto' and n.id == "Qt": # Qt namespace
- qualifier = "::"
- result = n.id + qualifier + result
- return result
-
-
-def format_reference(node, qualifier='auto'):
- """Format member reference or free item"""
- return node.id if isinstance(node, ast.Name) else format_member(node, qualifier)
-
-
-def format_function_def_arguments(function_def_node):
- """Formats arguments of a function definition"""
- # Default values is a list of the last default values, expand
- # so that indexes match
- argument_count = len(function_def_node.args.args)
- default_values = function_def_node.args.defaults
- while len(default_values) < argument_count:
- default_values.insert(0, None)
- result = ''
- for i, a in enumerate(function_def_node.args.args):
- 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 += ' = '
- default_value = default_values[i]
- if isinstance(default_value, ast.Attribute):
- result += format_reference(default_value)
- else:
- result += format_literal(default_value)
- return result
-
-
-def format_start_function_call(call_node):
- """Format a call of a free or member function"""
- return format_reference(call_node.func) + '('
-
-
-def write_import(file, i_node):
- """Print an import of a Qt class as #include"""
- for alias in i_node.names:
- if alias.name.startswith('Q'):
- file.write(f'#include <{alias.name}>\n')
-
-
-def write_import_from(file, i_node):
- """Print an import from Qt classes as #include sequence"""
- # "from PySide6.QtGui import QGuiApplication" or
- # "from PySide6 import QtGui"
- mod = i_node.module
- if not mod.startswith('PySide') and not mod.startswith('PyQt'):
- return
- dot = mod.find('.')
- qt_module = mod[dot + 1:] + '/' if dot >= 0 else ''
- for i in i_node.names:
- if i.name.startswith('Q'):
- file.write(f'#include <{qt_module}{i.name}>\n')
-
-
-class Indenter:
- """Helper for Indentation"""
-
- def __init__(self, output_file):
- self._indent_level = 0
- self._indentation = ''
- self._output_file = output_file
-
- def indent_string(self, string):
- """Start a new line by a string"""
- self._output_file.write(self._indentation)
- self._output_file.write(string)
-
- def indent_line(self, line):
- """Write an indented line"""
- self._output_file.write(self._indentation)
- self._output_file.write(line)
- self._output_file.write('\n')
-
- def INDENT(self):
- """Write indentation"""
- self._output_file.write(self._indentation)
-
- def indent(self):
- """Increase indentation level"""
- self._indent_level = self._indent_level + 1
- self._indentation = ' ' * self._indent_level
-
- def dedent(self):
- """Decrease indentation level"""
- self._indent_level = self._indent_level - 1
- self._indentation = ' ' * self._indent_level
-
-
-class CppFormatter(Indenter):
- """Provides helpers for formatting multi-line C++ constructs"""
-
- def __init__(self, output_file):
- Indenter.__init__(self, output_file)
-
- def write_class_def(self, class_node):
- """Print a class definition with inheritance"""
- self._output_file.write('\n')
- inherits = format_inheritance(class_node)
- self.indent_line(f'class {class_node.name}{inherits}')
- self.indent_line('{')
- self.indent_line('public:')
-
- def write_function_def(self, f_node, class_context):
- """Print a function definition with arguments"""
- self._output_file.write('\n')
- arguments = format_function_def_arguments(f_node)
- if f_node.name == '__init__' and class_context: # Constructor
- name = class_context
- elif f_node.name == '__del__' and class_context: # Destructor
- name = '~' + class_context
- else:
- 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})')
- self._output_file.write('\n')
- self.indent_line('{')
diff --git a/tools/qtpy2cpp_lib/nodedump.py b/tools/qtpy2cpp_lib/nodedump.py
deleted file mode 100644
index de62e9700..000000000
--- a/tools/qtpy2cpp_lib/nodedump.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-"""Helper to dump AST nodes for debugging"""
-
-
-import ast
-
-
-def to_string(node):
- """Helper to retrieve a string from the (Lists of )Name/Attribute
- aggregated into some nodes"""
- if isinstance(node, ast.Name):
- return node.id
- if isinstance(node, ast.Attribute):
- return node.attr
- return ''
-
-
-def debug_format_node(node):
- """Format AST node for debugging"""
- if isinstance(node, ast.alias):
- return f'alias("{node.name}")'
- if isinstance(node, ast.arg):
- return f'arg({node.arg})'
- if isinstance(node, ast.Attribute):
- if isinstance(node.value, ast.Name):
- nested_name = debug_format_node(node.value)
- return f'Attribute("{node.attr}", {nested_name})'
- return f'Attribute("{node.attr}")'
- if isinstance(node, ast.Call):
- return 'Call({}({}))'.format(to_string(node.func), len(node.args))
- if isinstance(node, ast.ClassDef):
- base_names = [to_string(base) for base in node.bases]
- bases = ': ' + ','.join(base_names) if base_names else ''
- return f'ClassDef({node.name}{bases})'
- if isinstance(node, ast.ImportFrom):
- return f'ImportFrom("{node.module}")'
- if isinstance(node, ast.FunctionDef):
- arg_names = [a.arg for a in node.args.args]
- return 'FunctionDef({}({}))'.format(node.name, ', '.join(arg_names))
- if isinstance(node, ast.Name):
- return 'Name("{}", Ctx={})'.format(node.id, type(node.ctx).__name__)
- if isinstance(node, ast.NameConstant):
- return f'NameConstant({node.value})'
- if isinstance(node, ast.Num):
- return f'Num({node.n})'
- if isinstance(node, ast.Str):
- return f'Str("{node.s}")'
- return type(node).__name__
diff --git a/tools/qtpy2cpp_lib/qt.py b/tools/qtpy2cpp_lib/qt.py
deleted file mode 100644
index 69bd54aeb..000000000
--- a/tools/qtpy2cpp_lib/qt.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-"""Provides some type information on Qt classes"""
-
-
-from enum import Flag
-
-
-class ClassFlag(Flag):
- PASS_BY_CONSTREF = 1
- PASS_BY_REF = 2
- PASS_BY_VALUE = 4
- PASS_ON_STACK_MASK = PASS_BY_CONSTREF | PASS_BY_REF | PASS_BY_VALUE
- INSTANTIATE_ON_STACK = 8
-
-
-_QT_CLASS_FLAGS = {
- "QBrush": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
- "QGradient": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
- "QIcon": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
- "QLine": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
- "QLineF": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
- "QPixmap": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
- "QPointF": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
- "QRect": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
- "QRectF": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
- "QSizeF": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
- "QString": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
- "QFile": ClassFlag.PASS_BY_REF | ClassFlag.INSTANTIATE_ON_STACK,
- "QSettings": ClassFlag.PASS_BY_REF | ClassFlag.INSTANTIATE_ON_STACK,
- "QTextStream": ClassFlag.PASS_BY_REF | ClassFlag.INSTANTIATE_ON_STACK,
- "QColor": ClassFlag.PASS_BY_VALUE | ClassFlag.INSTANTIATE_ON_STACK,
- "QPoint": ClassFlag.PASS_BY_VALUE | ClassFlag.INSTANTIATE_ON_STACK,
- "QSize": ClassFlag.PASS_BY_VALUE | ClassFlag.INSTANTIATE_ON_STACK,
- "QApplication": ClassFlag.INSTANTIATE_ON_STACK,
- "QColorDialog": ClassFlag.INSTANTIATE_ON_STACK,
- "QCoreApplication": ClassFlag.INSTANTIATE_ON_STACK,
- "QFileDialog": ClassFlag.INSTANTIATE_ON_STACK,
- "QFileInfo": ClassFlag.INSTANTIATE_ON_STACK,
- "QFontDialog": ClassFlag.INSTANTIATE_ON_STACK,
- "QGuiApplication": ClassFlag.INSTANTIATE_ON_STACK,
- "QMessageBox": ClassFlag.INSTANTIATE_ON_STACK,
- "QPainter": ClassFlag.INSTANTIATE_ON_STACK,
- "QPen": ClassFlag.INSTANTIATE_ON_STACK,
- "QQmlApplicationEngine": ClassFlag.INSTANTIATE_ON_STACK,
- "QQmlComponent": ClassFlag.INSTANTIATE_ON_STACK,
- "QQmlEngine": ClassFlag.INSTANTIATE_ON_STACK,
- "QQuickView": ClassFlag.INSTANTIATE_ON_STACK,
- "QSaveFile": ClassFlag.INSTANTIATE_ON_STACK
-}
-
-
-def qt_class_flags(type):
- f = _QT_CLASS_FLAGS.get(type)
- return f if f else ClassFlag(0)
diff --git a/tools/qtpy2cpp_lib/tests/baseline/basic_test.cpp b/tools/qtpy2cpp_lib/tests/baseline/basic_test.cpp
deleted file mode 100644
index 8ee7be31e..000000000
--- a/tools/qtpy2cpp_lib/tests/baseline/basic_test.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-// Converted from basic_test.py
-#include <QtCore/Qt>
-#include <QtGui/QColor>
-#include <QtGui/QPainter>
-#include <QtGui/QPaintEvent>
-#include <QtGui/QShortcut>
-#include <QtWidgets/QApplication>
-#include <QtWidgets/QWidget>
-
-class Window : public QWidget
-{
-public:
-
- Window(QWidget * parent = nullptr)
- {
- super()->__init__(parent);
- }
-
- void paintEvent(QPaintEvent * e)
- {
- paint("bla");
- }
-
- void paint(const QString & what, color = Qt::blue)
- {
- { // Converted from context manager
- p = QPainter();
- p->setPen(QColor(color));
- rect = rect();
- w = rect->width();
- h = rect->height();
- p->drawLine(0, 0, w, h);
- p->drawLine(0, h, w, 0);
- p->drawText(rect->center(), what);
- }
- }
-
- void sum()
- {
- values = {1, 2, 3};
- result = 0;
- for (v: values) {
- result += v
- }
- return result;
- }
-};
-
-int main(int argc, char *argv[])
-{
- QApplication app(sys->argv);
- window = Window();
- auto *sc = new QShortcut((Qt::CTRL | Qt::Key_Q), window);
- sc->activated->connect(window->close);
- window->setWindowTitle("Test");
- window->show();
- sys->exit(app.exec());
- return 0;
-}
diff --git a/tools/qtpy2cpp_lib/tests/baseline/basic_test.py b/tools/qtpy2cpp_lib/tests/baseline/basic_test.py
deleted file mode 100644
index 10dc73767..000000000
--- a/tools/qtpy2cpp_lib/tests/baseline/basic_test.py
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/usr/bin/env python
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-import sys
-
-from PySide6.QtCore import qVersion, Qt
-from PySide6.QtGui import QColor, QPainter, QPaintEvent, QShortcut
-from PySide6.QtWidgets import QApplication, QWidget
-
-
-class Window(QWidget):
- def __init__(self, parent: QWidget = None):
- super().__init__(parent)
-
- def paintEvent(self, e: QPaintEvent):
- self.paint("bla")
-
- def paint(self, what: str, color: Qt.GlobalColor = Qt.blue):
- with QPainter(self) as p:
- p.setPen(QColor(color))
- rect = self.rect()
- w = rect.width()
- h = rect.height()
- p.drawLine(0, 0, w, h)
- p.drawLine(0, h, w, 0)
- p.drawText(rect.center(), what)
-
- def sum(self):
- values = [1, 2, 3]
- result = 0
- for v in values:
- result += v
- return result
-
-
-if __name__ == '__main__':
- app = QApplication(sys.argv)
- window = Window()
- sc = QShortcut(Qt.CTRL | Qt.Key_Q, window)
- sc.activated.connect(window.close)
- window.setWindowTitle("Test")
- window.show()
- sys.exit(app.exec())
diff --git a/tools/qtpy2cpp_lib/tests/test_qtpy2cpp.py b/tools/qtpy2cpp_lib/tests/test_qtpy2cpp.py
deleted file mode 100644
index f9f921705..000000000
--- a/tools/qtpy2cpp_lib/tests/test_qtpy2cpp.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-import subprocess
-import tempfile
-import sys
-from pathlib import Path
-
-# run pytest-3
-
-
-def diff_code(actual_code, expected_file):
- """Helper to run diff if something fails (Linux only)."""
- with tempfile.NamedTemporaryFile(suffix=".cpp") as tf:
- tf.write(actual_code.encode('utf-8'))
- tf.flush()
- diff_cmd = ["diff", "-u", expected_file, tf.name]
- subprocess.run(diff_cmd)
-
-
-def run_converter(tool, file):
- """Run the converter and return C++ code generated from file."""
- cmd = [sys.executable, tool, "--stdout", file]
- output = ""
- with subprocess.Popen(cmd, stdout=subprocess.PIPE) as proc:
- output_b, errors_b = proc.communicate()
- output = output_b.decode('utf-8')
- if errors_b:
- print(errors_b.decode('utf-8'), file=sys.stderr)
- return output
-
-
-def test_examples():
- dir = Path(__file__).resolve().parent
- tool = dir.parents[1] / "qtpy2cpp.py"
- assert(tool.is_file)
- for test_file in (dir / "baseline").glob("*.py"):
- assert(test_file.is_file)
- expected_file = test_file.parent / (test_file.stem + ".cpp")
- if expected_file.is_file():
- actual_code = run_converter(tool, test_file)
- assert(actual_code)
- expected_code = expected_file.read_text()
- # Strip the license
- code_start = expected_code.find("// Converted from")
- assert(code_start != -1)
- expected_code = expected_code[code_start:]
-
- if actual_code != expected_code:
- diff_code(actual_code, expected_file)
- assert(actual_code == expected_code)
- else:
- print(f"Warning, {test_file} is missing a .cpp file.",
- file=sys.stderr)
diff --git a/tools/qtpy2cpp_lib/tokenizer.py b/tools/qtpy2cpp_lib/tokenizer.py
deleted file mode 100644
index d5e26c2a8..000000000
--- a/tools/qtpy2cpp_lib/tokenizer.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-"""Tool to dump Python Tokens"""
-
-
-import sys
-import tokenize
-
-
-def format_token(t):
- r = repr(t)
- if r.startswith('TokenInfo('):
- r = r[10:]
- pos = r.find("), line='")
- if pos < 0:
- pos = r.find('), line="')
- if pos > 0:
- r = r[:pos + 1]
- return r
-
-
-def first_non_space(s):
- for i, c in enumerate(s):
- if c != ' ':
- return i
- return 0
-
-
-if __name__ == '__main__':
- if len(sys.argv) < 2:
- print("Specify file Name")
- sys.exit(1)
- filename = sys.argv[1]
- indent_level = 0
- indent = ''
- last_line_number = -1
- with tokenize.open(filename) as f:
- generator = tokenize.generate_tokens(f.readline)
- for t in generator:
- line_number = t.start[0]
- if line_number != last_line_number:
- code_line = t.line.rstrip()
- non_space = first_non_space(code_line)
- print('{:04d} {}{}'.format(line_number, '_' * non_space,
- code_line[non_space:]))
- last_line_number = line_number
- if t.type == tokenize.INDENT:
- indent_level = indent_level + 1
- indent = ' ' * indent_level
- elif t.type == tokenize.DEDENT:
- indent_level = indent_level - 1
- indent = ' ' * indent_level
- else:
- print(' ', indent, format_token(t))
diff --git a/tools/qtpy2cpp_lib/visitor.py b/tools/qtpy2cpp_lib/visitor.py
deleted file mode 100644
index 1e8b5dc84..000000000
--- a/tools/qtpy2cpp_lib/visitor.py
+++ /dev/null
@@ -1,443 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-"""AST visitor printing out C++"""
-
-import ast
-import sys
-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
-from .qt import ClassFlag, qt_class_flags
-
-
-def _is_qt_constructor(assign_node):
- """Is this assignment node a plain construction of a Qt class?
- 'f = QFile(name)'. Returns the class_name."""
- call = assign_node.value
- if (isinstance(call, ast.Call) and isinstance(call.func, ast.Name)):
- func = call.func.id
- if func.startswith("Q"):
- return func
- return None
-
-
-def _is_if_main(if_node):
- """Return whether an if statement is: if __name__ == '__main__' """
- test = if_node.test
- return (isinstance(test, ast.Compare)
- and len(test.ops) == 1
- and isinstance(test.ops[0], ast.Eq)
- and isinstance(test.left, ast.Name)
- and test.left.id == "__name__"
- and len(test.comparators) == 1
- and isinstance(test.comparators[0], ast.Constant)
- and test.comparators[0].value == "__main__")
-
-
-class ConvertVisitor(ast.NodeVisitor, CppFormatter):
- """AST visitor printing out C++
- Note on implementation:
- - Any visit_XXX() overridden function should call self.generic_visit(node)
- to continue visiting
- - When controlling the visiting manually (cf visit_Call()),
- self.visit(child) needs to be called since that dispatches to
- visit_XXX(). This is usually done to prevent undesired output
- for example from references of calls, etc.
- """
-
- debug = False
-
- def __init__(self, file_name, output_file):
- ast.NodeVisitor.__init__(self)
- CppFormatter.__init__(self, output_file)
- self._file_name = file_name
- self._class_scope = [] # List of class names
- self._stack = [] # nodes
- self._stack_variables = [] # variables instantiated on stack
- self._debug_indent = 0
-
- @staticmethod
- def create_ast(filename):
- """Create an Abstract Syntax Tree on which a visitor can be run"""
- node = None
- with tokenize.open(filename) as file:
- node = ast.parse(file.read(), mode="exec")
- return node
-
- def generic_visit(self, node):
- parent = self._stack[-1] if self._stack else None
- if self.debug:
- self._debug_enter(node, parent)
- self._stack.append(node)
- try:
- super().generic_visit(node)
- except Exception as e:
- line_no = node.lineno if hasattr(node, 'lineno') else -1
- error_message = str(e)
- message = f'{self._file_name}:{line_no}: Error "{error_message}"'
- warnings.warn(message)
- self._output_file.write(f'\n// {error_message}\n')
- del self._stack[-1]
- if self.debug:
- self._debug_leave(node)
-
- def visit_Add(self, node):
- self._handle_bin_op(node, "+")
-
- def _is_augmented_assign(self):
- """Is it 'Augmented_assign' (operators +=/-=, etc)?"""
- return self._stack and isinstance(self._stack[-1], ast.AugAssign)
-
- def visit_AugAssign(self, node):
- """'Augmented_assign', Operators +=/-=, etc."""
- self.INDENT()
- self.generic_visit(node)
- self._output_file.write("\n")
-
- def visit_Assign(self, node):
- self.INDENT()
-
- qt_class = _is_qt_constructor(node)
- on_stack = qt_class and qt_class_flags(qt_class) & ClassFlag.INSTANTIATE_ON_STACK
-
- # Is this a free variable and not a member assignment? Instantiate
- # on stack or give a type
- if len(node.targets) == 1 and isinstance(node.targets[0], ast.Name):
- if qt_class:
- if on_stack:
- # "QFile f(args)"
- var = node.targets[0].id
- self._stack_variables.append(var)
- self._output_file.write(f"{qt_class} {var}(")
- self._write_function_args(node.value.args)
- self._output_file.write(");\n")
- return
- self._output_file.write("auto *")
-
- line_no = node.lineno if hasattr(node, 'lineno') else -1
- for target in node.targets:
- if isinstance(target, ast.Tuple):
- w = f"{self._file_name}:{line_no}: List assignment not handled."
- warnings.warn(w)
- elif isinstance(target, ast.Subscript):
- w = f"{self._file_name}:{line_no}: Subscript assignment not handled."
- warnings.warn(w)
- else:
- self._output_file.write(format_reference(target))
- self._output_file.write(' = ')
- if qt_class and not on_stack:
- self._output_file.write("new ")
- self.visit(node.value)
- self._output_file.write(';\n')
-
- 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):
- # Parentheses are not exposed, so, every binary operation needs to
- # be enclosed by ().
- self._output_file.write('(')
- self.generic_visit(node)
- self._output_file.write(')')
-
- def _handle_bin_op(self, node, op):
- """Handle a binary operator which can appear as 'Augmented Assign'."""
- self.generic_visit(node)
- full_op = f" {op}= " if self._is_augmented_assign() else f" {op} "
- self._output_file.write(full_op)
-
- def visit_BitAnd(self, node):
- self._handle_bin_op(node, "&")
-
- def visit_BitOr(self, node):
- self._handle_bin_op(node, "|")
-
- 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)
- else:
- # Attributes denoting chained calls "a->b()->c()". Walk along in
- # reverse order, recursing for other calls.
- names = []
- n = f
- while isinstance(n, ast.Attribute):
- names.insert(0, n.attr)
- n = n.value
-
- if isinstance(n, ast.Name): # Member or variable reference
- if n.id != "self":
- sep = "->"
- if n.id in self._stack_variables:
- sep = "."
- elif n.id[0:1].isupper(): # Heuristics for static
- sep = "::"
- self._output_file.write(n.id)
- self._output_file.write(sep)
- elif isinstance(n, ast.Call): # A preceding call
- self._format_call(n)
- self._output_file.write("->")
-
- self._output_file.write("->".join(names))
-
- self._output_file.write('(')
- self._write_function_args(node.args)
- self._output_file.write(')')
-
- def visit_Call(self, node):
- self._format_call(node)
- # Context manager expression?
- if self._within_context_manager():
- self._output_file.write(";\n")
-
- def _write_function_args(self, args_node):
- # Manually do visit(), skip the children of func
- for i, arg in enumerate(args_node):
- if i > 0:
- self._output_file.write(', ')
- self.visit(arg)
-
- def visit_ClassDef(self, node):
- # Manually do visit() to skip over base classes
- # and annotations
- self._class_scope.append(node.name)
- self.write_class_def(node)
- self.indent()
- for b in node.body:
- self.visit(b)
- self.dedent()
- self.indent_line('};')
- del self._class_scope[-1]
-
- def visit_Div(self, node):
- self._handle_bin_op(node, "/")
-
- def visit_Eq(self, node):
- self.generic_visit(node)
- self._output_file.write(" == ")
-
- def visit_Expr(self, node):
- self.INDENT()
- self.generic_visit(node)
- self._output_file.write(';\n')
-
- def visit_Gt(self, node):
- self.generic_visit(node)
- self._output_file.write(" > ")
-
- def visit_GtE(self, node):
- self.generic_visit(node)
- self._output_file.write(" >= ")
-
- def visit_For(self, node):
- # Manually do visit() to get the indentation right.
- # TODO: what about orelse?
- self.indent_line(format_for_loop(node))
- self.indent()
- for b in node.body:
- self.visit(b)
- self.dedent()
- self.indent_line('}')
-
- 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()
- self.indent_line('}')
- self._stack_variables.clear()
-
- def visit_If(self, node):
- # Manually do visit() to get the indentation right. Note:
- # elsif() is modelled as nested if.
-
- # Check for the main function
- if _is_if_main(node):
- self._output_file.write("\nint main(int argc, char *argv[])\n{\n")
- self.indent()
- for b in node.body:
- self.visit(b)
- self.indent_string("return 0;\n")
- self.dedent()
- self._output_file.write("}\n")
- return
-
- self.indent_string('if (')
- self.visit(node.test)
- self._output_file.write(') {\n')
- self.indent()
- for b in node.body:
- self.visit(b)
- self.dedent()
- self.indent_string('}')
- if node.orelse:
- self._output_file.write(' else {\n')
- self.indent()
- for b in node.orelse:
- self.visit(b)
- self.dedent()
- self.indent_string('}')
- self._output_file.write('\n')
-
- def visit_Import(self, node):
- write_import(self._output_file, node)
-
- def visit_ImportFrom(self, node):
- write_import_from(self._output_file, node)
-
- def visit_List(self, node):
- # Manually do visit() to get separators right
- self._output_file.write('{')
- for i, el in enumerate(node.elts):
- if i > 0:
- self._output_file.write(', ')
- self.visit(el)
- self._output_file.write('}')
-
- def visit_LShift(self, node):
- self.generic_visit(node)
- self._output_file.write(" << ")
-
- def visit_Lt(self, node):
- self.generic_visit(node)
- self._output_file.write(" < ")
-
- def visit_LtE(self, node):
- self.generic_visit(node)
- self._output_file.write(" <= ")
-
- def visit_Mult(self, node):
- self._handle_bin_op(node, "*")
-
- 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_Index(self, node):
- self._output_file.write("[")
- self.generic_visit(node)
- self._output_file.write("]")
-
- def visit_Name(self, node):
- """Format a variable reference (cf visit_Attribute)"""
- # 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)
- self._output_file.write(format_name_constant(node))
-
- def visit_Not(self, node):
- self.generic_visit(node)
- self._output_file.write("!")
-
- def visit_NotEq(self, node):
- self.generic_visit(node)
- self._output_file.write(" != ")
-
- def visit_Num(self, node):
- self.generic_visit(node)
- self._output_file.write(format_literal(node))
-
- def visit_RShift(self, node):
- self.generic_visit(node)
- self._output_file.write(" >> ")
-
- def visit_Return(self, node):
- self.indent_string("return")
- if node.value:
- self._output_file.write(" ")
- self.generic_visit(node)
- self._output_file.write(";\n")
-
- def visit_Slice(self, node):
- self._output_file.write("[")
- if node.lower:
- self.visit(node.lower)
- self._output_file.write(":")
- if node.upper:
- self.visit(node.upper)
- self._output_file.write("]")
-
- def visit_Str(self, node):
- self.generic_visit(node)
- self._output_file.write(format_literal(node))
-
- def visit_Sub(self, node):
- self._handle_bin_op(node, "-")
-
- def visit_UnOp(self, node):
- self.generic_visit(node)
-
- def visit_With(self, node):
- self.INDENT()
- self._output_file.write("{ // Converted from context manager\n")
- self.indent()
- for item in node.items:
- self.INDENT()
- if item.optional_vars:
- self._output_file.write(format_reference(item.optional_vars))
- self._output_file.write(" = ")
- self.generic_visit(node)
- self.dedent()
- self.INDENT()
- self._output_file.write("}\n")
-
- def _debug_enter(self, node, parent=None):
- message = '{}>generic_visit({})'.format(' ' * self ._debug_indent,
- debug_format_node(node))
- if parent:
- message += ', parent={}'.format(debug_format_node(parent))
- message += '\n'
- sys.stderr.write(message)
- self._debug_indent += 1
-
- def _debug_leave(self, node):
- self._debug_indent -= 1
- message = '{}<generic_visit({})\n'.format(' ' * self ._debug_indent,
- type(node).__name__)
- sys.stderr.write(message)