qgis-plugin-dev-tools 0.6.1__py3-none-any.whl → 0.7.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.
@@ -20,7 +20,7 @@
20
20
  import logging
21
21
  import sys
22
22
 
23
- __version__ = "0.6.1"
23
+ __version__ = "0.7.0"
24
24
 
25
25
  LOGGER = logging.getLogger(__name__)
26
26
  _handler = logging.StreamHandler(sys.stdout)
@@ -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
- if len(dev_tools_config.runtime_distributions) > 0:
67
- vendor_path = build_directory_path / plugin_package_name / "_vendor"
68
- vendor_path.mkdir(parents=True)
69
- vendor_init_file = vendor_path / "__init__.py"
70
- vendor_init_file.touch()
71
- if dev_tools_config.append_distributions_to_path:
72
- vendor_init_file.write_text(VENDOR_PATH_APPEND_SCRIPT)
73
-
74
- runtime_package_names = []
75
- # copy dist infos (licenses etc.) and all provided top level packages
76
- for dist in (
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
- dist_info_path = Path(dist._path) # type: ignore
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
- Path(sys.base_prefix) in dist_info_path.parent.parents
83
- and dist in dev_tools_config.extra_runtime_distributions
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
- # If QGIS Python includes the dependency, it does not have to be bundled
86
- LOGGER.debug(
94
+ LOGGER.warning(
87
95
  "skipping recursively found runtime requirement %s "
88
- "because it is included in QGIS",
89
- dist.metadata["Name"],
96
+ "because it is included in system packages",
97
+ vendored_distribution.name,
90
98
  )
91
99
  continue
92
100
 
93
- dist_top_level_packages = get_distribution_top_level_package_names(dist)
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
- dist_top_level_packages = [
105
- name for name in dist_top_level_packages if name != plugin_package_name
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
- "copying %s to build directory",
112
- dist_info_path.resolve(),
109
+ "bundling runtime requirement %s with top level names %s",
110
+ vendored_distribution.name,
111
+ dist_top_level_names,
113
112
  )
114
- shutil.copytree(
115
- src=dist_info_path,
116
- dst=build_directory_path
117
- / plugin_package_name
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
- if dev_tools_config.append_distributions_to_path:
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
- for package_name in runtime_package_names:
137
- LOGGER.debug("rewriting imports for %s", package_name)
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
- py_files = list((build_directory_path / plugin_package_name).rglob("*.py"))
140
- ui_files = list((build_directory_path / plugin_package_name).rglob("*.ui"))
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
- for source_file in py_files + ui_files:
143
- rewrite_imports_in_source_file(
144
- source_file,
145
- rewritten_package_name=package_name,
146
- container_package_name=f"{plugin_package_name}._vendor",
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 _copy_package(
151
- build_directory_path: Path,
152
- dist: Distribution,
153
- dist_info_path: Path,
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
- LOGGER.debug(
158
- "bundling runtime requirement %s package %s",
159
- dist.metadata["Name"],
160
- package_name,
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
- (dist_info_path.parent / package_name).resolve(),
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
- # Full package
168
- dist_src = dist_info_path.parent / package_name
169
- if dist_src.exists():
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=dist_src,
172
- dst=build_directory_path / plugin_package_name / "_vendor" / package_name,
178
+ src=original_path,
179
+ dst=new_path,
173
180
  ignore=IGNORED_FILES,
174
181
  )
175
- return
176
182
 
177
- # Single module
178
- dist_src = dist_info_path.parent / f"{package_name}.py"
179
- if dist_src.exists():
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
- # pyd file
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
- LOGGER.warning(
200
- "Could not find path %s",
201
- (dist_info_path.parent / package_name).resolve(),
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 List, Optional
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: List[Path]) -> None:
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?
@@ -63,13 +61,16 @@ def start(dotenv_file_paths: List[Path]) -> None:
63
61
  plugin_dependency_package_names=[
64
62
  name
65
63
  for dist in dev_tools_config.runtime_distributions
66
- for name in get_distribution_top_level_package_names(dist)
64
+ for name in get_distribution_top_level_names(dist)
67
65
  ],
68
66
  debugger_library=dotenv_config.DEBUGGER_LIBRARY,
69
67
  extra_plugin_package_names=[
70
68
  entry_point.name
71
69
  for entry_point in entry_points_found_from_python_env
72
- if entry_point.name != dev_tools_config.plugin_package_name
70
+ if (
71
+ entry_point.name != dev_tools_config.plugin_package_name
72
+ and entry_point.name not in dev_tools_config.disabled_extra_plugins
73
+ )
73
74
  ],
74
75
  )
75
76
  )
@@ -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: List[Distribution]
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: List[str],
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 = VersionNumberSource.CHANGELOG,
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,17 +76,23 @@ 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
81
- self.extra_runtime_distributions = list(
82
- ChainMap(
83
+ distributions_versions = {
84
+ dist.name: dist.version for dist in self.runtime_distributions
85
+ }
86
+ self.extra_runtime_distributions = [
87
+ dist
88
+ for dist in ChainMap(
83
89
  *(
84
90
  get_distribution_requirements(dist)
85
91
  for dist in self.runtime_distributions
86
92
  )
87
93
  ).values()
88
- )
94
+ if distributions_versions.get(dist.name) != dist.version
95
+ ]
89
96
 
90
97
  @staticmethod
91
98
  def from_pyproject_config(pyproject_file_path: Path) -> "DevToolsConfig":
@@ -104,4 +111,5 @@ class DevToolsConfig:
104
111
  version_number_source=VersionNumberSource.from_config_value(
105
112
  pyproject_config.version_number_source
106
113
  ),
114
+ disabled_extra_plugins=pyproject_config.disabled_extra_plugins,
107
115
  )
@@ -19,14 +19,14 @@
19
19
 
20
20
  import logging
21
21
  from pathlib import Path
22
- from typing import Dict, List, Optional
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: # noqa SIM119
29
+ class DotenvConfig:
30
30
  """
31
31
  Expected structure for the config keys in the .env.
32
32
  """
@@ -34,14 +34,14 @@ class DotenvConfig: # noqa SIM119
34
34
  QGIS_EXECUTABLE_PATH: Path
35
35
  DEBUGGER_LIBRARY: Optional[str]
36
36
  DEVELOPMENT_PROFILE_NAME: Optional[str]
37
- runtime_environment: Dict[str, str]
37
+ runtime_environment: dict[str, str]
38
38
 
39
39
  def __init__(
40
40
  self,
41
41
  *,
42
- QGIS_EXECUTABLE_PATH: str, # noqa N803
43
- DEBUGGER_LIBRARY: Optional[str] = None, # noqa N803
44
- DEVELOPMENT_PROFILE_NAME: Optional[str] = None, # noqa N803
42
+ QGIS_EXECUTABLE_PATH: str, # noqa: N803
43
+ DEBUGGER_LIBRARY: Optional[str] = None, # noqa: N803
44
+ DEVELOPMENT_PROFILE_NAME: Optional[str] = None, # noqa: N803
45
45
  **other_vars: str,
46
46
  ) -> None:
47
47
  self.QGIS_EXECUTABLE_PATH = Path(QGIS_EXECUTABLE_PATH)
@@ -54,7 +54,7 @@ class DotenvConfig: # noqa SIM119
54
54
  self.runtime_environment = other_vars
55
55
 
56
56
 
57
- def read_dotenv_configs(dotenv_file_paths: List[Path]) -> DotenvConfig:
57
+ def read_dotenv_configs(dotenv_file_paths: list[Path]) -> DotenvConfig:
58
58
  config = {}
59
59
  for dotenv_file_path in dotenv_file_paths:
60
60
  LOGGER.debug("reading config from %s", dotenv_file_path.resolve())
@@ -62,4 +62,4 @@ def read_dotenv_configs(dotenv_file_paths: List[Path]) -> DotenvConfig:
62
62
  try:
63
63
  return DotenvConfig(**{k: v for k, v in config.items() if v})
64
64
  except (KeyError, TypeError) as e:
65
- raise ValueError(f"dev tools config invalid in .env: {e}")
65
+ 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 List, Literal, Union
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: List[str] = field(default_factory=list)
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 Dict, Tuple, cast
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 != 200:
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(Dict[str, Tuple[int, int]], response.json()).get(
67
+ plugin_id, version_id = cast(dict[str, tuple[int, int]], response.json()).get(
66
68
  "result", (None, None)
67
69
  )
68
70
 
@@ -32,13 +32,11 @@ 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,
@@ -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, # noqa E501
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, # noqa E501
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 Dict, List, Optional
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: List[str]) -> None:
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: # noqa SIM105
46
+ try:
47
47
  if hasattr(sys.modules[module_name], "qCleanupResources"):
48
48
  sys.modules[module_name].qCleanupResources()
49
- except Exception: # noqa PIE786
49
+ except Exception:
50
50
  pass
51
- try: # noqa SIM105
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: List[str]
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 ( # noqa N813 (qgis naming)
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: List[str]
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: List[Path]) -> None:
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: Dict[str, str]) -> None:
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: List[str]
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: List[str],
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: # noqa PIE786
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: List[Path]
278
- runtime_environment: Dict[str, str]
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: List[str]
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: List[str]
282
+ extra_plugin_package_names: list[str]
285
283
 
286
284
  def __str__(self) -> str:
287
285
  result = ""
@@ -19,17 +19,17 @@
19
19
 
20
20
  from dataclasses import dataclass
21
21
  from pathlib import Path
22
- from typing import Dict, List, Optional
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
- runtime_environment: Dict[str, str]
30
- runtime_library_paths: List[Path]
29
+ runtime_environment: dict[str, str]
30
+ runtime_library_paths: list[Path]
31
31
  plugin_package_path: Path
32
32
  plugin_package_name: str
33
- plugin_dependency_package_names: List[str]
33
+ plugin_dependency_package_names: list[str]
34
34
  debugger_library: Optional[str]
35
- extra_plugin_package_names: List[str]
35
+ 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, Generator, Tuple
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[Tuple[int, Callable[[], bool]], None, None]:
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)
@@ -16,28 +16,69 @@
16
16
  #
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
+ import importlib.util
20
+ import logging
21
+ from importlib.machinery import SourceFileLoader
22
+ from typing import Optional, cast
19
23
 
20
- from typing import Dict, List
21
-
24
+ import importlib_metadata
22
25
  from importlib_metadata import Distribution, distribution
23
26
  from packaging.requirements import Requirement
24
27
 
28
+ LOGGER = logging.getLogger(__name__)
29
+
30
+
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
+ }
25
51
 
26
- def get_distribution_top_level_package_names(dist: Distribution) -> List[str]:
27
- return (dist.read_text("top_level.txt") or "").split()
28
52
 
53
+ def get_distribution_requirements(dist: Distribution) -> dict[str, Distribution]:
54
+ requirement_distributions: dict[str, Distribution] = {}
29
55
 
30
- def get_distribution_requirements(dist: Distribution) -> Dict[str, Distribution]:
31
56
  requirements = [
32
57
  Requirement(requirement.split(" ")[0])
33
58
  for requirement in dist.requires or []
34
59
  if "extra ==" not in requirement
35
60
  ]
36
- distributions = {
37
- requirement.name: distribution(requirement.name) for requirement in requirements
38
- }
39
- sub_requirements = {}
40
- for requirement in distributions.values():
41
- sub_requirements.update(get_distribution_requirements(requirement))
42
- distributions.update(sub_requirements)
43
- return distributions
61
+ for requirement in requirements:
62
+ try:
63
+ requirement_distributions[requirement.name] = distribution(requirement.name)
64
+ except importlib_metadata.PackageNotFoundError:
65
+ LOGGER.warning(
66
+ "Getting distribution for %s failed. "
67
+ "This may be caused by including builtin "
68
+ "packages as requirements.",
69
+ requirement.name,
70
+ )
71
+ spec = importlib.util.find_spec(requirement.name)
72
+ loader = cast(Optional[SourceFileLoader], spec.loader) if spec else None
73
+ if spec and loader and loader.is_package(requirement.name):
74
+ LOGGER.error("Could not find package %s", requirement.name)
75
+ continue
76
+
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.6.1
3
+ Version: 0.7.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
@@ -135,6 +135,15 @@ Development mode also enables using and developing multiple plugins easily if ce
135
135
 
136
136
  Extra plugins are loaded on launch and reloaded together with the main plugin if [Plugin Reloader] is used.
137
137
 
138
+ You can disable plugin auto-load by using `pyproject.toml` configuration (for example when using a dependency, that also provides a plugin entrypoint):
139
+
140
+ ```toml
141
+ [tool.qgis_plugin_dev_tools]
142
+ disabled_extra_plugins = [
143
+ "unwanted_plugin_package_name",
144
+ ]
145
+ ```
146
+
138
147
  ## Development of qgis-plugin-dev-tools
139
148
 
140
149
  See [development readme](./DEVELOPMENT.md).
@@ -156,6 +165,15 @@ All notable changes to this project will be documented in this file.
156
165
 
157
166
  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
167
 
168
+ ## [0.7.0] - 2024-05-21
169
+
170
+ - Fix: Bundle contents by parsing pep-compliant distribution file catalog instead of possibly missing tool-specific top-level.txt
171
+ - Feat: Allow disabling auto-loaded entrypoint plugins
172
+
173
+ ## [0.6.2] - 2023-09-27
174
+
175
+ - Fix: Fix issues with bundling requirements of the requirements recursively
176
+
159
177
  ## [0.6.1] - 2023-09-06
160
178
 
161
179
  - update author email
@@ -213,3 +231,5 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
213
231
  [0.5.0]: https://github.com/nlsfi/qgis-plugin-dev-tools/releases/tag/v0.5.0
214
232
  [0.6.0]: https://github.com/nlsfi/qgis-plugin-dev-tools/releases/tag/v0.6.0
215
233
  [0.6.1]: https://github.com/nlsfi/qgis-plugin-dev-tools/releases/tag/v0.6.1
234
+ [0.6.2]: https://github.com/nlsfi/qgis-plugin-dev-tools/releases/tag/v0.6.2
235
+ [0.7.0]: https://github.com/nlsfi/qgis-plugin-dev-tools/releases/tag/v0.7.0
@@ -0,0 +1,28 @@
1
+ qgis_plugin_dev_tools/__init__.py,sha256=3mpZORDnC1ScS0CWLrneAkXL6sWo8WJ34AxblGmNXqc,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=_LfVofPZDwIZA9OoDs_cU5d3qptnOzpbljf_s_9s9f0,6137
11
+ qgis_plugin_dev_tools/config/__init__.py,sha256=7l7Iyjst-pqLobiAO2bKLvkQFLVWoq4O_rny9lQvHp8,4678
12
+ qgis_plugin_dev_tools/config/dotenv.py,sha256=WMEcp8Qnt-Vh0XaP8ieQ868U9tH4_pYXTeZ5ZMraJVw,2372
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=f1It2zeTzomxKHm2mbXTBoe3v7MoZ5aXbEkPrL9IrC8,2085
16
+ qgis_plugin_dev_tools/start/config.py,sha256=kkTuPu-TSKeK1MHDZOcoTTw2LZz4psxJamJTfbAY7aQ,1274
17
+ qgis_plugin_dev_tools/start/daemon_server.py,sha256=1i0MWQ0NnbBKZzV6OdT2BZaDEqcIx29ABCIJPorECkg,1845
18
+ qgis_plugin_dev_tools/start/launch.py,sha256=koF0wfT8t8R0_ofy7dN_LSKGM_g9BjHfCKz5DRxc8IE,1612
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.7.0.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
24
+ qgis_plugin_dev_tools-0.7.0.dist-info/METADATA,sha256=rl7tvnGaGFnWWPZj3Ij7eOxIaiSDHCcENv6g-mYhL_Q,10457
25
+ qgis_plugin_dev_tools-0.7.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
26
+ qgis_plugin_dev_tools-0.7.0.dist-info/entry_points.txt,sha256=RwM8y7cN8cRk003jpw-psB4DvifC5foZKSi04IDRBVw,109
27
+ qgis_plugin_dev_tools-0.7.0.dist-info/top_level.txt,sha256=JjwWwRniudLz30UtQJgmIQDeS3HgU5VBgDlKYJhz-nQ,22
28
+ qgis_plugin_dev_tools-0.7.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.41.2)
2
+ Generator: bdist_wheel (0.43.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,28 +0,0 @@
1
- qgis_plugin_dev_tools/__init__.py,sha256=WLzrOZJP6WOaC66FgMUxO0p-qckd3X6c33f51sWH8wM,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=Xx-Xf3psUcSd0FqAgRG9CQFxabkRcNp2NgDIt3yyNoM,4255
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=hrNiCbMAuO8rlh8bqminvXfPAkPHXTOjkB_TH1_HuT8,1676
23
- qgis_plugin_dev_tools-0.6.1.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
24
- qgis_plugin_dev_tools-0.6.1.dist-info/METADATA,sha256=9NmVvNKFbwW64wgLixSvLZE8v9L3XUIIHn0YpLJu0HM,9733
25
- qgis_plugin_dev_tools-0.6.1.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
26
- qgis_plugin_dev_tools-0.6.1.dist-info/entry_points.txt,sha256=RwM8y7cN8cRk003jpw-psB4DvifC5foZKSi04IDRBVw,109
27
- qgis_plugin_dev_tools-0.6.1.dist-info/top_level.txt,sha256=JjwWwRniudLz30UtQJgmIQDeS3HgU5VBgDlKYJhz-nQ,22
28
- qgis_plugin_dev_tools-0.6.1.dist-info/RECORD,,