pyside-hatch-project 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,8 @@
1
+ *.py[co]
2
+ /.idea/
3
+ /temp/
4
+ __pycache__/
5
+ /.venv/
6
+ /.pytest_cache/
7
+ /.ruff_cache/
8
+ /dist/
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2026 Tomas Karabela
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,112 @@
1
+ Metadata-Version: 2.5
2
+ Name: pyside-hatch-project
3
+ Version: 0.1.0
4
+ Summary: Hatchling build hook and CLI that compiles PySide6 .ui, .ts and .qrc files
5
+ Project-URL: Homepage, https://github.com/tkarabela/pyside-hatch-project
6
+ Project-URL: Issues, https://github.com/tkarabela/pyside-hatch-project/issues
7
+ Author-email: Tomas Karabela <tkarabela@seznam.cz>
8
+ License-Expression: MIT
9
+ License-File: LICENSE.txt
10
+ Keywords: hatch,hatchling,lrelease,lupdate,pyside6,qt,rcc,uic
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Plugins
13
+ Classifier: Framework :: Hatch
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Programming Language :: Python :: 3.15
22
+ Classifier: Topic :: Software Development :: Build Tools
23
+ Classifier: Topic :: Software Development :: User Interfaces
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.12
26
+ Requires-Dist: hatchling>=1.12
27
+ Requires-Dist: pyside6-essentials>=6.6
28
+ Description-Content-Type: text/markdown
29
+
30
+ [![build master branch](https://img.shields.io/github/actions/workflow/status/tkarabela/pyside-hatch-project/ci.yml?branch=master)](https://github.com/tkarabela/pyside-hatch-project/actions)
31
+ [![test code coverage](https://img.shields.io/codecov/c/github/tkarabela/pyside-hatch-project)](https://app.codecov.io/github/tkarabela/pyside-hatch-project)
32
+ [![Static Badge](https://img.shields.io/badge/Pyrefly%20%26%20Ruff-checked-blue?style=flat)](https://github.com/tkarabela/pyside-hatch-project/actions)
33
+ [![PyPI - Version](https://img.shields.io/pypi/v/pyside-hatch-project.svg?style=flat)](https://pypi.org/project/pyside-hatch-project/)
34
+ [![PyPI - Status](https://img.shields.io/pypi/status/pyside-hatch-project.svg?style=flat)](https://pypi.org/project/pyside-hatch-project/)
35
+ [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pyside-hatch-project.svg?style=flat)](https://pypi.org/project/pyside-hatch-project/)
36
+ [![PyPI - License](https://img.shields.io/pypi/l/pyside-hatch-project.svg?style=flat)](LICENSE.txt)
37
+
38
+ # pyside-hatch-project
39
+
40
+ A Hatch-friendly alternative to `pyside6-project` for managing UI files, resource files and translations.
41
+ This library provides a Hatchling plugin as well as stand-alone CLI tool, both called `pyside-hatch-project`.
42
+ It reimplements a subset of `pyside6-project` functionality.
43
+
44
+ What this library gives you over `pyside6-project`:
45
+ - Compile `.ui`/`.ts`/`.qrc` as part of a standard Python build, no extra steps needed.
46
+ - Specify paths to `.ui`/`.ts`/`.qrc` using glob patterns in `pyproject.toml`.
47
+
48
+ If you'd rather just call `pyside6-project` from Hatchling, there is a similar project that does this:
49
+ [hatch-pyside](https://github.com/s-ball/hatch-pyside).
50
+
51
+ ## Usage
52
+
53
+ Example `pyproject.toml`:
54
+
55
+ ```toml
56
+ [project]
57
+ name = "example-project"
58
+
59
+ [dependency-groups]
60
+ dev = [
61
+ "pyside-hatch-project",
62
+ ]
63
+
64
+ [build-system]
65
+ requires = ["hatchling>=1.27", "pyside-hatch-project"]
66
+ build-backend = "hatchling.build"
67
+
68
+ [tool.hatch.build.hooks.pyside-hatch-project]
69
+ ui_files = ["src/**/*.ui"]
70
+ ts_files = ["src/**/*.ts"]
71
+ qrc_files = ["src/**/*.qrc"]
72
+
73
+ # -- If you don't want to use the Hatchling plugin:
74
+ # [tool.pyside-hatch-project]
75
+ # ui_files = ["src/**/*.ui"]
76
+ # ts_files = ["src/**/*.ts"]
77
+ # qrc_files = ["src/**/*.qrc"]
78
+
79
+ # -- If you want to use standard pyside6-project definitions:
80
+ # [tool.hatch.build.hooks.pyside-hatch-project]
81
+ #
82
+ # [tool.pyside6-project]
83
+ # files = [
84
+ # "src/main.py",
85
+ # "src/my_widget.ui",
86
+ # "src/my_translation.ts",
87
+ # "src/my_resource.qrc",
88
+ # ]
89
+ ```
90
+
91
+ From the terminal, you can use `pyside6-project` compatible commands:
92
+
93
+ ```shell
94
+ pyside-hatch-project build # compile .ui/.ts/.qrc files
95
+ pyside-hatch-project lupdate # extract strings from .py/.ui files into .ts files
96
+ pyside-hatch-project clean # remove compilation outputs (ui_*.py, rc_*.py, *.qm)
97
+ ```
98
+
99
+ The compiled files can be `.gitignore`d - the build hook will add them as artifacts explicitly.
100
+
101
+ ## Development
102
+
103
+ ```shell
104
+ uv sync
105
+ uv run pytest # unit and integration tests
106
+ uv run pytest -m e2e # end-to-end builds with pip and uv in fresh environments (slow, needs network)
107
+ uv run ruff check && uv run pyrefly check
108
+ ```
109
+
110
+ ## License
111
+
112
+ MIT – see [LICENSE.txt](./LICENSE.txt).
@@ -0,0 +1,83 @@
1
+ [![build master branch](https://img.shields.io/github/actions/workflow/status/tkarabela/pyside-hatch-project/ci.yml?branch=master)](https://github.com/tkarabela/pyside-hatch-project/actions)
2
+ [![test code coverage](https://img.shields.io/codecov/c/github/tkarabela/pyside-hatch-project)](https://app.codecov.io/github/tkarabela/pyside-hatch-project)
3
+ [![Static Badge](https://img.shields.io/badge/Pyrefly%20%26%20Ruff-checked-blue?style=flat)](https://github.com/tkarabela/pyside-hatch-project/actions)
4
+ [![PyPI - Version](https://img.shields.io/pypi/v/pyside-hatch-project.svg?style=flat)](https://pypi.org/project/pyside-hatch-project/)
5
+ [![PyPI - Status](https://img.shields.io/pypi/status/pyside-hatch-project.svg?style=flat)](https://pypi.org/project/pyside-hatch-project/)
6
+ [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pyside-hatch-project.svg?style=flat)](https://pypi.org/project/pyside-hatch-project/)
7
+ [![PyPI - License](https://img.shields.io/pypi/l/pyside-hatch-project.svg?style=flat)](LICENSE.txt)
8
+
9
+ # pyside-hatch-project
10
+
11
+ A Hatch-friendly alternative to `pyside6-project` for managing UI files, resource files and translations.
12
+ This library provides a Hatchling plugin as well as stand-alone CLI tool, both called `pyside-hatch-project`.
13
+ It reimplements a subset of `pyside6-project` functionality.
14
+
15
+ What this library gives you over `pyside6-project`:
16
+ - Compile `.ui`/`.ts`/`.qrc` as part of a standard Python build, no extra steps needed.
17
+ - Specify paths to `.ui`/`.ts`/`.qrc` using glob patterns in `pyproject.toml`.
18
+
19
+ If you'd rather just call `pyside6-project` from Hatchling, there is a similar project that does this:
20
+ [hatch-pyside](https://github.com/s-ball/hatch-pyside).
21
+
22
+ ## Usage
23
+
24
+ Example `pyproject.toml`:
25
+
26
+ ```toml
27
+ [project]
28
+ name = "example-project"
29
+
30
+ [dependency-groups]
31
+ dev = [
32
+ "pyside-hatch-project",
33
+ ]
34
+
35
+ [build-system]
36
+ requires = ["hatchling>=1.27", "pyside-hatch-project"]
37
+ build-backend = "hatchling.build"
38
+
39
+ [tool.hatch.build.hooks.pyside-hatch-project]
40
+ ui_files = ["src/**/*.ui"]
41
+ ts_files = ["src/**/*.ts"]
42
+ qrc_files = ["src/**/*.qrc"]
43
+
44
+ # -- If you don't want to use the Hatchling plugin:
45
+ # [tool.pyside-hatch-project]
46
+ # ui_files = ["src/**/*.ui"]
47
+ # ts_files = ["src/**/*.ts"]
48
+ # qrc_files = ["src/**/*.qrc"]
49
+
50
+ # -- If you want to use standard pyside6-project definitions:
51
+ # [tool.hatch.build.hooks.pyside-hatch-project]
52
+ #
53
+ # [tool.pyside6-project]
54
+ # files = [
55
+ # "src/main.py",
56
+ # "src/my_widget.ui",
57
+ # "src/my_translation.ts",
58
+ # "src/my_resource.qrc",
59
+ # ]
60
+ ```
61
+
62
+ From the terminal, you can use `pyside6-project` compatible commands:
63
+
64
+ ```shell
65
+ pyside-hatch-project build # compile .ui/.ts/.qrc files
66
+ pyside-hatch-project lupdate # extract strings from .py/.ui files into .ts files
67
+ pyside-hatch-project clean # remove compilation outputs (ui_*.py, rc_*.py, *.qm)
68
+ ```
69
+
70
+ The compiled files can be `.gitignore`d - the build hook will add them as artifacts explicitly.
71
+
72
+ ## Development
73
+
74
+ ```shell
75
+ uv sync
76
+ uv run pytest # unit and integration tests
77
+ uv run pytest -m e2e # end-to-end builds with pip and uv in fresh environments (slow, needs network)
78
+ uv run ruff check && uv run pyrefly check
79
+ ```
80
+
81
+ ## License
82
+
83
+ MIT – see [LICENSE.txt](./LICENSE.txt).
@@ -0,0 +1,84 @@
1
+ [project]
2
+ name = "pyside-hatch-project"
3
+ dynamic = ["version"]
4
+ description = "Hatchling build hook and CLI that compiles PySide6 .ui, .ts and .qrc files"
5
+ requires-python = ">=3.12"
6
+ readme = "README.md"
7
+ license = "MIT"
8
+ license-files = ["LICENSE.txt"]
9
+ authors = [
10
+ { name = "Tomas Karabela", email = "tkarabela@seznam.cz" },
11
+ ]
12
+ keywords = ["hatch", "hatchling", "pyside6", "qt", "uic", "rcc", "lrelease", "lupdate"]
13
+ classifiers = [
14
+ "Development Status :: 3 - Alpha",
15
+ "Environment :: Plugins",
16
+ "Framework :: Hatch",
17
+ "Intended Audience :: Developers",
18
+ "Operating System :: OS Independent",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3 :: Only",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ "Programming Language :: Python :: 3.14",
24
+ "Programming Language :: Python :: 3.15",
25
+ "Topic :: Software Development :: Build Tools",
26
+ "Topic :: Software Development :: User Interfaces",
27
+ "Typing :: Typed",
28
+ ]
29
+ dependencies = [
30
+ # 1.12 is the first release where BuildHookInterface is Generic (subscripted at runtime)
31
+ "hatchling>=1.12",
32
+ # 6.6 is the first release installable on Python 3.12
33
+ "pyside6-essentials>=6.6",
34
+ ]
35
+
36
+ [project.urls]
37
+ Homepage = "https://github.com/tkarabela/pyside-hatch-project"
38
+ Issues = "https://github.com/tkarabela/pyside-hatch-project/issues"
39
+
40
+ [project.scripts]
41
+ pyside-hatch-project = "pyside_hatch_project:main"
42
+
43
+ [project.entry-points.hatch]
44
+ pyside-hatch-project = "pyside_hatch_project"
45
+
46
+ [dependency-groups]
47
+ dev = [
48
+ "pyrefly>=1.4.0",
49
+ "pytest>=8.4",
50
+ "pytest-cov>=7.1.0",
51
+ "ruff>=0.17.1",
52
+ ]
53
+
54
+ [build-system]
55
+ requires = ["hatchling>=1.27", "hatch-vcs>=0.5"]
56
+ build-backend = "hatchling.build"
57
+
58
+ [tool.hatch.version]
59
+ source = "vcs"
60
+
61
+ [tool.hatch.build.targets.sdist]
62
+ exclude = ["/.github"]
63
+
64
+ [tool.uv]
65
+ # Rebuild the editable install when the commit or tags change, so the version stays current
66
+ cache-keys = [{ file = "pyproject.toml" }, { git = { commit = true, tags = true } }]
67
+
68
+ [tool.pyrefly]
69
+ preset = "strict"
70
+ project-excludes = ["tests/dummy_project/**"]
71
+
72
+ [tool.pytest.ini_options]
73
+ testpaths = ["tests"]
74
+ addopts = ["-m", "not e2e", "--strict-markers"]
75
+ markers = [
76
+ "e2e: end-to-end builds in fresh virtual environments (slow, needs network)",
77
+ ]
78
+
79
+ [tool.coverage.run]
80
+ branch = true
81
+
82
+ [tool.coverage.report]
83
+ show_missing = true
84
+ skip_covered = true
@@ -0,0 +1,254 @@
1
+ import argparse
2
+ import logging
3
+ import shutil
4
+ import subprocess
5
+ import sys
6
+ import sysconfig
7
+ import tomllib
8
+ from collections.abc import Iterable
9
+ from dataclasses import dataclass, field
10
+ from enum import StrEnum
11
+ from importlib.metadata import version
12
+ from pathlib import Path
13
+ from typing import Any, NoReturn, override
14
+
15
+ from hatchling.bridge.app import Application
16
+ from hatchling.builders.hooks.plugin.interface import BuildHookInterface
17
+ from hatchling.plugin import hookimpl
18
+
19
+ __version__ = version("pyside-hatch-project")
20
+ HATCH_PLUGIN_NAME = "pyside-hatch-project"
21
+ logger = logging.getLogger(HATCH_PLUGIN_NAME)
22
+
23
+
24
+ def _traverse_dict(d: dict[str, Any], keys: tuple[str, ...]) -> dict[str, Any]:
25
+ for key in keys:
26
+ d = d.get(key, {})
27
+ return d
28
+
29
+
30
+ def _ui_output_path(path: Path) -> Path:
31
+ return path.with_name(f"ui_{path.stem}.py")
32
+
33
+
34
+ def _ts_output_path(path: Path) -> Path:
35
+ return path.with_suffix(".qm")
36
+
37
+
38
+ def _qrc_output_path(path: Path) -> Path:
39
+ return path.with_name(f"rc_{path.stem}.py")
40
+
41
+
42
+ @dataclass
43
+ class Config:
44
+ files: list[Path] = field(default_factory=list)
45
+
46
+ def add(self, files: Iterable[Path]) -> None:
47
+ registered = set(self.files)
48
+
49
+ for path in files:
50
+ if path.suffix in (".ui", ".ts", ".qrc") and path not in registered:
51
+ registered.add(path)
52
+ self.files.append(path)
53
+
54
+ @property
55
+ def ui_files(self) -> Iterable[Path]:
56
+ return (path for path in self.files if path.suffix == ".ui")
57
+
58
+ @property
59
+ def ts_files(self) -> Iterable[Path]:
60
+ return (path for path in self.files if path.suffix == ".ts")
61
+
62
+ @property
63
+ def qrc_files(self) -> Iterable[Path]:
64
+ return (path for path in self.files if path.suffix == ".qrc")
65
+
66
+
67
+ class Project:
68
+ def __init__(self, root: Path) -> None:
69
+ self.root = root
70
+ self.config = self.read_config(root)
71
+
72
+ @staticmethod
73
+ def read_config(root: Path) -> Config:
74
+ config = Config()
75
+ pyproject = tomllib.loads(root.joinpath("pyproject.toml").read_text(encoding="utf-8"))
76
+
77
+ # [tool.pyside6-project]
78
+ config.add(
79
+ root.joinpath(relpath)
80
+ for relpath in _traverse_dict(pyproject, ("tool", "pyside6-project")).get("files", [])
81
+ )
82
+
83
+ # [tool.pyside-hatch-project]
84
+ # [tool.hatch.build.hooks.pyside-hatch-project]
85
+ for toml_path in (
86
+ ("tool", "pyside-hatch-project"),
87
+ ("tool", "hatch", "build", "hooks", HATCH_PLUGIN_NAME),
88
+ ):
89
+ for attribute in ("ui_files", "ts_files", "qrc_files"):
90
+ for relpath in _traverse_dict(pyproject, toml_path).get(attribute, []):
91
+ config.add(root.glob(relpath))
92
+
93
+ return config
94
+
95
+ def list_output_files(self) -> Iterable[Path]:
96
+ yield from (_ui_output_path(path) for path in self.config.ui_files)
97
+ yield from (_ts_output_path(path) for path in self.config.ts_files)
98
+ yield from (_qrc_output_path(path) for path in self.config.qrc_files)
99
+
100
+ def list_python_sources(self) -> Iterable[Path]:
101
+ src_dir = self.root.joinpath("src")
102
+ source_dir = src_dir if src_dir.exists() else self.root
103
+ return (
104
+ path
105
+ for path in sorted(source_dir.rglob("*.py"))
106
+ if not path.name.startswith(("ui_", "rc_")) and "__pycache__" not in path.parts
107
+ )
108
+
109
+ def compile_input_files(self) -> Iterable[Path]:
110
+ if ui_files := list(self.config.ui_files):
111
+ self.info(f"Compiling {len(ui_files)} .ui files")
112
+ for path in ui_files:
113
+ output_path = _ui_output_path(path)
114
+ self.run_tool("pyside6-uic", path, "-o", output_path)
115
+ yield output_path
116
+
117
+ if ts_files := list(self.config.ts_files):
118
+ self.info(f"Compiling {len(ts_files)} .ts files")
119
+ for path in ts_files:
120
+ output_path = _ts_output_path(path)
121
+ self.run_tool("pyside6-lrelease", path, "-qm", output_path)
122
+ yield output_path
123
+
124
+ # compile .qrc files last, in case they reference compiled translations (.qm files)
125
+ if qrc_files := list(self.config.qrc_files):
126
+ self.info(f"Compiling {len(qrc_files)} .qrc files")
127
+ for path in qrc_files:
128
+ output_path = _qrc_output_path(path)
129
+ self.run_tool("pyside6-rcc", path, "-o", output_path)
130
+ yield output_path
131
+
132
+ def clean_compiled_files(self) -> None:
133
+ for path in self.list_output_files():
134
+ path.unlink(missing_ok=True)
135
+
136
+ def update_translations(self) -> None:
137
+ if not (ts_files := list(self.config.ts_files)):
138
+ return
139
+
140
+ self.info(f"Updating {len(ts_files)} .ts files")
141
+
142
+ input_paths = [*self.config.ui_files, *self.list_python_sources()]
143
+
144
+ for ts in self.config.ts_files:
145
+ self.run_tool("pyside6-lupdate", *input_paths, "-ts", ts)
146
+
147
+ def run_tool(self, tool_name: str, *args: str | Path) -> None:
148
+ # prefer tools installed alongside the running interpreter, but fall back to PATH: pip's isolated
149
+ # build environment is not a venv, its scripts directory is only made available through PATH
150
+ tool = shutil.which(tool_name, path=sysconfig.get_path("scripts")) or shutil.which(tool_name)
151
+ if tool is None:
152
+ self.fail(f"{tool_name} not found (is pyside6-essentials installed?)")
153
+ cmd = [tool, *map(str, args)]
154
+ self.debug(f"Running: {cmd}")
155
+ rv = subprocess.run(cmd, check=False).returncode
156
+ if rv != 0:
157
+ self.fail(f"{' '.join(cmd)} failed with exit code {rv}")
158
+
159
+ def info(self, msg: str) -> None:
160
+ logger.info(msg)
161
+
162
+ def debug(self, msg: str) -> None:
163
+ logger.debug(msg)
164
+
165
+ def fail(self, msg: str) -> NoReturn:
166
+ logger.fatal(msg)
167
+ raise RuntimeError(msg)
168
+
169
+
170
+ class HatchProject(Project):
171
+ def __init__(self, hook: BuildHookInterface[Any]) -> None:
172
+ self.hook = hook
173
+ self.app: Application = hook.app
174
+ super().__init__(root=Path(hook.root))
175
+
176
+ @override
177
+ def info(self, msg: str) -> None:
178
+ self.app.display_info(msg)
179
+
180
+ @override
181
+ def debug(self, msg: str) -> None:
182
+ self.app.display_debug(msg)
183
+
184
+ @override
185
+ def fail(self, msg: str) -> NoReturn:
186
+ self.app.abort(msg)
187
+ raise SystemExit(1) # not used, Application.abort() is just missing NoReturn annotation
188
+
189
+
190
+ class PysideHatchProjectBuildHook(BuildHookInterface[Any]):
191
+ PLUGIN_NAME = HATCH_PLUGIN_NAME
192
+
193
+ @override
194
+ def clean(self, versions: list[str]) -> None:
195
+ self.__project.clean_compiled_files()
196
+
197
+ @override
198
+ def initialize(self, version: str, build_data: dict[str, Any]) -> None:
199
+ project = self.__project
200
+ output_files = list(project.compile_input_files())
201
+ # artifacts are gitignore-style patterns relative to the project root, anchored to match only these files
202
+ build_data["artifacts"].extend(f"/{path.relative_to(project.root).as_posix()}" for path in output_files)
203
+
204
+ @property
205
+ def __project(self) -> HatchProject:
206
+ return HatchProject(self)
207
+
208
+
209
+ class _Command(StrEnum):
210
+ BUILD = "build"
211
+ CLEAN = "clean"
212
+ LUPDATE = "lupdate"
213
+
214
+
215
+ class CLI:
216
+ def __init__(self) -> None:
217
+ self.parser = argparse.ArgumentParser()
218
+ self.parser.add_argument("command", choices=[_Command.BUILD, _Command.CLEAN, _Command.LUPDATE])
219
+ self.parser.add_argument("-v", "--verbose", action="store_true", default=False)
220
+ self.parser.add_argument("-V", "--version", action="version", version=__version__)
221
+
222
+ def run(self, argv: list[str]) -> int:
223
+ args = self.parser.parse_args(argv)
224
+ command = _Command(args.command)
225
+ verbose: bool = args.verbose
226
+
227
+ logging.basicConfig(level=logging.DEBUG if verbose else logging.INFO)
228
+ project = Project(Path.cwd().resolve())
229
+
230
+ try:
231
+ match command:
232
+ case _Command.BUILD:
233
+ _ = list(project.compile_input_files())
234
+ case _Command.CLEAN:
235
+ project.clean_compiled_files()
236
+ case _Command.LUPDATE:
237
+ project.update_translations()
238
+ return 0
239
+ except Exception:
240
+ logger.exception("pyside-hatch-project failed")
241
+ return 1
242
+
243
+
244
+ @hookimpl
245
+ def hatch_register_build_hook():
246
+ return PysideHatchProjectBuildHook
247
+
248
+
249
+ def main() -> int:
250
+ return CLI().run(sys.argv[1:])
251
+
252
+
253
+ if __name__ == "__main__":
254
+ sys.exit(main())
File without changes
@@ -0,0 +1,14 @@
1
+ import shutil
2
+ from pathlib import Path
3
+
4
+ import pytest
5
+
6
+ from tests.util import DUMMY_PROJECT
7
+
8
+
9
+ @pytest.fixture
10
+ def dummy_project(tmp_path: Path) -> Path:
11
+ """Fresh copy of the dummy project, so that compiled outputs never land in the repository."""
12
+ root = tmp_path / "dummy_project"
13
+ shutil.copytree(DUMMY_PROJECT, root)
14
+ return root
@@ -0,0 +1,4 @@
1
+ # compiled by pyside-hatch-project; they must still end up in the built distributions
2
+ ui_*.py
3
+ rc_*.py
4
+ *.qm
@@ -0,0 +1,17 @@
1
+ [project]
2
+ name = "dummy-app"
3
+ version = "0.1.0"
4
+ requires-python = ">=3.12"
5
+ dependencies = ["pyside6-essentials"]
6
+
7
+ [build-system]
8
+ requires = ["hatchling", "pyside-hatch-project"]
9
+ build-backend = "hatchling.build"
10
+
11
+ [tool.hatch.build.targets.wheel]
12
+ packages = ["src/dummy_app"]
13
+
14
+ [tool.hatch.build.hooks.pyside-hatch-project]
15
+ ui_files = ["src/**/*.ui"]
16
+ ts_files = ["src/**/*.ts"]
17
+ qrc_files = ["src/**/*.qrc"]
@@ -0,0 +1,6 @@
1
+ from PySide6.QtCore import QObject
2
+
3
+
4
+ class MainWindow(QObject):
5
+ def window_title(self) -> str:
6
+ return self.tr("Window title from Python")
@@ -0,0 +1,27 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <ui version="4.0">
3
+ <class>MainWindow</class>
4
+ <widget class="QMainWindow" name="MainWindow">
5
+ <property name="geometry">
6
+ <rect>
7
+ <x>0</x>
8
+ <y>0</y>
9
+ <width>320</width>
10
+ <height>240</height>
11
+ </rect>
12
+ </property>
13
+ <widget class="QWidget" name="centralwidget">
14
+ <layout class="QVBoxLayout" name="verticalLayout">
15
+ <item>
16
+ <widget class="QLabel" name="label">
17
+ <property name="text">
18
+ <string>Hello</string>
19
+ </property>
20
+ </widget>
21
+ </item>
22
+ </layout>
23
+ </widget>
24
+ </widget>
25
+ <resources/>
26
+ <connections/>
27
+ </ui>
@@ -0,0 +1,6 @@
1
+ <!DOCTYPE RCC>
2
+ <RCC version="1.0">
3
+ <qresource>
4
+ <file>translations/app_cs.qm</file>
5
+ </qresource>
6
+ </RCC>
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!DOCTYPE TS>
3
+ <TS version="2.1" language="cs_CZ">
4
+ <context>
5
+ <name>MainWindow</name>
6
+ <message>
7
+ <source>Hello</source>
8
+ <translation>Ahoj</translation>
9
+ </message>
10
+ </context>
11
+ </TS>