aboutsummaryrefslogtreecommitdiffstats
path: root/sources/pyside-tools/project/project_data.py
diff options
context:
space:
mode:
Diffstat (limited to 'sources/pyside-tools/project/project_data.py')
-rw-r--r--sources/pyside-tools/project/project_data.py244
1 files changed, 244 insertions, 0 deletions
diff --git a/sources/pyside-tools/project/project_data.py b/sources/pyside-tools/project/project_data.py
new file mode 100644
index 000000000..52e20be3f
--- /dev/null
+++ b/sources/pyside-tools/project/project_data.py
@@ -0,0 +1,244 @@
+# 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 json
+import os
+import subprocess
+import sys
+from typing import List, Tuple
+from pathlib import Path
+from . import (METATYPES_JSON_SUFFIX, PROJECT_FILE_SUFFIX, TRANSLATION_SUFFIX,
+ qt_metatype_json_dir, MOD_CMD, QML_IMPORT_MAJOR_VERSION,
+ QML_IMPORT_MINOR_VERSION, QML_IMPORT_NAME, QT_MODULES)
+
+
+def is_python_file(file: Path) -> bool:
+ return (file.suffix == ".py"
+ or sys.platform == "win32" and file.suffix == ".pyw")
+
+
+class ProjectData:
+ def __init__(self, project_file: Path) -> None:
+ """Parse the project."""
+ self._project_file = project_file
+ self._sub_projects_files: List[Path] = []
+
+ # All sources except subprojects
+ self._files: List[Path] = []
+ # QML files
+ self._qml_files: List[Path] = []
+ # Python files
+ self.main_file: Path = None
+ self._python_files: List[Path] = []
+ # ui files
+ self._ui_files: List[Path] = []
+ # qrc files
+ self._qrc_files: List[Path] = []
+ # ts files
+ self._ts_files: List[Path] = []
+
+ with project_file.open("r") as pyf:
+ pyproject = json.load(pyf)
+ for f in pyproject["files"]:
+ file = Path(project_file.parent / f)
+ if file.suffix == PROJECT_FILE_SUFFIX:
+ self._sub_projects_files.append(file)
+ else:
+ self._files.append(file)
+ if file.suffix == ".qml":
+ self._qml_files.append(file)
+ elif is_python_file(file):
+ if file.stem == "main":
+ self.main_file = file
+ self._python_files.append(file)
+ elif file.suffix == ".ui":
+ self._ui_files.append(file)
+ elif file.suffix == ".qrc":
+ self._qrc_files.append(file)
+ elif file.suffix == TRANSLATION_SUFFIX:
+ self._ts_files.append(file)
+
+ if not self.main_file:
+ self._find_main_file()
+
+ @property
+ def project_file(self):
+ return self._project_file
+
+ @property
+ def files(self):
+ return self._files
+
+ @property
+ def main_file(self):
+ return self._main_file
+
+ @main_file.setter
+ def main_file(self, main_file):
+ self._main_file = main_file
+
+ @property
+ def python_files(self):
+ return self._python_files
+
+ @property
+ def ui_files(self):
+ return self._ui_files
+
+ @property
+ def qrc_files(self):
+ return self._qrc_files
+
+ @property
+ def qml_files(self):
+ return self._qml_files
+
+ @property
+ def ts_files(self):
+ return self._ts_files
+
+ @property
+ def sub_projects_files(self):
+ return self._sub_projects_files
+
+ def _find_main_file(self) -> str:
+ """Find the entry point file containing the main function"""
+
+ def is_main(file):
+ return "__main__" in file.read_text(encoding="utf-8")
+
+ if not self.main_file:
+ for python_file in self.python_files:
+ if is_main(python_file):
+ self.main_file = python_file
+ return str(python_file)
+
+ # __main__ not found
+ print(
+ "Python file with main function not found. Add the file to" f" {self.project_file}",
+ file=sys.stderr,
+ )
+ sys.exit(1)
+
+
+class QmlProjectData:
+ """QML relevant project data."""
+
+ def __init__(self):
+ self._import_name: str = ""
+ self._import_major_version: int = 0
+ self._import_minor_version: int = 0
+ self._qt_modules: List[str] = []
+
+ def registrar_options(self):
+ result = [
+ "--import-name",
+ self._import_name,
+ "--major-version",
+ str(self._import_major_version),
+ "--minor-version",
+ str(self._import_minor_version),
+ ]
+ if self._qt_modules:
+ # Add Qt modules as foreign types
+ foreign_files: List[str] = []
+ meta_dir = qt_metatype_json_dir()
+ for mod in self._qt_modules:
+ mod_id = mod[2:].lower()
+ pattern = f"qt6{mod_id}_*"
+ if sys.platform != "win32":
+ pattern += "_" # qt6core_debug_metatypes.json (Linux)
+ pattern += METATYPES_JSON_SUFFIX
+ for f in meta_dir.glob(pattern):
+ foreign_files.append(os.fspath(f))
+ break
+ if foreign_files:
+ foreign_files_str = ",".join(foreign_files)
+ result.append(f"--foreign-types={foreign_files_str}")
+ return result
+
+ @property
+ def import_name(self):
+ return self._import_name
+
+ @import_name.setter
+ def import_name(self, n):
+ self._import_name = n
+
+ @property
+ def import_major_version(self):
+ return self._import_major_version
+
+ @import_major_version.setter
+ def import_major_version(self, v):
+ self._import_major_version = v
+
+ @property
+ def import_minor_version(self):
+ return self._import_minor_version
+
+ @import_minor_version.setter
+ def import_minor_version(self, v):
+ self._import_minor_version = v
+
+ @property
+ def qt_modules(self):
+ return self._qt_modules
+
+ @qt_modules.setter
+ def qt_modules(self, v):
+ self._qt_modules = v
+
+ def __str__(self) -> str:
+ vmaj = self._import_major_version
+ vmin = self._import_minor_version
+ return f'"{self._import_name}" v{vmaj}.{vmin}'
+
+ def __bool__(self) -> bool:
+ return len(self._import_name) > 0 and self._import_major_version > 0
+
+
+def _has_qml_decorated_class(class_list: List) -> bool:
+ """Check for QML-decorated classes in the moc json output."""
+ for d in class_list:
+ class_infos = d.get("classInfos")
+ if class_infos:
+ for e in class_infos:
+ if "QML" in e["name"]:
+ return True
+ return False
+
+
+def check_qml_decorators(py_file: Path) -> Tuple[bool, QmlProjectData]:
+ """Check if a Python file has QML-decorated classes by running a moc check
+ and return whether a class was found and the QML data."""
+ data = None
+ try:
+ cmd = [MOD_CMD, "--quiet", os.fspath(py_file)]
+ with subprocess.Popen(cmd, stdout=subprocess.PIPE) as proc:
+ data = json.load(proc.stdout)
+ proc.wait()
+ except Exception as e:
+ t = type(e).__name__
+ print(f"{t}: running {MOD_CMD} on {py_file}: {e}", file=sys.stderr)
+ sys.exit(1)
+
+ qml_project_data = QmlProjectData()
+ if not data:
+ return (False, qml_project_data) # No classes in file
+
+ first = data[0]
+ class_list = first["classes"]
+ has_class = _has_qml_decorated_class(class_list)
+ if has_class:
+ v = first.get(QML_IMPORT_NAME)
+ if v:
+ qml_project_data.import_name = v
+ v = first.get(QML_IMPORT_MAJOR_VERSION)
+ if v:
+ qml_project_data.import_major_version = v
+ qml_project_data.import_minor_version = first.get(QML_IMPORT_MINOR_VERSION)
+ v = first.get(QT_MODULES)
+ if v:
+ qml_project_data.qt_modules = v
+ return (has_class, qml_project_data)