qgis-plugin-dev-tools 0.6.2__py3-none-any.whl → 0.8.0__py3-none-any.whl
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.
- qgis_plugin_dev_tools/__init__.py +1 -1
- qgis_plugin_dev_tools/build/packaging.py +97 -107
- qgis_plugin_dev_tools/build/rewrite_imports.py +3 -4
- qgis_plugin_dev_tools/cli/__init__.py +10 -7
- qgis_plugin_dev_tools/config/__init__.py +9 -6
- qgis_plugin_dev_tools/config/dotenv.py +15 -9
- qgis_plugin_dev_tools/config/pyproject.py +4 -3
- qgis_plugin_dev_tools/publish/__init__.py +5 -3
- qgis_plugin_dev_tools/start/__init__.py +2 -2
- qgis_plugin_dev_tools/start/bootstrap/__init__.py +3 -3
- qgis_plugin_dev_tools/start/bootstrap/template.py +27 -29
- qgis_plugin_dev_tools/start/config.py +7 -5
- qgis_plugin_dev_tools/start/daemon_server.py +3 -2
- qgis_plugin_dev_tools/start/launch.py +12 -0
- qgis_plugin_dev_tools/utils/distributions.py +33 -11
- {qgis_plugin_dev_tools-0.6.2.dist-info → qgis_plugin_dev_tools-0.8.0.dist-info}/METADATA +23 -1
- qgis_plugin_dev_tools-0.8.0.dist-info/RECORD +28 -0
- {qgis_plugin_dev_tools-0.6.2.dist-info → qgis_plugin_dev_tools-0.8.0.dist-info}/WHEEL +1 -1
- qgis_plugin_dev_tools-0.6.2.dist-info/RECORD +0 -28
- {qgis_plugin_dev_tools-0.6.2.dist-info → qgis_plugin_dev_tools-0.8.0.dist-info}/LICENSE +0 -0
- {qgis_plugin_dev_tools-0.6.2.dist-info → qgis_plugin_dev_tools-0.8.0.dist-info}/entry_points.txt +0 -0
- {qgis_plugin_dev_tools-0.6.2.dist-info → qgis_plugin_dev_tools-0.8.0.dist-info}/top_level.txt +0 -0
|
@@ -28,9 +28,7 @@ from qgis_plugin_dev_tools.build.rewrite_imports import (
|
|
|
28
28
|
insert_as_first_import,
|
|
29
29
|
)
|
|
30
30
|
from qgis_plugin_dev_tools.config import DevToolsConfig
|
|
31
|
-
from qgis_plugin_dev_tools.utils.distributions import
|
|
32
|
-
get_distribution_top_level_package_names,
|
|
33
|
-
)
|
|
31
|
+
from qgis_plugin_dev_tools.utils.distributions import get_distribution_top_level_names
|
|
34
32
|
|
|
35
33
|
IGNORED_FILES = shutil.ignore_patterns("__pycache__", "*.pyc", "*.pyi")
|
|
36
34
|
LOGGER = logging.getLogger(__name__)
|
|
@@ -61,142 +59,134 @@ def copy_runtime_requirements(
|
|
|
61
59
|
dev_tools_config: DevToolsConfig,
|
|
62
60
|
build_directory_path: Path,
|
|
63
61
|
) -> None:
|
|
62
|
+
if len(dev_tools_config.runtime_distributions) == 0:
|
|
63
|
+
return
|
|
64
|
+
|
|
64
65
|
plugin_package_name = dev_tools_config.plugin_package_name
|
|
66
|
+
vendor_path = build_directory_path / plugin_package_name / "_vendor"
|
|
65
67
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
68
|
+
vendor_path.mkdir(parents=True)
|
|
69
|
+
vendor_init_file = vendor_path / "__init__.py"
|
|
70
|
+
vendor_init_file.touch()
|
|
71
|
+
|
|
72
|
+
if dev_tools_config.append_distributions_to_path:
|
|
73
|
+
vendor_init_file.write_text(VENDOR_PATH_APPEND_SCRIPT)
|
|
74
|
+
insert_as_first_import(
|
|
75
|
+
build_directory_path / plugin_package_name / "__init__.py",
|
|
76
|
+
f"{plugin_package_name}._vendor",
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
vendored_runtime_top_level_names: list[str] = []
|
|
80
|
+
|
|
81
|
+
for vendored_distribution in (
|
|
77
82
|
dev_tools_config.runtime_distributions
|
|
78
83
|
+ dev_tools_config.extra_runtime_distributions
|
|
79
84
|
):
|
|
80
|
-
|
|
85
|
+
# if a recursively found dependency is provided by system packages,
|
|
86
|
+
# assume it does not have to be bundled (this is possibly dangerous,
|
|
87
|
+
# if build is made on a different system package set than runtime)
|
|
81
88
|
if (
|
|
82
|
-
|
|
83
|
-
and
|
|
89
|
+
vendored_distribution in dev_tools_config.extra_runtime_distributions
|
|
90
|
+
and Path(
|
|
91
|
+
vendored_distribution._path, # type: ignore
|
|
92
|
+
).is_relative_to(Path(sys.base_prefix))
|
|
84
93
|
):
|
|
85
|
-
|
|
86
|
-
LOGGER.debug(
|
|
94
|
+
LOGGER.warning(
|
|
87
95
|
"skipping recursively found runtime requirement %s "
|
|
88
|
-
"because it is included in
|
|
89
|
-
|
|
96
|
+
"because it is included in system packages",
|
|
97
|
+
vendored_distribution.name,
|
|
90
98
|
)
|
|
91
99
|
continue
|
|
92
100
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
LOGGER.debug(
|
|
96
|
-
"bundling runtime requirement %s",
|
|
97
|
-
dist.metadata["Name"],
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
# don't vendor self, but allow to vendor other packages provided
|
|
101
|
+
# don't vendor plugin package, but allow to vendor other packages provided
|
|
101
102
|
# from plugin distribution. if "-e ." installs "my-plugin-name" distribution
|
|
102
103
|
# containing my_plugin & my_util_package top level packages, bundling is only
|
|
103
104
|
# needed for my_util_package
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
]
|
|
107
|
-
|
|
108
|
-
runtime_package_names.extend(dist_top_level_packages)
|
|
105
|
+
dist_top_level_names = get_distribution_top_level_names(vendored_distribution)
|
|
106
|
+
dist_top_level_names.discard(plugin_package_name)
|
|
109
107
|
|
|
110
108
|
LOGGER.debug(
|
|
111
|
-
"
|
|
112
|
-
|
|
109
|
+
"bundling runtime requirement %s with top level names %s",
|
|
110
|
+
vendored_distribution.name,
|
|
111
|
+
dist_top_level_names,
|
|
113
112
|
)
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
/ "_vendor"
|
|
119
|
-
/ dist_info_path.name,
|
|
120
|
-
ignore=IGNORED_FILES,
|
|
113
|
+
_copy_distribution_files(
|
|
114
|
+
vendored_distribution,
|
|
115
|
+
dist_top_level_names,
|
|
116
|
+
vendor_path,
|
|
121
117
|
)
|
|
122
|
-
for package_name in dist_top_level_packages:
|
|
123
|
-
_copy_package(
|
|
124
|
-
build_directory_path,
|
|
125
|
-
dist,
|
|
126
|
-
dist_info_path,
|
|
127
|
-
package_name,
|
|
128
|
-
plugin_package_name,
|
|
129
|
-
)
|
|
130
118
|
|
|
131
|
-
|
|
132
|
-
plugin_init_file = (build_directory_path / plugin_package_name) / "__init__.py"
|
|
133
|
-
insert_as_first_import(plugin_init_file, f"{plugin_package_name}._vendor")
|
|
134
|
-
return
|
|
119
|
+
vendored_runtime_top_level_names.extend(dist_top_level_names)
|
|
135
120
|
|
|
136
|
-
|
|
137
|
-
|
|
121
|
+
if not dev_tools_config.append_distributions_to_path:
|
|
122
|
+
for package_name in vendored_runtime_top_level_names:
|
|
123
|
+
LOGGER.debug("rewriting imports for %s", package_name)
|
|
138
124
|
|
|
139
|
-
|
|
140
|
-
|
|
125
|
+
py_files = list((build_directory_path / plugin_package_name).rglob("*.py"))
|
|
126
|
+
ui_files = list((build_directory_path / plugin_package_name).rglob("*.ui"))
|
|
141
127
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
128
|
+
for source_file in py_files + ui_files:
|
|
129
|
+
rewrite_imports_in_source_file(
|
|
130
|
+
source_file,
|
|
131
|
+
rewritten_package_name=package_name,
|
|
132
|
+
container_package_name=f"{plugin_package_name}._vendor",
|
|
133
|
+
)
|
|
148
134
|
|
|
149
135
|
|
|
150
|
-
def
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
package_name: str,
|
|
155
|
-
plugin_package_name: str,
|
|
136
|
+
def _copy_distribution_files(
|
|
137
|
+
distribution: Distribution,
|
|
138
|
+
top_level_names: set[str],
|
|
139
|
+
target_root_path: Path,
|
|
156
140
|
) -> None:
|
|
157
|
-
|
|
158
|
-
"
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
141
|
+
if (file_paths := distribution.files) is None:
|
|
142
|
+
LOGGER.warning("could not resolve %s contents to bundle", distribution.name)
|
|
143
|
+
return
|
|
144
|
+
|
|
145
|
+
# bundle metadata directory first
|
|
146
|
+
distribution_metadata_path = Path(distribution._path) # type: ignore
|
|
162
147
|
LOGGER.debug(
|
|
163
148
|
"copying %s to build directory",
|
|
164
|
-
|
|
149
|
+
distribution_metadata_path.resolve(),
|
|
150
|
+
)
|
|
151
|
+
shutil.copytree(
|
|
152
|
+
src=distribution_metadata_path,
|
|
153
|
+
dst=target_root_path / distribution_metadata_path.name,
|
|
154
|
+
ignore=IGNORED_FILES,
|
|
165
155
|
)
|
|
166
156
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
157
|
+
directories_to_bundle = {
|
|
158
|
+
top_directory_name
|
|
159
|
+
for file_path in file_paths
|
|
160
|
+
if len(file_path.parts) > 1
|
|
161
|
+
and (top_directory_name := file_path.parts[0]) in top_level_names
|
|
162
|
+
}
|
|
163
|
+
files_to_bundle = {
|
|
164
|
+
file_path
|
|
165
|
+
for file_path in file_paths
|
|
166
|
+
if len(file_path.parts) == 1 and file_path.stem in top_level_names
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
record_root_path = distribution_metadata_path.parent
|
|
170
|
+
|
|
171
|
+
for directory_path in directories_to_bundle:
|
|
172
|
+
original_path = record_root_path / directory_path
|
|
173
|
+
new_path = target_root_path / directory_path
|
|
174
|
+
|
|
175
|
+
LOGGER.debug("copying %s to build directory", original_path.resolve())
|
|
176
|
+
|
|
170
177
|
shutil.copytree(
|
|
171
|
-
src=
|
|
172
|
-
dst=
|
|
178
|
+
src=original_path,
|
|
179
|
+
dst=new_path,
|
|
173
180
|
ignore=IGNORED_FILES,
|
|
174
181
|
)
|
|
175
|
-
return
|
|
176
182
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
shutil.copy(
|
|
181
|
-
src=dist_src, dst=build_directory_path / plugin_package_name / "_vendor"
|
|
182
|
-
)
|
|
183
|
-
return
|
|
183
|
+
for file_path in files_to_bundle:
|
|
184
|
+
original_path = record_root_path / file_path
|
|
185
|
+
new_path = target_root_path / file_path
|
|
184
186
|
|
|
185
|
-
|
|
186
|
-
pyd_files = list(dist_info_path.parent.glob(f"{package_name}*.pyd"))
|
|
187
|
-
if pyd_files:
|
|
188
|
-
for dist_src in pyd_files:
|
|
189
|
-
shutil.copy(
|
|
190
|
-
src=dist_src, dst=build_directory_path / plugin_package_name / "_vendor"
|
|
191
|
-
)
|
|
192
|
-
return
|
|
193
|
-
if not package_name.startswith("_"):
|
|
194
|
-
raise ValueError(
|
|
195
|
-
f"Sources for runtime requirement {dist.metadata['Name']} "
|
|
196
|
-
f"cannot be found in {dist_info_path.parent}"
|
|
197
|
-
)
|
|
187
|
+
LOGGER.debug("copying %s to build directory", original_path.resolve())
|
|
198
188
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
189
|
+
shutil.copy(
|
|
190
|
+
src=original_path,
|
|
191
|
+
dst=new_path,
|
|
192
|
+
)
|
|
@@ -19,7 +19,7 @@ class SpecialImportRewriter(ast.NodeTransformer):
|
|
|
19
19
|
_replaced_imported_names: Dict[str, str] = field(default_factory=dict, init=False)
|
|
20
20
|
|
|
21
21
|
# collect the found imported names to replace the references also
|
|
22
|
-
def visit_Import(self, node: ast.Import) -> Any: # noqa N802
|
|
22
|
+
def visit_Import(self, node: ast.Import) -> Any: # noqa: N802
|
|
23
23
|
for alias in node.names:
|
|
24
24
|
if alias.name.startswith(f"{self.from_module}.") and alias.asname is None:
|
|
25
25
|
old_module_name = alias.name
|
|
@@ -33,7 +33,7 @@ class SpecialImportRewriter(ast.NodeTransformer):
|
|
|
33
33
|
return node
|
|
34
34
|
|
|
35
35
|
# check the attributes for an imported module name reference
|
|
36
|
-
def visit_Attribute(self, node: ast.Attribute) -> Any: # noqa N802
|
|
36
|
+
def visit_Attribute(self, node: ast.Attribute) -> Any: # noqa: N802
|
|
37
37
|
attribute_name = ast.unparse(node)
|
|
38
38
|
if attribute_name in self._replaced_imported_names:
|
|
39
39
|
replaced_name = self._replaced_imported_names[attribute_name]
|
|
@@ -42,7 +42,7 @@ class SpecialImportRewriter(ast.NodeTransformer):
|
|
|
42
42
|
return self.generic_visit(node)
|
|
43
43
|
|
|
44
44
|
# this will only handle sys.modules['something'] replace
|
|
45
|
-
def visit_Subscript(self, node: ast.Subscript) -> Any: # noqa N802
|
|
45
|
+
def visit_Subscript(self, node: ast.Subscript) -> Any: # noqa: N802
|
|
46
46
|
if (
|
|
47
47
|
ast.unparse(node.value) == "sys.modules"
|
|
48
48
|
and isinstance(node.slice, ast.Constant)
|
|
@@ -69,7 +69,6 @@ def rewrite_imports_in_source_file(
|
|
|
69
69
|
f'sys.modules["{rewritten_package_name}.',
|
|
70
70
|
]
|
|
71
71
|
if any(special in contents for special in specials):
|
|
72
|
-
|
|
73
72
|
# hold on to the original for license comments
|
|
74
73
|
# since comments are lost with ast parse+unparse
|
|
75
74
|
orig_file = source_file.with_name(source_file.name + "_original")
|
|
@@ -21,7 +21,7 @@ import argparse
|
|
|
21
21
|
import logging
|
|
22
22
|
import sys
|
|
23
23
|
from pathlib import Path
|
|
24
|
-
from typing import
|
|
24
|
+
from typing import Optional
|
|
25
25
|
|
|
26
26
|
from importlib_metadata import entry_points
|
|
27
27
|
|
|
@@ -32,14 +32,12 @@ from qgis_plugin_dev_tools.config.dotenv import read_dotenv_configs
|
|
|
32
32
|
from qgis_plugin_dev_tools.publish import publish_plugin_zip_file
|
|
33
33
|
from qgis_plugin_dev_tools.start import launch_development_qgis
|
|
34
34
|
from qgis_plugin_dev_tools.start.config import DevelopmentModeConfig
|
|
35
|
-
from qgis_plugin_dev_tools.utils.distributions import
|
|
36
|
-
get_distribution_top_level_package_names,
|
|
37
|
-
)
|
|
35
|
+
from qgis_plugin_dev_tools.utils.distributions import get_distribution_top_level_names
|
|
38
36
|
|
|
39
37
|
LOGGER = logging.getLogger(__name__)
|
|
40
38
|
|
|
41
39
|
|
|
42
|
-
def start(dotenv_file_paths:
|
|
40
|
+
def start(dotenv_file_paths: list[Path]) -> None:
|
|
43
41
|
# TODO: allow choosing pyproject file from cli?
|
|
44
42
|
dev_tools_config = DevToolsConfig.from_pyproject_config(Path("pyproject.toml"))
|
|
45
43
|
# TODO: allow setting debugger flag from cli?
|
|
@@ -56,6 +54,8 @@ def start(dotenv_file_paths: List[Path]) -> None:
|
|
|
56
54
|
DevelopmentModeConfig(
|
|
57
55
|
qgis_executable_path=dotenv_config.QGIS_EXECUTABLE_PATH,
|
|
58
56
|
profile_name=dotenv_config.DEVELOPMENT_PROFILE_NAME,
|
|
57
|
+
locale=dotenv_config.QGIS_LOCALE,
|
|
58
|
+
ui_ini=dotenv_config.QGIS_GUI_INI,
|
|
59
59
|
runtime_environment=dotenv_config.runtime_environment,
|
|
60
60
|
runtime_library_paths=[Path(p) for p in sys.path],
|
|
61
61
|
plugin_package_path=dev_tools_config.plugin_package_path,
|
|
@@ -63,13 +63,16 @@ def start(dotenv_file_paths: List[Path]) -> None:
|
|
|
63
63
|
plugin_dependency_package_names=[
|
|
64
64
|
name
|
|
65
65
|
for dist in dev_tools_config.runtime_distributions
|
|
66
|
-
for name in
|
|
66
|
+
for name in get_distribution_top_level_names(dist)
|
|
67
67
|
],
|
|
68
68
|
debugger_library=dotenv_config.DEBUGGER_LIBRARY,
|
|
69
69
|
extra_plugin_package_names=[
|
|
70
70
|
entry_point.name
|
|
71
71
|
for entry_point in entry_points_found_from_python_env
|
|
72
|
-
if
|
|
72
|
+
if (
|
|
73
|
+
entry_point.name != dev_tools_config.plugin_package_name
|
|
74
|
+
and entry_point.name not in dev_tools_config.disabled_extra_plugins
|
|
75
|
+
)
|
|
73
76
|
],
|
|
74
77
|
)
|
|
75
78
|
)
|
|
@@ -21,7 +21,6 @@ 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 List
|
|
25
24
|
|
|
26
25
|
from importlib_metadata import Distribution, distribution
|
|
27
26
|
from packaging.requirements import Requirement
|
|
@@ -39,25 +38,27 @@ class VersionNumberSource(Enum):
|
|
|
39
38
|
try:
|
|
40
39
|
return VersionNumberSource[config_value.upper()]
|
|
41
40
|
except KeyError:
|
|
42
|
-
raise ValueError(f"{config_value=} is not a valid value")
|
|
41
|
+
raise ValueError(f"{config_value=} is not a valid value") from None
|
|
43
42
|
|
|
44
43
|
|
|
45
44
|
class DevToolsConfig:
|
|
46
45
|
plugin_package_name: str
|
|
47
46
|
plugin_package_path: Path
|
|
48
|
-
runtime_distributions:
|
|
47
|
+
runtime_distributions: list[Distribution]
|
|
49
48
|
changelog_file_path: Path
|
|
50
49
|
append_distributions_to_path: bool
|
|
51
50
|
version_number_source: VersionNumberSource
|
|
51
|
+
disabled_extra_plugins: list[str]
|
|
52
52
|
|
|
53
|
-
def __init__(
|
|
53
|
+
def __init__( # noqa: PLR0913
|
|
54
54
|
self,
|
|
55
55
|
plugin_package_name: str,
|
|
56
|
-
runtime_requires:
|
|
56
|
+
runtime_requires: list[str],
|
|
57
57
|
changelog_file_path: Path,
|
|
58
58
|
append_distributions_to_path: bool,
|
|
59
59
|
auto_add_recursive_runtime_dependencies: bool,
|
|
60
|
-
version_number_source: VersionNumberSource
|
|
60
|
+
version_number_source: VersionNumberSource,
|
|
61
|
+
disabled_extra_plugins: list[str],
|
|
61
62
|
) -> None:
|
|
62
63
|
plugin_package_spec = find_spec(plugin_package_name)
|
|
63
64
|
if plugin_package_spec is None or plugin_package_spec.origin is None:
|
|
@@ -75,6 +76,7 @@ class DevToolsConfig:
|
|
|
75
76
|
self.append_distributions_to_path = append_distributions_to_path
|
|
76
77
|
self.version_number_source = version_number_source
|
|
77
78
|
self.extra_runtime_distributions = []
|
|
79
|
+
self.disabled_extra_plugins = disabled_extra_plugins
|
|
78
80
|
|
|
79
81
|
if auto_add_recursive_runtime_dependencies:
|
|
80
82
|
# Add the requirements of the distributions as well
|
|
@@ -109,4 +111,5 @@ class DevToolsConfig:
|
|
|
109
111
|
version_number_source=VersionNumberSource.from_config_value(
|
|
110
112
|
pyproject_config.version_number_source
|
|
111
113
|
),
|
|
114
|
+
disabled_extra_plugins=pyproject_config.disabled_extra_plugins,
|
|
112
115
|
)
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
|
|
20
20
|
import logging
|
|
21
21
|
from pathlib import Path
|
|
22
|
-
from typing import
|
|
22
|
+
from typing import Optional
|
|
23
23
|
|
|
24
24
|
from dotenv import dotenv_values
|
|
25
25
|
|
|
26
26
|
LOGGER = logging.getLogger(__name__)
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
class DotenvConfig:
|
|
29
|
+
class DotenvConfig:
|
|
30
30
|
"""
|
|
31
31
|
Expected structure for the config keys in the .env.
|
|
32
32
|
"""
|
|
@@ -34,14 +34,18 @@ class DotenvConfig: # noqa SIM119
|
|
|
34
34
|
QGIS_EXECUTABLE_PATH: Path
|
|
35
35
|
DEBUGGER_LIBRARY: Optional[str]
|
|
36
36
|
DEVELOPMENT_PROFILE_NAME: Optional[str]
|
|
37
|
-
|
|
37
|
+
QGIS_LOCALE: Optional[str]
|
|
38
|
+
QGIS_GUI_INI: Optional[str]
|
|
39
|
+
runtime_environment: dict[str, str]
|
|
38
40
|
|
|
39
|
-
def __init__(
|
|
41
|
+
def __init__( # noqa: PLR0913
|
|
40
42
|
self,
|
|
41
43
|
*,
|
|
42
|
-
QGIS_EXECUTABLE_PATH: str, # noqa N803
|
|
43
|
-
DEBUGGER_LIBRARY: Optional[str] = None, # noqa N803
|
|
44
|
-
DEVELOPMENT_PROFILE_NAME: Optional[str] = None, # noqa N803
|
|
44
|
+
QGIS_EXECUTABLE_PATH: str, # noqa: N803
|
|
45
|
+
DEBUGGER_LIBRARY: Optional[str] = None, # noqa: N803
|
|
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,10 +55,12 @@ class DotenvConfig: # noqa SIM119
|
|
|
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
|
|
|
57
|
-
def read_dotenv_configs(dotenv_file_paths:
|
|
63
|
+
def read_dotenv_configs(dotenv_file_paths: list[Path]) -> DotenvConfig:
|
|
58
64
|
config = {}
|
|
59
65
|
for dotenv_file_path in dotenv_file_paths:
|
|
60
66
|
LOGGER.debug("reading config from %s", dotenv_file_path.resolve())
|
|
@@ -62,4 +68,4 @@ def read_dotenv_configs(dotenv_file_paths: List[Path]) -> DotenvConfig:
|
|
|
62
68
|
try:
|
|
63
69
|
return DotenvConfig(**{k: v for k, v in config.items() if v})
|
|
64
70
|
except (KeyError, TypeError) as e:
|
|
65
|
-
raise ValueError(f"dev tools config invalid in .env: {e}")
|
|
71
|
+
raise ValueError(f"dev tools config invalid in .env: {e}") from e
|
|
@@ -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
|
|
23
|
+
from typing import Literal, Union
|
|
24
24
|
|
|
25
25
|
import tomli
|
|
26
26
|
|
|
@@ -36,12 +36,13 @@ class PyprojectConfig:
|
|
|
36
36
|
DEV_TOOLS_SECTION_LOCATOR = "qgis_plugin_dev_tools"
|
|
37
37
|
|
|
38
38
|
plugin_package_name: str
|
|
39
|
-
runtime_requires:
|
|
39
|
+
runtime_requires: list[str] = field(default_factory=list)
|
|
40
40
|
use_dangerous_vendor_sys_path_append: bool = False
|
|
41
41
|
auto_add_recursive_runtime_dependencies: bool = False
|
|
42
42
|
version_number_source: Union[
|
|
43
43
|
Literal["changelog"], Literal["distribution"]
|
|
44
44
|
] = "changelog"
|
|
45
|
+
disabled_extra_plugins: list[str] = field(default_factory=list)
|
|
45
46
|
|
|
46
47
|
def __post_init__(self) -> None:
|
|
47
48
|
if self.version_number_source not in ["changelog", "distribution"]:
|
|
@@ -60,4 +61,4 @@ def read_pyproject_config(pyproject_file_path: Path) -> PyprojectConfig:
|
|
|
60
61
|
]
|
|
61
62
|
return PyprojectConfig(**dev_tools_configuration)
|
|
62
63
|
except (KeyError, TypeError) as e:
|
|
63
|
-
raise ValueError(f"dev tools config invalid in pyproject.toml: {e}")
|
|
64
|
+
raise ValueError(f"dev tools config invalid in pyproject.toml: {e}") from e
|
|
@@ -4,13 +4,15 @@ import logging
|
|
|
4
4
|
import os
|
|
5
5
|
from base64 import b64encode
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import
|
|
7
|
+
from typing import cast
|
|
8
8
|
from uuid import uuid4
|
|
9
9
|
|
|
10
10
|
import requests
|
|
11
11
|
|
|
12
12
|
LOGGER = logging.getLogger(__name__)
|
|
13
13
|
|
|
14
|
+
HTTP_STATUS_CODE_OK = 200
|
|
15
|
+
|
|
14
16
|
|
|
15
17
|
def publish_plugin_zip_file(plugin_zip_file_path: Path) -> None:
|
|
16
18
|
username = os.environ.get("QPDT_PUBLISH_USERNAME")
|
|
@@ -51,7 +53,7 @@ def publish_plugin_zip_file(plugin_zip_file_path: Path) -> None:
|
|
|
51
53
|
response.text,
|
|
52
54
|
)
|
|
53
55
|
|
|
54
|
-
if response.status_code !=
|
|
56
|
+
if response.status_code != HTTP_STATUS_CODE_OK:
|
|
55
57
|
raise Exception(
|
|
56
58
|
"QGIS plugin repository plugin upload "
|
|
57
59
|
f"HTTP request failed with status {response.status_code}"
|
|
@@ -62,7 +64,7 @@ def publish_plugin_zip_file(plugin_zip_file_path: Path) -> None:
|
|
|
62
64
|
f"request returned error response {response.json().get('error')}"
|
|
63
65
|
)
|
|
64
66
|
|
|
65
|
-
plugin_id, version_id = cast(
|
|
67
|
+
plugin_id, version_id = cast(dict[str, tuple[int, int]], response.json()).get(
|
|
66
68
|
"result", (None, None)
|
|
67
69
|
)
|
|
68
70
|
|
|
@@ -32,18 +32,18 @@ def launch_development_qgis(
|
|
|
32
32
|
) -> None:
|
|
33
33
|
LOGGER.info("starting daemon server")
|
|
34
34
|
with start_daemon_server() as (port, handle_single_request):
|
|
35
|
-
|
|
36
35
|
LOGGER.info("creating a bootstrap file")
|
|
37
36
|
with create_bootstrap_file(
|
|
38
37
|
development_mode_config,
|
|
39
38
|
port,
|
|
40
39
|
) as bootstrap_file_path:
|
|
41
|
-
|
|
42
40
|
LOGGER.info("launching qgis")
|
|
43
41
|
launch_qgis_with_bootstrap_script(
|
|
44
42
|
development_mode_config.qgis_executable_path,
|
|
45
43
|
bootstrap_file_path,
|
|
46
44
|
development_mode_config.profile_name,
|
|
45
|
+
development_mode_config.locale,
|
|
46
|
+
development_mode_config.ui_ini,
|
|
47
47
|
)
|
|
48
48
|
|
|
49
49
|
LOGGER.info("waiting for qgis to connect")
|
|
@@ -21,11 +21,11 @@ import dataclasses
|
|
|
21
21
|
import logging
|
|
22
22
|
import pickle
|
|
23
23
|
import sys
|
|
24
|
+
from collections.abc import Generator
|
|
24
25
|
from contextlib import contextmanager
|
|
25
26
|
from importlib import resources
|
|
26
27
|
from pathlib import Path
|
|
27
28
|
from tempfile import TemporaryDirectory
|
|
28
|
-
from typing import Generator
|
|
29
29
|
|
|
30
30
|
from qgis_plugin_dev_tools.start.bootstrap.template import BootstrapConfig
|
|
31
31
|
from qgis_plugin_dev_tools.start.config import DevelopmentModeConfig
|
|
@@ -44,10 +44,10 @@ def create_bootstrap_file(
|
|
|
44
44
|
runtime_environment=development_mode_configuration.runtime_environment,
|
|
45
45
|
plugin_package_path=development_mode_configuration.plugin_package_path,
|
|
46
46
|
plugin_package_name=development_mode_configuration.plugin_package_name,
|
|
47
|
-
plugin_dependency_package_names=development_mode_configuration.plugin_dependency_package_names,
|
|
47
|
+
plugin_dependency_package_names=development_mode_configuration.plugin_dependency_package_names,
|
|
48
48
|
debugger_library=development_mode_configuration.debugger_library,
|
|
49
49
|
bootstrap_python_executable_path=Path(sys.executable),
|
|
50
|
-
extra_plugin_package_names=development_mode_configuration.extra_plugin_package_names,
|
|
50
|
+
extra_plugin_package_names=development_mode_configuration.extra_plugin_package_names,
|
|
51
51
|
)
|
|
52
52
|
|
|
53
53
|
LOGGER.debug("using bootstrap config:\n%s", bootstrap_config)
|
|
@@ -26,14 +26,14 @@ import sys
|
|
|
26
26
|
from dataclasses import asdict, dataclass
|
|
27
27
|
from importlib.util import find_spec
|
|
28
28
|
from pathlib import Path
|
|
29
|
-
from typing import
|
|
29
|
+
from typing import Optional
|
|
30
30
|
|
|
31
31
|
# defer qgis.* imports until necessary to avoid loading those
|
|
32
32
|
# for the interpreter that launches the bootstrapping, since it
|
|
33
33
|
# will import the config class from this module
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
def _unload_package_modules(package_names:
|
|
36
|
+
def _unload_package_modules(package_names: list[str]) -> None:
|
|
37
37
|
to_clean_names = [
|
|
38
38
|
module_name
|
|
39
39
|
for module_name in sys.modules
|
|
@@ -43,19 +43,17 @@ def _unload_package_modules(package_names: List[str]) -> None:
|
|
|
43
43
|
)
|
|
44
44
|
]
|
|
45
45
|
for module_name in to_clean_names:
|
|
46
|
-
try:
|
|
46
|
+
try:
|
|
47
47
|
if hasattr(sys.modules[module_name], "qCleanupResources"):
|
|
48
48
|
sys.modules[module_name].qCleanupResources()
|
|
49
|
-
except Exception:
|
|
49
|
+
except Exception:
|
|
50
50
|
pass
|
|
51
|
-
|
|
51
|
+
with contextlib.suppress(Exception):
|
|
52
52
|
del sys.modules[module_name]
|
|
53
|
-
except Exception: # noqa PIE786
|
|
54
|
-
pass
|
|
55
53
|
|
|
56
54
|
|
|
57
55
|
def _monkeypatch_plugin_module_unload_to_unload_dependencies(
|
|
58
|
-
plugin_package_name: str, plugin_dependency_package_names:
|
|
56
|
+
plugin_package_name: str, plugin_dependency_package_names: list[str]
|
|
59
57
|
) -> None:
|
|
60
58
|
from qgis.core import Qgis, QgsMessageLog
|
|
61
59
|
|
|
@@ -67,7 +65,7 @@ def _monkeypatch_plugin_module_unload_to_unload_dependencies(
|
|
|
67
65
|
level=Qgis.Info,
|
|
68
66
|
)
|
|
69
67
|
|
|
70
|
-
from qgis.utils import ( #
|
|
68
|
+
from qgis.utils import ( # (qgis naming)
|
|
71
69
|
_unloadPluginModules as _original_unload,
|
|
72
70
|
)
|
|
73
71
|
|
|
@@ -75,7 +73,7 @@ def _monkeypatch_plugin_module_unload_to_unload_dependencies(
|
|
|
75
73
|
_unload_package_modules, plugin_dependency_package_names
|
|
76
74
|
)
|
|
77
75
|
|
|
78
|
-
def _custom_unload(packageName: str) -> bool: # noqa N803 (qgis naming)
|
|
76
|
+
def _custom_unload(packageName: str) -> bool: # noqa: N803 (qgis naming)
|
|
79
77
|
original_return = _original_unload(packageName)
|
|
80
78
|
if packageName == plugin_package_name:
|
|
81
79
|
unload_plugin_dependency_modules()
|
|
@@ -87,14 +85,14 @@ def _monkeypatch_plugin_module_unload_to_unload_dependencies(
|
|
|
87
85
|
|
|
88
86
|
|
|
89
87
|
def _monkeypatch_plugin_reload_to_reload_extra_plugins(
|
|
90
|
-
main_plugin_package_name: str, extra_plugin_package_names:
|
|
88
|
+
main_plugin_package_name: str, extra_plugin_package_names: list[str]
|
|
91
89
|
) -> None:
|
|
92
90
|
from qgis.core import Qgis, QgsMessageLog
|
|
93
|
-
from qgis.utils import loadPlugin as _original_load # noqa N813 (qgis naming)
|
|
91
|
+
from qgis.utils import loadPlugin as _original_load # noqa: N813 (qgis naming)
|
|
94
92
|
from qgis.utils import startPlugin
|
|
95
|
-
from qgis.utils import unloadPlugin as _original_unload # noqa N813 (qgis naming)
|
|
93
|
+
from qgis.utils import unloadPlugin as _original_unload # noqa: N813 (qgis naming)
|
|
96
94
|
|
|
97
|
-
def _custom_unload(packageName: str) -> bool: # noqa N803 (qgis naming)
|
|
95
|
+
def _custom_unload(packageName: str) -> bool: # noqa: N803 (qgis naming)
|
|
98
96
|
original_return = _original_unload(packageName)
|
|
99
97
|
|
|
100
98
|
if packageName == main_plugin_package_name:
|
|
@@ -104,7 +102,7 @@ def _monkeypatch_plugin_reload_to_reload_extra_plugins(
|
|
|
104
102
|
|
|
105
103
|
return original_return
|
|
106
104
|
|
|
107
|
-
def _custom_load(packageName: str) -> bool: # noqa N803 (qgis naming)
|
|
105
|
+
def _custom_load(packageName: str) -> bool: # noqa: N803 (qgis naming)
|
|
108
106
|
if packageName == main_plugin_package_name:
|
|
109
107
|
for plugin_package_name in extra_plugin_package_names:
|
|
110
108
|
with contextlib.suppress(Exception):
|
|
@@ -124,7 +122,7 @@ def _monkeypatch_plugin_reload_to_reload_extra_plugins(
|
|
|
124
122
|
qgis_utils_module.loadPlugin = _custom_load
|
|
125
123
|
|
|
126
124
|
|
|
127
|
-
def _setup_runtime_library_paths(runtime_library_paths:
|
|
125
|
+
def _setup_runtime_library_paths(runtime_library_paths: list[Path]) -> None:
|
|
128
126
|
from qgis.core import Qgis, QgsMessageLog
|
|
129
127
|
|
|
130
128
|
QgsMessageLog.logMessage(
|
|
@@ -133,7 +131,7 @@ def _setup_runtime_library_paths(runtime_library_paths: List[Path]) -> None:
|
|
|
133
131
|
sys.path.extend(str(p) for p in runtime_library_paths)
|
|
134
132
|
|
|
135
133
|
|
|
136
|
-
def _setup_runtime_environment(runtime_environment:
|
|
134
|
+
def _setup_runtime_environment(runtime_environment: dict[str, str]) -> None:
|
|
137
135
|
from qgis.core import Qgis, QgsMessageLog
|
|
138
136
|
|
|
139
137
|
QgsMessageLog.logMessage("setting dev env variables", "Bootstrap", level=Qgis.Info)
|
|
@@ -141,7 +139,7 @@ def _setup_runtime_environment(runtime_environment: Dict[str, str]) -> None:
|
|
|
141
139
|
|
|
142
140
|
|
|
143
141
|
def _enable_extra_plugins(
|
|
144
|
-
main_plugin_package_name: str, extra_plugin_package_names:
|
|
142
|
+
main_plugin_package_name: str, extra_plugin_package_names: list[str]
|
|
145
143
|
) -> None:
|
|
146
144
|
from qgis.PyQt.QtCore import QSettings
|
|
147
145
|
from qgis.utils import plugin_paths, unloadPlugin
|
|
@@ -162,7 +160,7 @@ def _enable_extra_plugins(
|
|
|
162
160
|
def _enable_plugin(
|
|
163
161
|
plugin_package_name: str,
|
|
164
162
|
plugin_package_path: Path,
|
|
165
|
-
plugin_dependency_package_names:
|
|
163
|
+
plugin_dependency_package_names: list[str],
|
|
166
164
|
) -> None:
|
|
167
165
|
from pyplugin_installer.installer_data import plugins as installer_plugins
|
|
168
166
|
from qgis.core import Qgis, QgsMessageLog
|
|
@@ -240,24 +238,24 @@ def _start_debugger(library_name: Optional[str], python_executable_path: Path) -
|
|
|
240
238
|
|
|
241
239
|
try:
|
|
242
240
|
if library_name == "debugpy":
|
|
243
|
-
import debugpy # noqa SC200
|
|
241
|
+
import debugpy # noqa: SC200
|
|
244
242
|
|
|
245
243
|
# at least on windows qgis resets the env and sys.executable points
|
|
246
244
|
# to the qgis executable, hold on to the original python to use here
|
|
247
|
-
debugpy.configure(python=str(python_executable_path)) # noqa SC200
|
|
248
|
-
debugpy.listen(("localhost", 5678)) # noqa SC200
|
|
245
|
+
debugpy.configure(python=str(python_executable_path)) # noqa: SC200
|
|
246
|
+
debugpy.listen(("localhost", 5678)) # noqa: SC200
|
|
249
247
|
|
|
250
248
|
elif library_name == "pydevd":
|
|
251
|
-
import pydevd # noqa SC200
|
|
249
|
+
import pydevd # noqa: SC200
|
|
252
250
|
|
|
253
|
-
pydevd.settrace( # noqa SC200
|
|
251
|
+
pydevd.settrace( # noqa: SC200
|
|
254
252
|
"localhost", port=5678, stdoutToServer=True, stderrToServer=True
|
|
255
253
|
)
|
|
256
254
|
|
|
257
255
|
else:
|
|
258
256
|
return
|
|
259
257
|
|
|
260
|
-
except Exception as e:
|
|
258
|
+
except Exception as e:
|
|
261
259
|
QgsMessageLog.logMessage(
|
|
262
260
|
f"failed to start {library_name} debugger: {e}",
|
|
263
261
|
"Bootstrap",
|
|
@@ -274,14 +272,14 @@ def _start_debugger(library_name: Optional[str], python_executable_path: Path) -
|
|
|
274
272
|
@dataclass
|
|
275
273
|
class BootstrapConfig:
|
|
276
274
|
daemon_socket_port: int
|
|
277
|
-
runtime_library_paths:
|
|
278
|
-
runtime_environment:
|
|
275
|
+
runtime_library_paths: list[Path]
|
|
276
|
+
runtime_environment: dict[str, str]
|
|
279
277
|
plugin_package_path: Path
|
|
280
278
|
plugin_package_name: str
|
|
281
|
-
plugin_dependency_package_names:
|
|
279
|
+
plugin_dependency_package_names: list[str]
|
|
282
280
|
debugger_library: Optional[str]
|
|
283
281
|
bootstrap_python_executable_path: Path
|
|
284
|
-
extra_plugin_package_names:
|
|
282
|
+
extra_plugin_package_names: list[str]
|
|
285
283
|
|
|
286
284
|
def __str__(self) -> str:
|
|
287
285
|
result = ""
|
|
@@ -19,17 +19,19 @@
|
|
|
19
19
|
|
|
20
20
|
from dataclasses import dataclass
|
|
21
21
|
from pathlib import Path
|
|
22
|
-
from typing import
|
|
22
|
+
from typing import Optional
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
@dataclass
|
|
26
26
|
class DevelopmentModeConfig:
|
|
27
27
|
qgis_executable_path: Path
|
|
28
28
|
profile_name: Optional[str]
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
locale: Optional[str]
|
|
30
|
+
ui_ini: Optional[str]
|
|
31
|
+
runtime_environment: dict[str, str]
|
|
32
|
+
runtime_library_paths: list[Path]
|
|
31
33
|
plugin_package_path: Path
|
|
32
34
|
plugin_package_name: str
|
|
33
|
-
plugin_dependency_package_names:
|
|
35
|
+
plugin_dependency_package_names: list[str]
|
|
34
36
|
debugger_library: Optional[str]
|
|
35
|
-
extra_plugin_package_names:
|
|
37
|
+
extra_plugin_package_names: list[str]
|
|
@@ -17,9 +17,10 @@
|
|
|
17
17
|
# You should have received a copy of the GNU General Public License
|
|
18
18
|
# along with qgis-plugin-dev-tools. If not, see <https://www.gnu.org/licenses/>.
|
|
19
19
|
|
|
20
|
+
from collections.abc import Generator
|
|
20
21
|
from contextlib import contextmanager
|
|
21
22
|
from socketserver import BaseRequestHandler, TCPServer
|
|
22
|
-
from typing import Callable
|
|
23
|
+
from typing import Callable
|
|
23
24
|
|
|
24
25
|
DAEMON_SERVER_TIMEOUT = 60
|
|
25
26
|
|
|
@@ -34,7 +35,7 @@ class SingleRequestHandlingTCPServer(TCPServer):
|
|
|
34
35
|
|
|
35
36
|
|
|
36
37
|
@contextmanager
|
|
37
|
-
def start_daemon_server() -> Generator[
|
|
38
|
+
def start_daemon_server() -> Generator[tuple[int, Callable[[], bool]], None, None]:
|
|
38
39
|
# TODO: add hot reload instead of closing the daemon?
|
|
39
40
|
# watcher = QFileSystemWatcher([str(p) for p in Path(plugin_path).rglob('*.py')])
|
|
40
41
|
# watcher.fileChanged.connect(send_something_to_socket)
|
|
@@ -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(
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
import importlib.util
|
|
20
20
|
import logging
|
|
21
21
|
from importlib.machinery import SourceFileLoader
|
|
22
|
-
from typing import
|
|
22
|
+
from typing import Optional, cast
|
|
23
23
|
|
|
24
24
|
import importlib_metadata
|
|
25
25
|
from importlib_metadata import Distribution, distribution
|
|
@@ -28,20 +28,39 @@ from packaging.requirements import Requirement
|
|
|
28
28
|
LOGGER = logging.getLogger(__name__)
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
def
|
|
32
|
-
|
|
31
|
+
def get_distribution_top_level_names(dist: Distribution) -> set[str]:
|
|
32
|
+
if (file_paths := dist.files) is None:
|
|
33
|
+
LOGGER.warning("could not resolve %s top level names", dist.name)
|
|
34
|
+
return set()
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
top_level_directory_name
|
|
38
|
+
for path in file_paths
|
|
39
|
+
if (
|
|
40
|
+
len(path.parts) > 1
|
|
41
|
+
and not (top_level_directory_name := path.parts[0]).endswith(
|
|
42
|
+
(".dist-info", ".egg-info")
|
|
43
|
+
)
|
|
44
|
+
and top_level_directory_name not in ("..", "__pycache__")
|
|
45
|
+
)
|
|
46
|
+
} | {
|
|
47
|
+
path.stem
|
|
48
|
+
for path in file_paths
|
|
49
|
+
if len(path.parts) == 1 and path.suffix in (".py", ".pyd")
|
|
50
|
+
}
|
|
33
51
|
|
|
34
52
|
|
|
35
|
-
def get_distribution_requirements(dist: Distribution) ->
|
|
53
|
+
def get_distribution_requirements(dist: Distribution) -> dict[str, Distribution]:
|
|
54
|
+
requirement_distributions: dict[str, Distribution] = {}
|
|
55
|
+
|
|
36
56
|
requirements = [
|
|
37
57
|
Requirement(requirement.split(" ")[0])
|
|
38
58
|
for requirement in dist.requires or []
|
|
39
59
|
if "extra ==" not in requirement
|
|
40
60
|
]
|
|
41
|
-
distributions = {}
|
|
42
61
|
for requirement in requirements:
|
|
43
62
|
try:
|
|
44
|
-
|
|
63
|
+
requirement_distributions[requirement.name] = distribution(requirement.name)
|
|
45
64
|
except importlib_metadata.PackageNotFoundError:
|
|
46
65
|
LOGGER.warning(
|
|
47
66
|
"Getting distribution for %s failed. "
|
|
@@ -55,8 +74,11 @@ def get_distribution_requirements(dist: Distribution) -> Dict[str, Distribution]
|
|
|
55
74
|
LOGGER.error("Could not find package %s", requirement.name)
|
|
56
75
|
continue
|
|
57
76
|
|
|
58
|
-
|
|
59
|
-
for
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
77
|
+
nested_requirement_distributions: dict[str, Distribution] = {}
|
|
78
|
+
for requirement_distribution in requirement_distributions.values():
|
|
79
|
+
nested_requirement_distributions.update(
|
|
80
|
+
get_distribution_requirements(requirement_distribution)
|
|
81
|
+
)
|
|
82
|
+
requirement_distributions.update(nested_requirement_distributions)
|
|
83
|
+
|
|
84
|
+
return requirement_distributions
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: qgis-plugin-dev-tools
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.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
|
|
@@ -113,6 +113,8 @@ By default config is read from `pyproject.toml` and runtime config from `.env` i
|
|
|
113
113
|
QGIS_EXECUTABLE_PATH= # path to qgis-bin/qgis-bin-ltr or .exe equivalents, necessary
|
|
114
114
|
# DEBUGGER_LIBRARY= # debugpy/pydevd to start a debugger on init, library must be installed to the environment
|
|
115
115
|
# DEVELOPMENT_PROFILE_NAME= # name of the profile that qgis is launched with, otherwise uses default
|
|
116
|
+
# QGIS_LOCALE= # locale code of QGIS, otherwise uses default
|
|
117
|
+
# QGIS_GUI_INI= # path to ini file containing QGIS UI customizations
|
|
116
118
|
|
|
117
119
|
# any other variables are added to the runtime QGIS environment
|
|
118
120
|
# SOMETHING=something
|
|
@@ -135,6 +137,15 @@ Development mode also enables using and developing multiple plugins easily if ce
|
|
|
135
137
|
|
|
136
138
|
Extra plugins are loaded on launch and reloaded together with the main plugin if [Plugin Reloader] is used.
|
|
137
139
|
|
|
140
|
+
You can disable plugin auto-load by using `pyproject.toml` configuration (for example when using a dependency, that also provides a plugin entrypoint):
|
|
141
|
+
|
|
142
|
+
```toml
|
|
143
|
+
[tool.qgis_plugin_dev_tools]
|
|
144
|
+
disabled_extra_plugins = [
|
|
145
|
+
"unwanted_plugin_package_name",
|
|
146
|
+
]
|
|
147
|
+
```
|
|
148
|
+
|
|
138
149
|
## Development of qgis-plugin-dev-tools
|
|
139
150
|
|
|
140
151
|
See [development readme](./DEVELOPMENT.md).
|
|
@@ -156,6 +167,15 @@ All notable changes to this project will be documented in this file.
|
|
|
156
167
|
|
|
157
168
|
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).
|
|
158
169
|
|
|
170
|
+
## [0.8.0] - 2024-08-23
|
|
171
|
+
|
|
172
|
+
- Feat: Add locale and ui ini options to be able to further customize development environment
|
|
173
|
+
|
|
174
|
+
## [0.7.0] - 2024-05-21
|
|
175
|
+
|
|
176
|
+
- Fix: Bundle contents by parsing pep-compliant distribution file catalog instead of possibly missing tool-specific top-level.txt
|
|
177
|
+
- Feat: Allow disabling auto-loaded entrypoint plugins
|
|
178
|
+
|
|
159
179
|
## [0.6.2] - 2023-09-27
|
|
160
180
|
|
|
161
181
|
- Fix: Fix issues with bundling requirements of the requirements recursively
|
|
@@ -218,3 +238,5 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
|
218
238
|
[0.6.0]: https://github.com/nlsfi/qgis-plugin-dev-tools/releases/tag/v0.6.0
|
|
219
239
|
[0.6.1]: https://github.com/nlsfi/qgis-plugin-dev-tools/releases/tag/v0.6.1
|
|
220
240
|
[0.6.2]: https://github.com/nlsfi/qgis-plugin-dev-tools/releases/tag/v0.6.2
|
|
241
|
+
[0.7.0]: https://github.com/nlsfi/qgis-plugin-dev-tools/releases/tag/v0.7.0
|
|
242
|
+
[0.8.0]: https://github.com/nlsfi/qgis-plugin-dev-tools/releases/tag/v0.8.0
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
qgis_plugin_dev_tools/__init__.py,sha256=EyLJdeFZiG9GjOkxlKxhB9yZDouz8kylbW5tBwmPCo4,1064
|
|
2
|
+
qgis_plugin_dev_tools/__main__.py,sha256=EqC7agwIoIKbMeOPrcpDWGAQmsaO_oFc0OkIxlowzmA,900
|
|
3
|
+
qgis_plugin_dev_tools/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
qgis_plugin_dev_tools/build/__init__.py,sha256=RbPVZeH29y8efqZRO0dmnH1ArzRvhcpOj9Ka60plX8c,3514
|
|
5
|
+
qgis_plugin_dev_tools/build/changelog_parser.py,sha256=t9u30K2DyWXBOW47r2dMXzP-nbW7oIFCkYeBzsL8r_A,2212
|
|
6
|
+
qgis_plugin_dev_tools/build/distribution.py,sha256=ST2h-YeVh8HWeDURhXoG_Rba9Zha167tdBlVJbXEdQQ,835
|
|
7
|
+
qgis_plugin_dev_tools/build/metadata.py,sha256=rDKRDmvgJxsrlEWKnHNfq3gqOQxClgtNUeeKIIYyMos,1429
|
|
8
|
+
qgis_plugin_dev_tools/build/packaging.py,sha256=ShLrWY_tGlf9orj865MzuET8VRJTpODgb1CPNvISosc,6714
|
|
9
|
+
qgis_plugin_dev_tools/build/rewrite_imports.py,sha256=YZ3ORvm_7Gf6tBKqjqzp-BR-Q_CoIbIh7MlxRZIfPQA,5945
|
|
10
|
+
qgis_plugin_dev_tools/cli/__init__.py,sha256=qv0rEGKSUpk9oskNZC0X-bqiZy715XKDWQS0YBloqWI,6230
|
|
11
|
+
qgis_plugin_dev_tools/config/__init__.py,sha256=7l7Iyjst-pqLobiAO2bKLvkQFLVWoq4O_rny9lQvHp8,4678
|
|
12
|
+
qgis_plugin_dev_tools/config/dotenv.py,sha256=CmqPAIPCwRFS5gMuCePZuNAMwsjggCoXejA1Fpq5k8c,2647
|
|
13
|
+
qgis_plugin_dev_tools/config/pyproject.py,sha256=QvhZIk4QQezKSmTm28IG_lHBIO59CUQmAUQNB78Gxa4,2383
|
|
14
|
+
qgis_plugin_dev_tools/publish/__init__.py,sha256=PB0bUWl6vjDSlqPm92hw2Vtn1A3sqymm3pWgkWsPCOI,2081
|
|
15
|
+
qgis_plugin_dev_tools/start/__init__.py,sha256=9eBCJnpLjE7bll2R_YljiTFj9Bq0NqaFGPOKC7DG274,2181
|
|
16
|
+
qgis_plugin_dev_tools/start/config.py,sha256=n8MaHsY-21LVcNnxwTrg9k8Wo2jb03nCKta6OB9dN3s,1326
|
|
17
|
+
qgis_plugin_dev_tools/start/daemon_server.py,sha256=1i0MWQ0NnbBKZzV6OdT2BZaDEqcIx29ABCIJPorECkg,1845
|
|
18
|
+
qgis_plugin_dev_tools/start/launch.py,sha256=Lqvb7ISTnwV8K1AEVM34q5oUV3OYDsjfRTYMUva6EgE,1959
|
|
19
|
+
qgis_plugin_dev_tools/start/bootstrap/__init__.py,sha256=4FoIYBxXM7zgyaTM4gz5fGD99kYaaqEm-LwBj3Fbuks,2750
|
|
20
|
+
qgis_plugin_dev_tools/start/bootstrap/template.py,sha256=KV2EgHSMxKzA7wbKaxYwPnn6c1f7D2Yrd1Qq_Huh6y0,12432
|
|
21
|
+
qgis_plugin_dev_tools/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
|
+
qgis_plugin_dev_tools/utils/distributions.py,sha256=Q2jZT44Iw8LIBNrf1X2thfCLiKu9yiu7joj0Pdxk-Oc,3215
|
|
23
|
+
qgis_plugin_dev_tools-0.8.0.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
24
|
+
qgis_plugin_dev_tools-0.8.0.dist-info/METADATA,sha256=AwVTjaFDZk27lJbFbpOXmLmmk0kaXPuMMKPKbBe_IF4,10783
|
|
25
|
+
qgis_plugin_dev_tools-0.8.0.dist-info/WHEEL,sha256=Mdi9PDNwEZptOjTlUcAth7XJDFtKrHYaQMPulZeBCiQ,91
|
|
26
|
+
qgis_plugin_dev_tools-0.8.0.dist-info/entry_points.txt,sha256=RwM8y7cN8cRk003jpw-psB4DvifC5foZKSi04IDRBVw,109
|
|
27
|
+
qgis_plugin_dev_tools-0.8.0.dist-info/top_level.txt,sha256=JjwWwRniudLz30UtQJgmIQDeS3HgU5VBgDlKYJhz-nQ,22
|
|
28
|
+
qgis_plugin_dev_tools-0.8.0.dist-info/RECORD,,
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
qgis_plugin_dev_tools/__init__.py,sha256=JgKq-kP715P2g3UWmF-Zx44yqEiPO6O7QHxo_XGafUg,1064
|
|
2
|
-
qgis_plugin_dev_tools/__main__.py,sha256=EqC7agwIoIKbMeOPrcpDWGAQmsaO_oFc0OkIxlowzmA,900
|
|
3
|
-
qgis_plugin_dev_tools/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
qgis_plugin_dev_tools/build/__init__.py,sha256=RbPVZeH29y8efqZRO0dmnH1ArzRvhcpOj9Ka60plX8c,3514
|
|
5
|
-
qgis_plugin_dev_tools/build/changelog_parser.py,sha256=t9u30K2DyWXBOW47r2dMXzP-nbW7oIFCkYeBzsL8r_A,2212
|
|
6
|
-
qgis_plugin_dev_tools/build/distribution.py,sha256=ST2h-YeVh8HWeDURhXoG_Rba9Zha167tdBlVJbXEdQQ,835
|
|
7
|
-
qgis_plugin_dev_tools/build/metadata.py,sha256=rDKRDmvgJxsrlEWKnHNfq3gqOQxClgtNUeeKIIYyMos,1429
|
|
8
|
-
qgis_plugin_dev_tools/build/packaging.py,sha256=ZoS_H6kcMvZOm8JaEsBz7jotLbh6rulZaxJcDb3MkWA,6798
|
|
9
|
-
qgis_plugin_dev_tools/build/rewrite_imports.py,sha256=LNqIgoKCjc6H0BCYq2APGuB44aJ9QdmroTp6y-HzRPc,5943
|
|
10
|
-
qgis_plugin_dev_tools/cli/__init__.py,sha256=L2kTsaxXk27OMPufHf2wlBQOPJv-YUyvQ3ttH1drVQ0,6040
|
|
11
|
-
qgis_plugin_dev_tools/config/__init__.py,sha256=TuLN8HEs_iwpgPXc1uX7cG_4Ig7ypIReCAVGa9USC4U,4489
|
|
12
|
-
qgis_plugin_dev_tools/config/dotenv.py,sha256=b0Vt-OHwML9rFHAJTxlgnU2rSzIgcZIgIfJF_wSmSpg,2389
|
|
13
|
-
qgis_plugin_dev_tools/config/pyproject.py,sha256=rsiavq377mNuYtLPX9wk65TDskwxTTG0cnEGt3UWsUg,2314
|
|
14
|
-
qgis_plugin_dev_tools/publish/__init__.py,sha256=XRxYfWzHBROqEBPu2k-HxZNDbTVoPOuVRF5qd9F1XpU,2051
|
|
15
|
-
qgis_plugin_dev_tools/start/__init__.py,sha256=yDlvaA5smjimK31EcwhmLndjNdzK_m8sJlrM-DlUI0Q,2087
|
|
16
|
-
qgis_plugin_dev_tools/start/config.py,sha256=SCHjvcjPIw2jeI8RFCijOaQBYmkUDU0hFDC9ctChpnc,1286
|
|
17
|
-
qgis_plugin_dev_tools/start/daemon_server.py,sha256=lyD40pVz5C1cPxmWVgbPobAwynKLBo-hddqntWXITRA,1825
|
|
18
|
-
qgis_plugin_dev_tools/start/launch.py,sha256=koF0wfT8t8R0_ofy7dN_LSKGM_g9BjHfCKz5DRxc8IE,1612
|
|
19
|
-
qgis_plugin_dev_tools/start/bootstrap/__init__.py,sha256=qahrpF95vufP-M_lA6uDoLk84FSKpl2EOytRiMHjdZ0,2767
|
|
20
|
-
qgis_plugin_dev_tools/start/bootstrap/template.py,sha256=Qlh88TkZ4Gx53WW8ouZEZUJFL6xzAtvMJ_NRwcVy6Yk,12530
|
|
21
|
-
qgis_plugin_dev_tools/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
|
-
qgis_plugin_dev_tools/utils/distributions.py,sha256=BaAlFfC6vqYrXp19jNNkdXu4EdfllR5yuBJoPbF7aL4,2482
|
|
23
|
-
qgis_plugin_dev_tools-0.6.2.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
24
|
-
qgis_plugin_dev_tools-0.6.2.dist-info/METADATA,sha256=wlGWUK7A4QDS-fXLHNtNkAmejIA8EGd3yDRDMF8W5a0,9912
|
|
25
|
-
qgis_plugin_dev_tools-0.6.2.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
|
|
26
|
-
qgis_plugin_dev_tools-0.6.2.dist-info/entry_points.txt,sha256=RwM8y7cN8cRk003jpw-psB4DvifC5foZKSi04IDRBVw,109
|
|
27
|
-
qgis_plugin_dev_tools-0.6.2.dist-info/top_level.txt,sha256=JjwWwRniudLz30UtQJgmIQDeS3HgU5VBgDlKYJhz-nQ,22
|
|
28
|
-
qgis_plugin_dev_tools-0.6.2.dist-info/RECORD,,
|
|
File without changes
|
{qgis_plugin_dev_tools-0.6.2.dist-info → qgis_plugin_dev_tools-0.8.0.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{qgis_plugin_dev_tools-0.6.2.dist-info → qgis_plugin_dev_tools-0.8.0.dist-info}/top_level.txt
RENAMED
|
File without changes
|