qgis-plugin-dev-tools 0.7.0__tar.gz → 0.9.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.
Files changed (41) hide show
  1. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/CHANGELOG.md +10 -0
  2. {qgis_plugin_dev_tools-0.7.0/src/qgis_plugin_dev_tools.egg-info → qgis_plugin_dev_tools-0.9.0}/PKG-INFO +21 -1
  3. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/README.md +10 -0
  4. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/src/qgis_plugin_dev_tools/__init__.py +1 -1
  5. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/src/qgis_plugin_dev_tools/build/__init__.py +2 -0
  6. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/src/qgis_plugin_dev_tools/build/packaging.py +53 -0
  7. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/src/qgis_plugin_dev_tools/cli/__init__.py +2 -0
  8. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/src/qgis_plugin_dev_tools/config/__init__.py +12 -0
  9. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/src/qgis_plugin_dev_tools/config/dotenv.py +7 -1
  10. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/src/qgis_plugin_dev_tools/config/pyproject.py +2 -1
  11. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/src/qgis_plugin_dev_tools/start/__init__.py +2 -0
  12. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/src/qgis_plugin_dev_tools/start/config.py +2 -0
  13. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/src/qgis_plugin_dev_tools/start/launch.py +12 -0
  14. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0/src/qgis_plugin_dev_tools.egg-info}/PKG-INFO +21 -1
  15. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/test/test_build.py +105 -16
  16. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/test/test_read_distributions.py +0 -2
  17. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/test/test_read_dotenv.py +2 -0
  18. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/LICENSE +0 -0
  19. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/MANIFEST.in +0 -0
  20. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/pyproject.toml +0 -0
  21. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/setup.cfg +0 -0
  22. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/setup.py +0 -0
  23. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/src/qgis_plugin_dev_tools/__main__.py +0 -0
  24. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/src/qgis_plugin_dev_tools/build/changelog_parser.py +0 -0
  25. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/src/qgis_plugin_dev_tools/build/distribution.py +0 -0
  26. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/src/qgis_plugin_dev_tools/build/metadata.py +0 -0
  27. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/src/qgis_plugin_dev_tools/build/rewrite_imports.py +0 -0
  28. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/src/qgis_plugin_dev_tools/publish/__init__.py +0 -0
  29. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/src/qgis_plugin_dev_tools/py.typed +0 -0
  30. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/src/qgis_plugin_dev_tools/start/bootstrap/__init__.py +0 -0
  31. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/src/qgis_plugin_dev_tools/start/bootstrap/template.py +0 -0
  32. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/src/qgis_plugin_dev_tools/start/daemon_server.py +0 -0
  33. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/src/qgis_plugin_dev_tools/utils/__init__.py +0 -0
  34. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/src/qgis_plugin_dev_tools/utils/distributions.py +0 -0
  35. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/src/qgis_plugin_dev_tools.egg-info/SOURCES.txt +0 -0
  36. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/src/qgis_plugin_dev_tools.egg-info/dependency_links.txt +0 -0
  37. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/src/qgis_plugin_dev_tools.egg-info/entry_points.txt +0 -0
  38. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/src/qgis_plugin_dev_tools.egg-info/requires.txt +0 -0
  39. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/src/qgis_plugin_dev_tools.egg-info/top_level.txt +0 -0
  40. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/test/test_read_pyproject.py +0 -0
  41. {qgis_plugin_dev_tools-0.7.0 → qgis_plugin_dev_tools-0.9.0}/test/test_rewrite_imports.py +0 -0
@@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.9.0] - 2025-01-08
8
+
9
+ - Feat: Copy license into the plugin zip while deploying
10
+
11
+ ## [0.8.0] - 2024-08-23
12
+
13
+ - Feat: Add locale and ui ini options to be able to further customize development environment
14
+
7
15
  ## [0.7.0] - 2024-05-21
8
16
 
9
17
  - Fix: Bundle contents by parsing pep-compliant distribution file catalog instead of possibly missing tool-specific top-level.txt
@@ -72,3 +80,5 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
72
80
  [0.6.1]: https://github.com/nlsfi/qgis-plugin-dev-tools/releases/tag/v0.6.1
73
81
  [0.6.2]: https://github.com/nlsfi/qgis-plugin-dev-tools/releases/tag/v0.6.2
74
82
  [0.7.0]: https://github.com/nlsfi/qgis-plugin-dev-tools/releases/tag/v0.7.0
83
+ [0.8.0]: https://github.com/nlsfi/qgis-plugin-dev-tools/releases/tag/v0.8.0
84
+ [0.9.0]: https://github.com/nlsfi/qgis-plugin-dev-tools/releases/tag/v0.9.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: qgis-plugin-dev-tools
3
- Version: 0.7.0
3
+ Version: 0.9.0
4
4
  Summary: QGIS plugin development and packaging tools, which make managing runtime dependencies easy.
5
5
  Home-page: https://github.com/nlsfi/qgis-plugin-dev-tools
6
6
  Author: National Land Survey of Finland
@@ -91,6 +91,14 @@ plugin_package_name = "your_plugin_package_name"
91
91
  version_number_source = "distribution" # or "changelog" (default if missing)
92
92
  ```
93
93
 
94
+ QGIS plugins are required to have a LICENSE file included in the plugin package. However, usually it is more convenient to keep the license file in the root of the repository. LICENSE can be copied automatically to the plugin zip while packaging if such file is found. A relative path to the license file can also be configured with `license_file_path` option.
95
+
96
+ ```toml
97
+ [tool.qgis_plugin_dev_tools]
98
+ plugin_package_name = "your_plugin_package_name"
99
+ license_file_path = "docs/my-license.md"
100
+ ```
101
+
94
102
  ## Plugin packaging
95
103
 
96
104
  Run `qgis-plugin-dev-tools build` (short `qpdt b`) to package the plugin and any runtime dependencies to a standard QGIS plugin zip file, that can be installed and published.
@@ -113,6 +121,8 @@ By default config is read from `pyproject.toml` and runtime config from `.env` i
113
121
  QGIS_EXECUTABLE_PATH= # path to qgis-bin/qgis-bin-ltr or .exe equivalents, necessary
114
122
  # DEBUGGER_LIBRARY= # debugpy/pydevd to start a debugger on init, library must be installed to the environment
115
123
  # DEVELOPMENT_PROFILE_NAME= # name of the profile that qgis is launched with, otherwise uses default
124
+ # QGIS_LOCALE= # locale code of QGIS, otherwise uses default
125
+ # QGIS_GUI_INI= # path to ini file containing QGIS UI customizations
116
126
 
117
127
  # any other variables are added to the runtime QGIS environment
118
128
  # SOMETHING=something
@@ -165,6 +175,14 @@ All notable changes to this project will be documented in this file.
165
175
 
166
176
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
167
177
 
178
+ ## [0.9.0] - 2025-01-08
179
+
180
+ - Feat: Copy license into the plugin zip while deploying
181
+
182
+ ## [0.8.0] - 2024-08-23
183
+
184
+ - Feat: Add locale and ui ini options to be able to further customize development environment
185
+
168
186
  ## [0.7.0] - 2024-05-21
169
187
 
170
188
  - Fix: Bundle contents by parsing pep-compliant distribution file catalog instead of possibly missing tool-specific top-level.txt
@@ -233,3 +251,5 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
233
251
  [0.6.1]: https://github.com/nlsfi/qgis-plugin-dev-tools/releases/tag/v0.6.1
234
252
  [0.6.2]: https://github.com/nlsfi/qgis-plugin-dev-tools/releases/tag/v0.6.2
235
253
  [0.7.0]: https://github.com/nlsfi/qgis-plugin-dev-tools/releases/tag/v0.7.0
254
+ [0.8.0]: https://github.com/nlsfi/qgis-plugin-dev-tools/releases/tag/v0.8.0
255
+ [0.9.0]: https://github.com/nlsfi/qgis-plugin-dev-tools/releases/tag/v0.9.0
@@ -61,6 +61,14 @@ plugin_package_name = "your_plugin_package_name"
61
61
  version_number_source = "distribution" # or "changelog" (default if missing)
62
62
  ```
63
63
 
64
+ QGIS plugins are required to have a LICENSE file included in the plugin package. However, usually it is more convenient to keep the license file in the root of the repository. LICENSE can be copied automatically to the plugin zip while packaging if such file is found. A relative path to the license file can also be configured with `license_file_path` option.
65
+
66
+ ```toml
67
+ [tool.qgis_plugin_dev_tools]
68
+ plugin_package_name = "your_plugin_package_name"
69
+ license_file_path = "docs/my-license.md"
70
+ ```
71
+
64
72
  ## Plugin packaging
65
73
 
66
74
  Run `qgis-plugin-dev-tools build` (short `qpdt b`) to package the plugin and any runtime dependencies to a standard QGIS plugin zip file, that can be installed and published.
@@ -83,6 +91,8 @@ By default config is read from `pyproject.toml` and runtime config from `.env` i
83
91
  QGIS_EXECUTABLE_PATH= # path to qgis-bin/qgis-bin-ltr or .exe equivalents, necessary
84
92
  # DEBUGGER_LIBRARY= # debugpy/pydevd to start a debugger on init, library must be installed to the environment
85
93
  # DEVELOPMENT_PROFILE_NAME= # name of the profile that qgis is launched with, otherwise uses default
94
+ # QGIS_LOCALE= # locale code of QGIS, otherwise uses default
95
+ # QGIS_GUI_INI= # path to ini file containing QGIS UI customizations
86
96
 
87
97
  # any other variables are added to the runtime QGIS environment
88
98
  # SOMETHING=something
@@ -20,7 +20,7 @@
20
20
  import logging
21
21
  import sys
22
22
 
23
- __version__ = "0.7.0"
23
+ __version__ = "0.9.0"
24
24
 
25
25
  LOGGER = logging.getLogger(__name__)
26
26
  _handler = logging.StreamHandler(sys.stdout)
@@ -35,6 +35,7 @@ from qgis_plugin_dev_tools.build.metadata import update_metadata_file
35
35
  from qgis_plugin_dev_tools.build.packaging import (
36
36
  copy_plugin_code,
37
37
  copy_runtime_requirements,
38
+ copy_license,
38
39
  )
39
40
  from qgis_plugin_dev_tools.config import DevToolsConfig, VersionNumberSource
40
41
 
@@ -84,6 +85,7 @@ def make_plugin_zip(
84
85
  changelog_contents,
85
86
  )
86
87
  copy_runtime_requirements(dev_tools_config, build_directory_path)
88
+ copy_license(dev_tools_config, build_directory_path)
87
89
 
88
90
  LOGGER.debug("creating built plugin zip file from build directory")
89
91
 
@@ -20,6 +20,8 @@
20
20
  import logging
21
21
  import shutil
22
22
  import sys
23
+ from typing import Generator, Optional
24
+
23
25
  from importlib_metadata import Distribution
24
26
  from pathlib import Path
25
27
 
@@ -133,6 +135,45 @@ def copy_runtime_requirements(
133
135
  )
134
136
 
135
137
 
138
+ def copy_license(dev_tools_config: DevToolsConfig, build_directory_path: Path) -> None:
139
+ plugin_build_path = build_directory_path / dev_tools_config.plugin_package_name
140
+ target_license_file = plugin_build_path / "LICENSE"
141
+ license_file = dev_tools_config.license_file_path
142
+ if license_file is not None:
143
+ if not license_file.exists():
144
+ LOGGER.error(
145
+ f"Configured license file "
146
+ f"{license_file} "
147
+ f"does not exist. Check the configuration."
148
+ )
149
+ return
150
+
151
+ if target_license_file.exists():
152
+ LOGGER.warning(
153
+ f"Overwriting existing license {target_license_file} "
154
+ f"with configured license {license_file}"
155
+ )
156
+ LOGGER.debug(f"Copying license file {license_file} to {build_directory_path}")
157
+ shutil.copy(license_file, target_license_file)
158
+ return
159
+
160
+ if target_license_file.exists():
161
+ LOGGER.debug(f"Existing license file {target_license_file} found.")
162
+ return
163
+
164
+ # Try finding the license
165
+ license_file = _find_existing_license_file(dev_tools_config.pyproject_path)
166
+ if not license_file:
167
+ LOGGER.warning(
168
+ "Cannot copy LICENSE file since it does not exist. "
169
+ "Configure valid LICENSE file with license_file_path in pyproject.toml"
170
+ )
171
+ return
172
+
173
+ LOGGER.debug(f"Copying license file {license_file} to {build_directory_path}")
174
+ shutil.copy(license_file, target_license_file)
175
+
176
+
136
177
  def _copy_distribution_files(
137
178
  distribution: Distribution,
138
179
  top_level_names: set[str],
@@ -190,3 +231,15 @@ def _copy_distribution_files(
190
231
  src=original_path,
191
232
  dst=new_path,
192
233
  )
234
+
235
+
236
+ def _find_existing_license_file(search_path: Path) -> Optional[Path]:
237
+ def generate_license_candidates() -> Generator[Path, None, None]:
238
+ for base_name in ("LICENSE", "license"):
239
+ for extension in ("", ".md", ".MD"):
240
+ yield search_path / f"{base_name}{extension}"
241
+
242
+ for potential_path in generate_license_candidates():
243
+ if potential_path.exists():
244
+ return potential_path
245
+ return None
@@ -54,6 +54,8 @@ def start(dotenv_file_paths: list[Path]) -> None:
54
54
  DevelopmentModeConfig(
55
55
  qgis_executable_path=dotenv_config.QGIS_EXECUTABLE_PATH,
56
56
  profile_name=dotenv_config.DEVELOPMENT_PROFILE_NAME,
57
+ locale=dotenv_config.QGIS_LOCALE,
58
+ ui_ini=dotenv_config.QGIS_GUI_INI,
57
59
  runtime_environment=dotenv_config.runtime_environment,
58
60
  runtime_library_paths=[Path(p) for p in sys.path],
59
61
  plugin_package_path=dev_tools_config.plugin_package_path,
@@ -21,6 +21,7 @@ from collections import ChainMap
21
21
  from enum import Enum, auto
22
22
  from importlib.util import find_spec
23
23
  from pathlib import Path
24
+ from typing import Optional
24
25
 
25
26
  from importlib_metadata import Distribution, distribution
26
27
  from packaging.requirements import Requirement
@@ -42,6 +43,7 @@ class VersionNumberSource(Enum):
42
43
 
43
44
 
44
45
  class DevToolsConfig:
46
+ pyproject_path: Path
45
47
  plugin_package_name: str
46
48
  plugin_package_path: Path
47
49
  runtime_distributions: list[Distribution]
@@ -49,9 +51,11 @@ class DevToolsConfig:
49
51
  append_distributions_to_path: bool
50
52
  version_number_source: VersionNumberSource
51
53
  disabled_extra_plugins: list[str]
54
+ license_file_path: Optional[Path]
52
55
 
53
56
  def __init__( # noqa: PLR0913
54
57
  self,
58
+ pyproject_path: Path,
55
59
  plugin_package_name: str,
56
60
  runtime_requires: list[str],
57
61
  changelog_file_path: Path,
@@ -59,6 +63,7 @@ class DevToolsConfig:
59
63
  auto_add_recursive_runtime_dependencies: bool,
60
64
  version_number_source: VersionNumberSource,
61
65
  disabled_extra_plugins: list[str],
66
+ license_file_path: Optional[Path],
62
67
  ) -> None:
63
68
  plugin_package_spec = find_spec(plugin_package_name)
64
69
  if plugin_package_spec is None or plugin_package_spec.origin is None:
@@ -66,6 +71,7 @@ class DevToolsConfig:
66
71
  f"could not find {plugin_package_name=} in the current environment"
67
72
  )
68
73
 
74
+ self.pyproject_path = pyproject_path
69
75
  self.plugin_package_path = Path(plugin_package_spec.origin).parent
70
76
  self.plugin_package_name = plugin_package_name
71
77
  # TODO: check versions are satisfied?
@@ -77,6 +83,7 @@ class DevToolsConfig:
77
83
  self.version_number_source = version_number_source
78
84
  self.extra_runtime_distributions = []
79
85
  self.disabled_extra_plugins = disabled_extra_plugins
86
+ self.license_file_path = license_file_path
80
87
 
81
88
  if auto_add_recursive_runtime_dependencies:
82
89
  # Add the requirements of the distributions as well
@@ -98,6 +105,7 @@ class DevToolsConfig:
98
105
  def from_pyproject_config(pyproject_file_path: Path) -> "DevToolsConfig":
99
106
  pyproject_config = read_pyproject_config(pyproject_file_path)
100
107
  return DevToolsConfig(
108
+ pyproject_path=pyproject_file_path,
101
109
  plugin_package_name=pyproject_config.plugin_package_name,
102
110
  runtime_requires=pyproject_config.runtime_requires,
103
111
  # TODO: allow setting path in pyproject file?
@@ -112,4 +120,8 @@ class DevToolsConfig:
112
120
  pyproject_config.version_number_source
113
121
  ),
114
122
  disabled_extra_plugins=pyproject_config.disabled_extra_plugins,
123
+ license_file_path=pyproject_file_path.parent
124
+ / pyproject_config.license_file_path
125
+ if pyproject_config.license_file_path
126
+ else None,
115
127
  )
@@ -34,14 +34,18 @@ class DotenvConfig:
34
34
  QGIS_EXECUTABLE_PATH: Path
35
35
  DEBUGGER_LIBRARY: Optional[str]
36
36
  DEVELOPMENT_PROFILE_NAME: Optional[str]
37
+ QGIS_LOCALE: Optional[str]
38
+ QGIS_GUI_INI: Optional[str]
37
39
  runtime_environment: dict[str, str]
38
40
 
39
- def __init__(
41
+ def __init__( # noqa: PLR0913
40
42
  self,
41
43
  *,
42
44
  QGIS_EXECUTABLE_PATH: str, # noqa: N803
43
45
  DEBUGGER_LIBRARY: Optional[str] = None, # noqa: N803
44
46
  DEVELOPMENT_PROFILE_NAME: Optional[str] = None, # noqa: N803
47
+ QGIS_LOCALE: Optional[str] = None, # noqa: N803
48
+ QGIS_GUI_INI: Optional[str] = None, # noqa: N803
45
49
  **other_vars: str,
46
50
  ) -> None:
47
51
  self.QGIS_EXECUTABLE_PATH = Path(QGIS_EXECUTABLE_PATH)
@@ -51,6 +55,8 @@ class DotenvConfig:
51
55
  )
52
56
  self.DEBUGGER_LIBRARY = DEBUGGER_LIBRARY
53
57
  self.DEVELOPMENT_PROFILE_NAME = DEVELOPMENT_PROFILE_NAME
58
+ self.QGIS_LOCALE = QGIS_LOCALE
59
+ self.QGIS_GUI_INI = QGIS_GUI_INI
54
60
  self.runtime_environment = other_vars
55
61
 
56
62
 
@@ -20,7 +20,7 @@
20
20
  import logging
21
21
  from dataclasses import dataclass, field
22
22
  from pathlib import Path
23
- from typing import Literal, Union
23
+ from typing import Literal, Optional, Union
24
24
 
25
25
  import tomli
26
26
 
@@ -43,6 +43,7 @@ class PyprojectConfig:
43
43
  Literal["changelog"], Literal["distribution"]
44
44
  ] = "changelog"
45
45
  disabled_extra_plugins: list[str] = field(default_factory=list)
46
+ license_file_path: Optional[str] = None
46
47
 
47
48
  def __post_init__(self) -> None:
48
49
  if self.version_number_source not in ["changelog", "distribution"]:
@@ -42,6 +42,8 @@ def launch_development_qgis(
42
42
  development_mode_config.qgis_executable_path,
43
43
  bootstrap_file_path,
44
44
  development_mode_config.profile_name,
45
+ development_mode_config.locale,
46
+ development_mode_config.ui_ini,
45
47
  )
46
48
 
47
49
  LOGGER.info("waiting for qgis to connect")
@@ -26,6 +26,8 @@ from typing import Optional
26
26
  class DevelopmentModeConfig:
27
27
  qgis_executable_path: Path
28
28
  profile_name: Optional[str]
29
+ locale: Optional[str]
30
+ ui_ini: Optional[str]
29
31
  runtime_environment: dict[str, str]
30
32
  runtime_library_paths: list[Path]
31
33
  plugin_package_path: Path
@@ -29,6 +29,8 @@ def launch_qgis_with_bootstrap_script(
29
29
  qgis_executable_path: Path,
30
30
  bootstrap_script_path: Path,
31
31
  profile_name: Optional[str],
32
+ locale: Optional[str],
33
+ ui_ini: Optional[str],
32
34
  ) -> None:
33
35
  args = [
34
36
  str(qgis_executable_path),
@@ -42,6 +44,16 @@ def launch_qgis_with_bootstrap_script(
42
44
  else:
43
45
  LOGGER.info("using default profile name")
44
46
 
47
+ if locale:
48
+ LOGGER.info("using locale name %s", locale)
49
+ args.extend(["--lang", locale])
50
+ else:
51
+ LOGGER.info("using default locale")
52
+
53
+ if ui_ini:
54
+ LOGGER.info("using ui ini file from path %s", ui_ini)
55
+ args.extend(["--customizationfile", ui_ini])
56
+
45
57
  LOGGER.debug("launch command args %s", args)
46
58
 
47
59
  process = Popen(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: qgis-plugin-dev-tools
3
- Version: 0.7.0
3
+ Version: 0.9.0
4
4
  Summary: QGIS plugin development and packaging tools, which make managing runtime dependencies easy.
5
5
  Home-page: https://github.com/nlsfi/qgis-plugin-dev-tools
6
6
  Author: National Land Survey of Finland
@@ -91,6 +91,14 @@ plugin_package_name = "your_plugin_package_name"
91
91
  version_number_source = "distribution" # or "changelog" (default if missing)
92
92
  ```
93
93
 
94
+ QGIS plugins are required to have a LICENSE file included in the plugin package. However, usually it is more convenient to keep the license file in the root of the repository. LICENSE can be copied automatically to the plugin zip while packaging if such file is found. A relative path to the license file can also be configured with `license_file_path` option.
95
+
96
+ ```toml
97
+ [tool.qgis_plugin_dev_tools]
98
+ plugin_package_name = "your_plugin_package_name"
99
+ license_file_path = "docs/my-license.md"
100
+ ```
101
+
94
102
  ## Plugin packaging
95
103
 
96
104
  Run `qgis-plugin-dev-tools build` (short `qpdt b`) to package the plugin and any runtime dependencies to a standard QGIS plugin zip file, that can be installed and published.
@@ -113,6 +121,8 @@ By default config is read from `pyproject.toml` and runtime config from `.env` i
113
121
  QGIS_EXECUTABLE_PATH= # path to qgis-bin/qgis-bin-ltr or .exe equivalents, necessary
114
122
  # DEBUGGER_LIBRARY= # debugpy/pydevd to start a debugger on init, library must be installed to the environment
115
123
  # DEVELOPMENT_PROFILE_NAME= # name of the profile that qgis is launched with, otherwise uses default
124
+ # QGIS_LOCALE= # locale code of QGIS, otherwise uses default
125
+ # QGIS_GUI_INI= # path to ini file containing QGIS UI customizations
116
126
 
117
127
  # any other variables are added to the runtime QGIS environment
118
128
  # SOMETHING=something
@@ -165,6 +175,14 @@ All notable changes to this project will be documented in this file.
165
175
 
166
176
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
167
177
 
178
+ ## [0.9.0] - 2025-01-08
179
+
180
+ - Feat: Copy license into the plugin zip while deploying
181
+
182
+ ## [0.8.0] - 2024-08-23
183
+
184
+ - Feat: Add locale and ui ini options to be able to further customize development environment
185
+
168
186
  ## [0.7.0] - 2024-05-21
169
187
 
170
188
  - Fix: Bundle contents by parsing pep-compliant distribution file catalog instead of possibly missing tool-specific top-level.txt
@@ -233,3 +251,5 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
233
251
  [0.6.1]: https://github.com/nlsfi/qgis-plugin-dev-tools/releases/tag/v0.6.1
234
252
  [0.6.2]: https://github.com/nlsfi/qgis-plugin-dev-tools/releases/tag/v0.6.2
235
253
  [0.7.0]: https://github.com/nlsfi/qgis-plugin-dev-tools/releases/tag/v0.7.0
254
+ [0.8.0]: https://github.com/nlsfi/qgis-plugin-dev-tools/releases/tag/v0.8.0
255
+ [0.9.0]: https://github.com/nlsfi/qgis-plugin-dev-tools/releases/tag/v0.9.0
@@ -5,7 +5,7 @@ import zipfile
5
5
  from pathlib import Path
6
6
 
7
7
  import pytest
8
- from qgis_plugin_dev_tools.build import make_plugin_zip
8
+ from qgis_plugin_dev_tools.build import copy_license, make_plugin_zip
9
9
  from qgis_plugin_dev_tools.config import DevToolsConfig, VersionNumberSource
10
10
 
11
11
 
@@ -49,6 +49,9 @@ def plugin_dir(tmp_path: Path) -> Path:
49
49
  main_file = plugin_dir / "__init__.py"
50
50
  main_file.touch()
51
51
 
52
+ license_file = tmp_path / "LICENSE"
53
+ license_file.touch()
54
+
52
55
  os.chdir(tmp_path.resolve())
53
56
  sys.path.append(tmp_path.resolve().as_posix())
54
57
 
@@ -56,10 +59,11 @@ def plugin_dir(tmp_path: Path) -> Path:
56
59
 
57
60
 
58
61
  @pytest.fixture()
59
- def dev_tools_config(plugin_dir: Path) -> DevToolsConfig:
62
+ def dev_tools_config(tmp_path: Path, plugin_dir: Path) -> DevToolsConfig:
60
63
  from qgis_plugin_dev_tools.config import DevToolsConfig
61
64
 
62
65
  return DevToolsConfig(
66
+ pyproject_path=tmp_path,
63
67
  plugin_package_name="Plugin",
64
68
  runtime_requires=["pytest"],
65
69
  changelog_file_path=plugin_dir / "CHANGELOG.md",
@@ -67,14 +71,18 @@ def dev_tools_config(plugin_dir: Path) -> DevToolsConfig:
67
71
  auto_add_recursive_runtime_dependencies=True,
68
72
  version_number_source=VersionNumberSource.CHANGELOG,
69
73
  disabled_extra_plugins=[],
74
+ license_file_path=None,
70
75
  )
71
76
 
72
77
 
73
78
  @pytest.fixture()
74
- def dev_tools_config_with_duplicate_dependencies(plugin_dir: Path) -> DevToolsConfig:
79
+ def dev_tools_config_with_duplicate_dependencies(
80
+ tmp_path: Path, plugin_dir: Path
81
+ ) -> DevToolsConfig:
75
82
  from qgis_plugin_dev_tools.config import DevToolsConfig
76
83
 
77
84
  return DevToolsConfig(
85
+ pyproject_path=tmp_path,
78
86
  plugin_package_name="Plugin",
79
87
  runtime_requires=["pytest-cov"],
80
88
  changelog_file_path=plugin_dir / "CHANGELOG.md",
@@ -82,15 +90,17 @@ def dev_tools_config_with_duplicate_dependencies(plugin_dir: Path) -> DevToolsCo
82
90
  auto_add_recursive_runtime_dependencies=True,
83
91
  version_number_source=VersionNumberSource.CHANGELOG,
84
92
  disabled_extra_plugins=[],
93
+ license_file_path=None,
85
94
  )
86
95
 
87
96
 
88
97
  @pytest.fixture()
89
- def dev_tools_config_minimal(plugin_dir: Path) -> DevToolsConfig:
98
+ def dev_tools_config_minimal(tmp_path: Path, plugin_dir: Path) -> DevToolsConfig:
90
99
  # No python path append and not recursive deps
91
100
  from qgis_plugin_dev_tools.config import DevToolsConfig
92
101
 
93
102
  return DevToolsConfig(
103
+ pyproject_path=tmp_path,
94
104
  plugin_package_name="Plugin",
95
105
  runtime_requires=["pytest"],
96
106
  changelog_file_path=plugin_dir / "CHANGELOG.md",
@@ -98,6 +108,7 @@ def dev_tools_config_minimal(plugin_dir: Path) -> DevToolsConfig:
98
108
  auto_add_recursive_runtime_dependencies=False,
99
109
  version_number_source=VersionNumberSource.CHANGELOG,
100
110
  disabled_extra_plugins=[],
111
+ license_file_path=None,
101
112
  )
102
113
 
103
114
 
@@ -115,19 +126,17 @@ def test_make_zip(dev_tools_config: "DevToolsConfig", tmp_path: Path):
115
126
  expected_zip, "Plugin/_vendor/__init__.py"
116
127
  )
117
128
  vendor_files = _get_file_names(expected_zip, "Plugin/_vendor/")
129
+ plugin_files = _get_file_names(expected_zip, "Plugin/")
130
+ assert "LICENSE" in plugin_files
118
131
 
119
132
  assert "import Plugin._vendor" in plugin_init_file_contents
120
133
  assert "sys.path.append" in vendor_init_file_contents
121
134
  assert vendor_files == {
122
135
  "__init__.py",
123
136
  "_pytest",
124
- "atomicwrites",
125
- "atomicwrites-1.4.0.dist-info",
126
137
  "attr",
127
138
  "attrs",
128
139
  "attrs-21.4.0.dist-info",
129
- "colorama",
130
- "colorama-0.4.4.dist-info",
131
140
  "importlib_metadata",
132
141
  "importlib_metadata-4.11.3.dist-info",
133
142
  "iniconfig",
@@ -142,7 +151,7 @@ def test_make_zip(dev_tools_config: "DevToolsConfig", tmp_path: Path):
142
151
  "pytest-6.2.5.dist-info",
143
152
  "toml",
144
153
  "toml-0.10.2.dist-info",
145
- "typing_extensions-4.2.0.dist-info",
154
+ "typing_extensions-4.12.2.dist-info",
146
155
  "typing_extensions.py", # no top-level.txt in .dist-info, parsed from record
147
156
  "zipp-3.8.0.dist-info",
148
157
  "zipp.py", # top-level.txt in .dist-info, includes zipp
@@ -172,15 +181,11 @@ def test_make_zip_with_duplicate_dependencies(
172
181
  assert vendor_files == {
173
182
  "__init__.py",
174
183
  "_pytest",
175
- "atomicwrites",
176
- "atomicwrites-1.4.0.dist-info",
177
184
  "attr",
178
185
  "attrs",
179
186
  "attrs-21.4.0.dist-info",
180
- "colorama",
181
- "colorama-0.4.4.dist-info",
182
187
  "coverage",
183
- "coverage-6.3.2.dist-info",
188
+ "coverage-7.6.1.dist-info",
184
189
  "importlib_metadata",
185
190
  "importlib_metadata-4.11.3.dist-info",
186
191
  "iniconfig",
@@ -194,10 +199,10 @@ def test_make_zip_with_duplicate_dependencies(
194
199
  "pytest",
195
200
  "pytest-6.2.5.dist-info",
196
201
  "pytest_cov",
197
- "pytest_cov-2.12.0.dist-info",
202
+ "pytest_cov-4.1.0.dist-info",
198
203
  "toml",
199
204
  "toml-0.10.2.dist-info",
200
- "typing_extensions-4.2.0.dist-info",
205
+ "typing_extensions-4.12.2.dist-info",
201
206
  "typing_extensions.py", # no top-level.txt in .dist-info, parsed from record
202
207
  "zipp-3.8.0.dist-info",
203
208
  "zipp.py", # top-level.txt in .dist-info, includes zipp
@@ -233,6 +238,90 @@ def test_make_zip_with_minimal_config(
233
238
  }
234
239
 
235
240
 
241
+ @pytest.mark.parametrize("licence_file_name", ["LICENSE", "license.md"])
242
+ def test_make_zip_copies_license_from_custom_path(
243
+ dev_tools_config_minimal: "DevToolsConfig", tmp_path: Path, licence_file_name: str
244
+ ):
245
+ # Remove original
246
+ original_license = tmp_path / "LICENSE"
247
+ os.remove(original_license)
248
+
249
+ license_dir = tmp_path / "license"
250
+ license_dir.mkdir()
251
+ license_file = license_dir / licence_file_name
252
+ license_file.touch()
253
+ dev_tools_config_minimal.license_file_path = license_file
254
+ target_path = tmp_path / "dist"
255
+ expected_zip = target_path / "Plugin-test-version.zip"
256
+
257
+ make_plugin_zip(
258
+ dev_tools_config_minimal, target_path, override_plugin_version="test-version"
259
+ )
260
+
261
+ assert target_path.exists()
262
+ assert expected_zip.exists()
263
+ plugin_files = _get_file_names(expected_zip, "Plugin/")
264
+ assert "LICENSE" in plugin_files
265
+
266
+
267
+ def test_make_zip_copies_license_with_different_name(
268
+ dev_tools_config_minimal: "DevToolsConfig",
269
+ tmp_path: Path,
270
+ plugin_dir: Path,
271
+ ):
272
+ # Remove original
273
+ original_license = tmp_path / "LICENSE"
274
+ os.remove(original_license)
275
+
276
+ license_file = tmp_path / "license.md"
277
+ license_file.touch()
278
+
279
+ copy_license(dev_tools_config_minimal, plugin_dir.parent)
280
+
281
+ assert (plugin_dir / "LICENSE").exists()
282
+
283
+
284
+ def test_make_zip_does_not_create_license_if_it_does_not_exist(
285
+ dev_tools_config_minimal: "DevToolsConfig", tmp_path: Path
286
+ ):
287
+ # Remove original
288
+ original_license = tmp_path / "LICENSE"
289
+ os.remove(original_license)
290
+
291
+ target_path = tmp_path / "dist"
292
+ expected_zip = target_path / "Plugin-test-version.zip"
293
+
294
+ make_plugin_zip(
295
+ dev_tools_config_minimal, target_path, override_plugin_version="test-version"
296
+ )
297
+
298
+ assert target_path.exists()
299
+ assert expected_zip.exists()
300
+ plugin_files = _get_file_names(expected_zip, "Plugin/")
301
+ assert "LICENSE" not in plugin_files
302
+
303
+
304
+ def test_make_zip_does_not_create_license_if_configured_one_does_not_exist(
305
+ dev_tools_config_minimal: "DevToolsConfig", tmp_path: Path
306
+ ):
307
+ dev_tools_config_minimal.license_file_path = tmp_path / "non-existing-license"
308
+
309
+ original_license = tmp_path / "LICENSE"
310
+ os.remove(original_license)
311
+
312
+ target_path = tmp_path / "dist"
313
+ expected_zip = target_path / "Plugin-test-version.zip"
314
+
315
+ make_plugin_zip(
316
+ dev_tools_config_minimal, target_path, override_plugin_version="test-version"
317
+ )
318
+
319
+ assert target_path.exists()
320
+ assert expected_zip.exists()
321
+ plugin_files = _get_file_names(expected_zip, "Plugin/")
322
+ assert "LICENSE" not in plugin_files
323
+
324
+
236
325
  def _get_file_names(zip_file: Path, prefix: str) -> set[str]:
237
326
  with zipfile.ZipFile(zip_file) as z:
238
327
  namelist = z.namelist()
@@ -14,9 +14,7 @@ def sample_dist() -> Any:
14
14
  def test_get_distribution_requirements(sample_dist: Any):
15
15
  requirements = get_distribution_requirements(sample_dist)
16
16
  assert sorted(requirements.keys()) == [
17
- "atomicwrites",
18
17
  "attrs",
19
- "colorama",
20
18
  "importlib-metadata",
21
19
  "iniconfig",
22
20
  "packaging",
@@ -46,6 +46,8 @@ def test_optionals_can_be_missing(
46
46
  result = read_dotenv_configs([test_file])
47
47
  assert result.DEVELOPMENT_PROFILE_NAME is None
48
48
  assert result.DEBUGGER_LIBRARY is None
49
+ assert result.QGIS_LOCALE is None
50
+ assert result.QGIS_GUI_INI is None
49
51
 
50
52
 
51
53
  def test_other_vars_saved_as_runtime_env(