Skip to content

Commit 545ca79

Browse files
Jaime ResanoFriedemannKleint
Jaime Resano
authored andcommitted
pyproject.toml: 2. Add pyproject.toml support for pyside6 tools
This patch adds support for pyproject.toml files to the pyside6-project tool. A new command argument is added to migrate a .pyproject JSON file to the new pyproject.toml file: `pyside6-project migrate-pyproject` The new features are tested and it is guaranteed that the current behavior is preserved. A new flag is added to the project creation operations, "--legacy-pyproject", in order to generate a .pyproject file instead of a pyproject.toml file. Note that the tomlkit library is added to the requirements.txt file. https://github.com/python-poetry/tomlkit Task-number: PYSIDE-2714 Change-Id: If33956dea73b79df0a52d4dcda3934c85e57182d Reviewed-by: Friedemann Kleint <[email protected]>
1 parent 3ea0261 commit 545ca79

File tree

9 files changed

+268
-45
lines changed

9 files changed

+268
-45
lines changed

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ patchelf==0.17.2; sys_platform == 'linux'
99
numpy<=2.0.2; python_version <= '3.9'
1010
numpy==2.1.3; python_version > '3.9'
1111
mypy[faster-cache]>=1.14.0
12+
tomlkit==0.12.1

sources/pyside-tools/project.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
from pathlib import Path
88
from argparse import ArgumentParser, RawTextHelpFormatter
99

10-
from project_lib import (QmlProjectData, check_qml_decorators, is_python_file,
11-
QMLDIR_FILE, MOD_CMD, METATYPES_JSON_SUFFIX,
12-
SHADER_SUFFIXES, TRANSLATION_SUFFIX, requires_rebuild, run_command,
13-
remove_path, ProjectData, resolve_valid_project_file, new_project,
14-
NewProjectTypes, ClOptions, DesignStudioProject)
10+
from project_lib import (QmlProjectData, check_qml_decorators, is_python_file, migrate_pyproject,
11+
QMLDIR_FILE, MOD_CMD, METATYPES_JSON_SUFFIX, SHADER_SUFFIXES,
12+
TRANSLATION_SUFFIX, requires_rebuild, run_command, remove_path,
13+
ProjectData, resolve_valid_project_file, new_project, NewProjectTypes,
14+
ClOptions, DesignStudioProject)
1515

1616
DESCRIPTION = """
1717
pyside6-project is a command line tool for creating, building and deploying Qt for Python
@@ -29,6 +29,7 @@
2929
"qmllint": "Run the qmllint tool on QML files in the project.",
3030
"deploy": "Create a deployable package of the application including all dependencies.",
3131
"lupdate": "Update translation files (.ts) with new strings from source files.",
32+
"migrate-pyproject": "Migrate a *.pyproject file to pyproject.toml format."
3233
}
3334

3435
UIC_CMD = "pyside6-uic"
@@ -263,7 +264,8 @@ def lupdate(self):
263264

264265

265266
def main(mode: str = None, dry_run: bool = False, quiet: bool = False, force: bool = False,
266-
qml_module: bool = None, project_dir: str = None, project_path: str = None):
267+
qml_module: bool = None, project_dir: str = None, project_path: str = None,
268+
legacy_pyproject: bool = False):
267269
cl_options = ClOptions(dry_run=dry_run, quiet=quiet, # noqa: F841
268270
force=force, qml_module=qml_module)
269271

@@ -281,7 +283,10 @@ def main(mode: str = None, dry_run: bool = False, quiet: bool = False, force: bo
281283
print("Invalid project name", file=sys.stderr)
282284
sys.exit(1)
283285

284-
sys.exit(new_project(project_dir, new_project_type))
286+
sys.exit(new_project(project_dir, new_project_type, legacy_pyproject))
287+
288+
if mode == "migrate-pyproject":
289+
sys.exit(migrate_pyproject(project_path))
285290

286291
try:
287292
project_file = resolve_valid_project_file(project_path)
@@ -325,6 +330,9 @@ def main(mode: str = None, dry_run: bool = False, quiet: bool = False, force: bo
325330
new_parser.add_argument(
326331
"project_dir", help="Name or location of the new project", nargs="?", type=str)
327332

333+
new_parser.add_argument(
334+
"--legacy-pyproject", action="store_true", help="Create a legacy *.pyproject file")
335+
328336
# Add subparser for project operation commands
329337
for op_mode, op_help in OPERATION_HELP.items():
330338
op_parser = subparsers.add_parser(op_mode, help=op_help)
@@ -333,4 +341,5 @@ def main(mode: str = None, dry_run: bool = False, quiet: bool = False, force: bo
333341
args = parser.parse_args()
334342

335343
main(args.mode, args.dry_run, args.quiet, args.force, args.qml_module,
336-
getattr(args, "project_dir", None), getattr(args, "project_path", None))
344+
getattr(args, "project_dir", None), getattr(args, "project_path", None),
345+
getattr(args, "legacy_pyproject", None))

sources/pyside-tools/project_lib/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@
77
QTPATHS_CMD = "qtpaths6"
88
MOD_CMD = "pyside6-metaobjectdump"
99

10+
PYPROJECT_TOML_PATTERN = "pyproject.toml"
1011
PYPROJECT_JSON_PATTERN = "*.pyproject"
1112
# Note that the order is important, as the first pattern that matches is used
12-
PYPROJECT_FILE_PATTERNS = [PYPROJECT_JSON_PATTERN]
13+
PYPROJECT_FILE_PATTERNS = [PYPROJECT_TOML_PATTERN, PYPROJECT_JSON_PATTERN]
1314
QMLDIR_FILE = "qmldir"
1415

1516
QML_IMPORT_NAME = "QML_IMPORT_NAME"
@@ -48,4 +49,5 @@ class ClOptions(metaclass=Singleton):
4849
check_qml_decorators)
4950
from .newproject import new_project, NewProjectTypes
5051
from .design_studio_project import DesignStudioProject
52+
from .pyproject_toml import parse_pyproject_toml, write_pyproject_toml, migrate_pyproject
5153
from .pyproject_json import parse_pyproject_json

sources/pyside-tools/project_lib/newproject.py

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
33
from __future__ import annotations
44

5-
import json
65
import os
76
import sys
87
from dataclasses import dataclass
98
from enum import Enum
109
from pathlib import Path
11-
from typing import Callable
10+
11+
from .pyproject_toml import write_pyproject_toml
12+
from .pyproject_json import write_pyproject_json
1213

1314
"""New project generation code."""
1415

@@ -19,23 +20,19 @@
1920
sys.exit(app.exec())
2021
"""
2122

22-
2323
_WIDGET_IMPORTS = """import sys
2424
from PySide6.QtWidgets import QApplication, QMainWindow
2525
"""
2626

27-
2827
_WIDGET_CLASS_DEFINITION = """class MainWindow(QMainWindow):
2928
def __init__(self):
3029
super().__init__()
3130
"""
3231

33-
3432
_WIDGET_SETUP_UI_CODE = """ self._ui = Ui_MainWindow()
3533
self._ui.setupUi(self)
3634
"""
3735

38-
3936
_MAINWINDOW_FORM = """<?xml version="1.0" encoding="UTF-8"?>
4037
<ui version="4.0">
4138
<class>MainWindow</class>
@@ -67,7 +64,6 @@ def __init__(self):
6764
</ui>
6865
"""
6966

70-
7167
_QUICK_FORM = """import QtQuick
7268
import QtQuick.Controls
7369
@@ -99,18 +95,17 @@ def __init__(self):
9995
sys.exit(exit_code)
10096
"""
10197

98+
NewProjectFiles = list[tuple[str, str]] # tuple of (filename, contents).
99+
102100

103101
@dataclass(frozen=True)
104102
class NewProjectType:
105103
command: str
106104
description: str
107-
get_files: Callable
108-
109-
110-
NewProjectFiles = list[tuple[str, str]] # tuple of (filename, contents).
105+
files: NewProjectFiles
111106

112107

113-
def _write_project(directory: Path, files: NewProjectFiles):
108+
def _write_project(directory: Path, files: NewProjectFiles, legacy_pyproject: bool):
114109
"""
115110
Create the project files in the specified directory.
116111
@@ -123,9 +118,12 @@ def _write_project(directory: Path, files: NewProjectFiles):
123118
print(f"Wrote {directory.name}{os.sep}{file_name}.")
124119
file_names.append(file_name)
125120

126-
pyproject = {"files": files}
127-
pyproject_file = f"{directory}.pyproject"
128-
(directory / pyproject_file).write_text(json.dumps(pyproject))
121+
if legacy_pyproject:
122+
pyproject_file = directory / f"{directory.name}.pyproject"
123+
write_pyproject_json(pyproject_file, file_names)
124+
else:
125+
pyproject_file = directory / "pyproject.toml"
126+
write_pyproject_toml(pyproject_file, directory.name, file_names)
129127
print(f"Wrote {pyproject_file}.")
130128

131129

@@ -153,16 +151,19 @@ def _qml_project() -> NewProjectFiles:
153151

154152

155153
class NewProjectTypes(Enum):
156-
QUICK = NewProjectType("new-quick", "Create a new Qt Quick project", _qml_project)
157-
WIDGET_FORM = NewProjectType("new-ui", "Create a new Qt Widgets Form project", _ui_form_project)
158-
WIDGET = NewProjectType("new-widget", "Create a new Qt Widgets project", _widget_project)
154+
QUICK = NewProjectType("new-quick", "Create a new Qt Quick project", _qml_project())
155+
WIDGET_FORM = NewProjectType("new-ui", "Create a new Qt Widgets Form project",
156+
_ui_form_project())
157+
WIDGET = NewProjectType("new-widget", "Create a new Qt Widgets project", _widget_project())
159158

160159
@staticmethod
161160
def find_by_command(command: str) -> NewProjectType | None:
162161
return next((pt.value for pt in NewProjectTypes if pt.value.command == command), None)
163162

164163

165-
def new_project(project_dir: Path, project_type: NewProjectType) -> int:
164+
def new_project(
165+
project_dir: Path, project_type: NewProjectType, legacy_pyproject: bool
166+
) -> int:
166167
"""
167168
Create a new project at the specified project_dir directory.
168169
@@ -176,15 +177,13 @@ def new_project(project_dir: Path, project_type: NewProjectType) -> int:
176177
return 1
177178
project_dir.mkdir(parents=True, exist_ok=True)
178179

179-
files = project_type.get_files()
180-
181180
try:
182-
_write_project(project_dir, files)
181+
_write_project(project_dir, project_type.files, legacy_pyproject)
183182
except Exception as e:
184183
print(f"Error creating project file: {str(e)}", file=sys.stderr)
185184
return 1
186185

187186
if project_type == NewProjectTypes.WIDGET_FORM:
188187
print(f'Run "pyside6-project build {project_dir}" to build the project')
189-
print(f'Run "pyside6-project run {project_dir}{os.sep}main.py" to run the project')
188+
print(f'Run "pyside6-project run {project_dir / "main.py"}" to run the project')
190189
return 0

sources/pyside-tools/project_lib/project_data.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@
77
import subprocess
88
import sys
99
from pathlib import Path
10-
from . import (METATYPES_JSON_SUFFIX, PYPROJECT_JSON_PATTERN,
10+
from . import (METATYPES_JSON_SUFFIX, PYPROJECT_JSON_PATTERN, PYPROJECT_TOML_PATTERN,
1111
PYPROJECT_FILE_PATTERNS, TRANSLATION_SUFFIX, qt_metatype_json_dir, MOD_CMD,
1212
QML_IMPORT_MAJOR_VERSION, QML_IMPORT_MINOR_VERSION, QML_IMPORT_NAME, QT_MODULES)
13+
from .pyproject_toml import parse_pyproject_toml
1314
from .pyproject_json import parse_pyproject_json
1415

1516

@@ -40,6 +41,8 @@ def __init__(self, project_file: Path) -> None:
4041

4142
if project_file.match(PYPROJECT_JSON_PATTERN):
4243
project_file_data = parse_pyproject_json(project_file)
44+
elif project_file.match(PYPROJECT_TOML_PATTERN):
45+
project_file_data = parse_pyproject_toml(project_file)
4346
else:
4447
print(f"Unknown project file format: {project_file}", file=sys.stderr)
4548
sys.exit(1)

sources/pyside-tools/project_lib/pyproject_json.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,21 @@
66
from .pyproject_parse_result import PyProjectParseResult
77

88

9+
def write_pyproject_json(pyproject_file: Path, project_files: list[str]):
10+
"""
11+
Create or update a *.pyproject file with the specified content.
12+
13+
:param pyproject_file: The *.pyproject file path to create or update.
14+
:param project_files: The relative paths of the files to include in the project.
15+
"""
16+
# The content of the file is fully replaced, so it is not necessary to read and merge any
17+
# existing content
18+
content = {
19+
"files": sorted(project_files),
20+
}
21+
pyproject_file.write_text(json.dumps(content), encoding="utf-8")
22+
23+
924
def parse_pyproject_json(pyproject_json_file: Path) -> PyProjectParseResult:
1025
"""
1126
Parse a pyproject.json file and return a PyProjectParseResult object.

0 commit comments

Comments
 (0)