wexample-wex-addon-dev-python 0.0.61__py3-none-any.whl → 0.0.62__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.
@@ -2,248 +2,49 @@ from __future__ import annotations
2
2
 
3
3
  from typing import TYPE_CHECKING
4
4
 
5
- from wexample_filestate.config_value.readme_content_config_value import (
6
- ReadmeContentConfigValue,
7
- )
8
- from wexample_helpers.classes.field import public_field
9
5
  from wexample_helpers.decorator.base_class import base_class
6
+ from wexample_wex_addon_app.config_value.app_readme_config_value import (
7
+ AppReadmeConfigValue,
8
+ )
10
9
 
11
10
  if TYPE_CHECKING:
12
- from wexample_wex_addon_dev_python.workdir.python_package_workdir import (
13
- PythonPackageWorkdir,
14
- )
11
+ pass
15
12
 
16
13
 
17
14
  @base_class
18
- class PythonPackageReadmeContentConfigValue(ReadmeContentConfigValue):
19
- workdir: PythonPackageWorkdir = public_field(
20
- description="The python package workdir"
21
- )
22
-
23
- def get_templates(self) -> list[str] | None:
24
- # Prepare context for Jinja2 rendering
25
- context = self._get_template_context()
26
-
27
- # Define fixed order of README sections
28
- section_names = [
29
- "title",
30
- "table-of-contents",
31
- "status-compatibility",
32
- "prerequisites",
33
- "installation",
34
- "quickstart",
35
- "basic-usage",
36
- "configuration",
37
- "logging",
38
- "api-reference",
39
- "examples",
40
- "tests",
41
- "code-quality",
42
- "versioning",
43
- "changelog",
44
- "migration-notes",
45
- "roadmap",
46
- "troubleshooting",
47
- "security",
48
- "privacy",
49
- "support",
50
- "contribution-guidelines",
51
- "maintainers",
52
- "license",
53
- "useful-links",
54
- "suite-integration",
55
- "compatibility-matrix",
56
- "requirements",
57
- "dependencies",
58
- "links",
59
- "suite-signature",
60
- ]
61
-
62
- # First pass: collect available sections (excluding title and table-of-contents)
63
- available_sections = []
64
- for section_name in section_names:
65
- if section_name not in ["title", "table-of-contents"]:
66
- # Check if section exists
67
- if self._section_exists(section_name):
68
- available_sections.append(
69
- {
70
- "name": section_name,
71
- "title": self._section_name_to_title(section_name),
72
- "anchor": section_name.replace("_", "-"),
73
- }
74
- )
75
-
76
- # Add available sections to context for table-of-contents
77
- context["available_sections"] = available_sections
15
+ class PythonPackageReadmeContentConfigValue(AppReadmeConfigValue):
16
+ """README generation for Python packages."""
78
17
 
79
- # Render ordered sections (supports both .md and .md.j2)
80
- rendered_content = ""
81
- for section_name in section_names:
82
- section_content = self._render_readme_section(section_name, context)
83
- if section_content:
84
- rendered_content += f"{section_content}\n\n"
18
+ def _get_app_description(self) -> str:
19
+ """Extract description from pyproject.toml."""
20
+ return self.workdir.get_app_config().get("project", {}).get("description")
85
21
 
86
- return [rendered_content]
87
-
88
- def _get_template_context(self) -> dict:
89
- # Use TOMLDocument from the workdir
90
- doc = self.workdir.get_project_config()
91
- project = doc.get("project", {}) if isinstance(doc, dict) else {}
92
-
93
- # Extract information
94
- description = project.get("description", "")
95
- python_version = project.get("requires-python", "")
96
- dependencies = project.get("dependencies", [])
22
+ def _get_app_homepage(self) -> str:
23
+ """Extract homepage URL from pyproject.toml."""
24
+ project = self.workdir.get_app_config()
97
25
  urls = (
98
26
  project.get("urls", {}) if isinstance(project.get("urls", {}), dict) else {}
99
27
  )
100
- # Accept both lowercase and capitalized homepage key variants
101
- homepage = urls.get("homepage") or urls.get("Homepage") or ""
28
+ return urls.get("homepage") or urls.get("Homepage") or ""
29
+
30
+ def _get_project_license(self) -> str | None:
31
+ """Extract license information from pyproject.toml."""
32
+ project = self.workdir.get_app_config()
102
33
  license_field = project.get("license", {})
103
34
  if isinstance(license_field, dict):
104
- license_info = license_field.get("text", "") or license_field.get(
105
- "file", ""
106
- )
107
- else:
108
- license_info = str(license_field) if license_field else ""
109
-
110
- # Format dependencies list
111
- deps_list = "\n".join([f"- {dep}" for dep in dependencies])
112
-
113
- return {
114
- "package_name": self.workdir.get_package_name(),
115
- "version": self.workdir.get_project_version(),
116
- "description": description,
117
- "python_version": python_version,
118
- "dependencies": dependencies,
119
- "deps_list": deps_list,
120
- "homepage": homepage,
121
- "license_info": license_info,
122
- "workdir": self.workdir,
123
- }
124
-
125
- def _render_readme_section(self, section_name: str, context: dict) -> str | None:
126
- """
127
- Render a README section from .md or .md.j2 file with Jinja2 support.
128
-
129
- Searches in three levels (in order):
130
- 1. Package-level templates
131
- 2. Suite-level templates
132
- 3. Default templates (bundled with the module)
133
-
134
- Tries .md.j2 first, then .md. Both formats support Jinja2 variables.
135
-
136
- Args:
137
- section_name: Name of the section (without extension)
138
- context: Jinja2 context variables for rendering
139
-
140
- Returns:
141
- Rendered content or None if section file not found
142
- """
143
- from pathlib import Path
144
-
145
- from jinja2 import Environment, FileSystemLoader, TemplateNotFound
146
- from wexample_app.const.globals import WORKDIR_SETUP_DIR
147
-
148
- workdir_path = self.workdir.get_path()
149
-
150
- search_paths = [
151
- workdir_path / WORKDIR_SETUP_DIR / "knowledge" / "readme", # Package-level
152
- ]
35
+ return license_field.get("text", "") or license_field.get("file", "")
36
+ return str(license_field) if license_field else ""
153
37
 
154
- # Package may have a suite.
155
- suite_path = self.workdir.find_suite_workdir_path()
156
- if suite_path is not None:
157
- search_paths.append(
158
- suite_path
159
- / WORKDIR_SETUP_DIR
160
- / "knowledge"
161
- / "package-readme", # Suite-level
162
- )
163
-
164
- # Add default templates path (bundled with the module)
165
- default_templates_path = (
166
- Path(__file__).parent.parent / "resources" / "readme_templates"
167
- )
168
- search_paths.append(default_templates_path)
169
-
170
- # Try .md.j2 first (Jinja2 template)
171
- for search_path in search_paths:
172
- if not search_path.exists():
173
- continue
174
-
175
- env = Environment(loader=FileSystemLoader(str(search_path)))
176
- try:
177
- template = env.get_template(f"{section_name}.md.j2")
178
- return template.render(context)
179
- except TemplateNotFound:
180
- pass
181
-
182
- # Try .md (static markdown, still rendered with Jinja2)
183
- for search_path in search_paths:
184
- md_path = search_path / f"{section_name}.md"
185
- if md_path.exists():
186
- content = md_path.read_text(encoding="utf-8")
187
- env = Environment(loader=FileSystemLoader(str(search_path)))
188
- template = env.from_string(content)
189
- return template.render(context)
190
-
191
- return None
192
-
193
- def _section_exists(self, section_name: str) -> bool:
194
- """
195
- Check if a section file exists (.md or .md.j2).
196
-
197
- Searches in three levels:
198
- 1. Package-level templates
199
- 2. Suite-level templates
200
- 3. Default templates (bundled with the module)
201
-
202
- Args:
203
- section_name: Name of the section (without extension)
38
+ def _get_template_context(self) -> dict:
39
+ """Build template context with Python-specific variables.
204
40
 
205
- Returns:
206
- True if section file exists, False otherwise
41
+ Adds python_version to the base context.
207
42
  """
208
- from pathlib import Path
43
+ context = super()._get_template_context()
209
44
 
210
- from wexample_app.const.globals import WORKDIR_SETUP_DIR
211
-
212
- workdir_path = self.workdir.get_path()
213
-
214
- search_paths = [
215
- workdir_path / WORKDIR_SETUP_DIR / "knowledge" / "readme",
216
- ]
217
-
218
- # Package may have a suite.
219
- suite_path = self.workdir.find_suite_workdir_path()
220
- if suite_path is not None:
221
- search_paths.append(
222
- suite_path / WORKDIR_SETUP_DIR / "knowledge" / "package-readme",
223
- )
224
-
225
- # Add default templates path (bundled with the module)
226
- default_templates_path = (
227
- Path(__file__).parent.parent / "resources" / "readme_templates"
45
+ # Add Python-specific variable
46
+ context["python_version"] = (
47
+ self.workdir.get_app_config().get("project", {}).get("requires-python", "")
228
48
  )
229
- search_paths.append(default_templates_path)
230
-
231
- for search_path in search_paths:
232
- if (search_path / f"{section_name}.md.j2").exists():
233
- return True
234
- if (search_path / f"{section_name}.md").exists():
235
- return True
236
49
 
237
- return False
238
-
239
- def _section_name_to_title(self, section_name: str) -> str:
240
- """
241
- Convert section name to human-readable title.
242
-
243
- Args:
244
- section_name: Section name (e.g., "basic-usage")
245
-
246
- Returns:
247
- Human-readable title (e.g., "Basic Usage")
248
- """
249
- return section_name.replace("-", " ").replace("_", " ").title()
50
+ return context
File without changes
@@ -0,0 +1,41 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Iterable
4
+ from typing import Any, ClassVar
5
+
6
+ from wexample_app.item.file.iml_file import ImlFile
7
+
8
+
9
+ class PythonAppImlFile(ImlFile):
10
+ """
11
+ IntelliJ IDEA .iml helper tailored for Python apps (src/tests layout, python module type).
12
+ """
13
+
14
+ MODULE_TYPE: ClassVar[str] = "PYTHON_MODULE"
15
+
16
+ def _default_exclude_folders(self) -> Iterable[dict[str, Any]]:
17
+ return (
18
+ {
19
+ "@url": f"{self.MODULE_DIR_URL}/dist",
20
+ },
21
+ )
22
+
23
+ def _default_module_attributes(self) -> dict[str, str]:
24
+ attrs = super()._default_module_attributes()
25
+ attrs.setdefault("@type", self.MODULE_TYPE)
26
+ return attrs
27
+
28
+ def _default_order_entries(self) -> Iterable[dict[str, Any]]:
29
+ return ({"@type": "sourceFolder", "@forTests": "false"},)
30
+
31
+ def _default_source_folders(self) -> Iterable[dict[str, Any]]:
32
+ return (
33
+ {
34
+ "@url": f"{self.MODULE_DIR_URL}/src",
35
+ "@isTestSource": "false",
36
+ },
37
+ {
38
+ "@url": f"{self.MODULE_DIR_URL}/tests",
39
+ "@isTestSource": "true",
40
+ },
41
+ )
@@ -14,29 +14,44 @@ if TYPE_CHECKING:
14
14
 
15
15
 
16
16
  @base_class
17
- class PythonPackageTomlFile(TomlFile):
17
+ class PythonPyprojectTomlFile(TomlFile):
18
18
  def add_dependency(
19
- self, spec: str, optional: bool = False, group: str = "dev"
19
+ self,
20
+ package_name: str,
21
+ version: str,
22
+ operator: str = "==",
23
+ optional: bool = False,
24
+ group: None | str = None,
20
25
  ) -> bool:
21
26
  from packaging.requirements import Requirement
22
27
  from packaging.utils import canonicalize_name
23
28
  from wexample_filestate_python.helpers.toml import toml_sort_string_array
24
29
 
25
- deps = self._get_deps_array(optional=optional, group=group)
30
+ spec = f"{package_name}{operator}{version}"
26
31
  new_req = Requirement(spec)
27
32
  new_name = canonicalize_name(new_req.name)
28
33
 
29
- old_spec = None
30
- for dep in deps:
31
- if canonicalize_name(Requirement(dep).name) == new_name:
32
- old_spec = dep
33
- break
34
+ deps = self._get_deps_array(optional=optional, group=group)
35
+
36
+ # Look for existing dependency
37
+ old_spec = next(
38
+ (d for d in deps if canonicalize_name(Requirement(d).name) == new_name),
39
+ None,
40
+ )
34
41
 
35
- self.remove_dependency_by_name(new_name, optional=optional, group=group)
42
+ # Remove old dep if exists
43
+ if old_spec:
44
+ deps.remove(old_spec)
36
45
 
46
+ # Add new version
37
47
  deps.append(spec)
48
+
49
+ # Sort array
38
50
  toml_sort_string_array(deps)
39
51
 
52
+ # Save file
53
+ self.write_parsed()
54
+
40
55
  return old_spec != spec
41
56
 
42
57
  def dumps(self, content: TOMLDocument | dict | None = None) -> str:
@@ -70,35 +85,21 @@ class PythonPackageTomlFile(TomlFile):
70
85
 
71
86
  return self.find_closest(CodeBaseWorkdir)
72
87
 
73
- def list_dependencies(
88
+ def get_dependencies_versions(
74
89
  self, optional: bool = False, group: str = "dev"
75
- ) -> list[str]:
76
- deps = self._get_deps_array(optional=optional, group=group)
77
- return [str(x) for x in list(deps)]
78
-
79
- def list_dependency_names(
80
- self,
81
- canonicalize_names: bool = True,
82
- optional: bool = False,
83
- group: str = "dev",
84
- ) -> list[str]:
85
- """Return dependency package names derived from list_dependencies().
86
-
87
- If canonicalize_names is True, names are normalized using packaging's
88
- canonicalize_name for robust comparisons (dash/underscore, case, etc.).
89
- """
90
+ ) -> dict[str, str]:
90
91
  from packaging.requirements import Requirement
91
92
  from packaging.utils import canonicalize_name
92
93
 
93
- names: list[str] = []
94
- for spec in self.list_dependencies(optional=optional, group=group):
95
- try:
96
- name = Requirement(spec).name
97
- names.append(canonicalize_name(name) if canonicalize_names else name)
98
- except Exception:
99
- # Skip unparsable entries when deriving names
100
- continue
101
- return names
94
+ deps = self._get_deps_array(optional=optional, group=group)
95
+
96
+ map = {}
97
+ for spec in list(deps):
98
+ req = Requirement(spec)
99
+ # name: version
100
+ map[canonicalize_name(req.name)] = str(req.specifier)
101
+
102
+ return map
102
103
 
103
104
  def optional_group_array(self, group: str):
104
105
  """Ensure and return project.optional-dependencies[group] as multi-line array."""
@@ -305,7 +306,6 @@ class PythonPackageTomlFile(TomlFile):
305
306
 
306
307
  toml_sort_string_array(dev_arr)
307
308
 
308
- # --- Unified dependency accessors (runtime vs optional) ---
309
309
  def _get_deps_array(self, optional: bool = False, group: str = "dev"):
310
310
  """Return TOML array for runtime deps or optional group (multiline)."""
311
311
  return (
File without changes
@@ -1,8 +1,13 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from pathlib import Path
3
4
  from typing import TYPE_CHECKING
4
5
 
5
6
  from wexample_filestate.const.disk import DiskItemType
7
+ from wexample_wex_addon_app.helpers.python import (
8
+ python_install_dependency_in_venv,
9
+ python_is_package_installed_editable_in_venv,
10
+ )
6
11
 
7
12
  from wexample_wex_addon_dev_python.workdir.python_workdir import PythonWorkdir
8
13
 
@@ -20,165 +25,6 @@ if TYPE_CHECKING:
20
25
  class PythonPackageWorkdir(PythonWorkdir):
21
26
  _project_info_cache = None
22
27
 
23
- def app_install(self, env: str | None = None, force: bool = False) -> bool:
24
- from wexample_app.const.env import ENV_NAME_LOCAL
25
- from wexample_helpers.helpers.shell import shell_run
26
-
27
- # In local env, installs packages using pip.
28
- if env == ENV_NAME_LOCAL:
29
- toml_file = self.get_project_config_file()
30
- # Get all dependencies from pyproject.toml
31
- pyproject_toml_dependencies = toml_file.list_dependency_names()
32
-
33
- suite_workdir = self.get_shallow_suite_workdir()
34
-
35
- # Ensure venv is created and configured
36
- app_path = self.get_path()
37
- venv_path = app_path / ".venv"
38
-
39
- # Check if venv exists and is valid (has bin/python)
40
- venv_python = venv_path / "bin" / "python"
41
- venv_is_valid = venv_path.exists() and venv_python.exists()
42
-
43
- if not venv_is_valid:
44
- # Remove corrupted/empty venv if it exists
45
- if venv_path.exists():
46
- self.log(f"Removing invalid venv at {venv_path}", indentation=1)
47
- import shutil
48
-
49
- shutil.rmtree(venv_path)
50
-
51
- # Create new venv
52
- shell_run(
53
- cmd=["pdm", "venv", "create"],
54
- cwd=app_path,
55
- inherit_stdio=True,
56
- )
57
-
58
- # Force PDM to use the local .venv
59
- shell_run(
60
- cmd=["pdm", "use", ".venv"],
61
- cwd=app_path,
62
- inherit_stdio=True,
63
- )
64
-
65
- # Ensure pip is installed in the venv
66
- shell_run(
67
- cmd=[
68
- ".venv/bin/python",
69
- "-m",
70
- "ensurepip",
71
- "--upgrade",
72
- ],
73
- cwd=app_path,
74
- inherit_stdio=True,
75
- )
76
-
77
- # The package is a part of a workdir, so we install manually individual package.
78
- if suite_workdir:
79
- # Get all packages from the suite ordered by dependencies (leaf -> trunk)
80
- suite_packages = suite_workdir.get_ordered_packages()
81
- suite_package_names = {pkg.get_package_name() for pkg in suite_packages}
82
-
83
- # Collect all suite packages that need to be installed (including transitive dependencies)
84
- suite_dependencies_ordered = self._collect_suite_dependencies(
85
- pyproject_toml_dependencies, suite_workdir, suite_package_names
86
- )
87
-
88
- # External dependencies are those not in the suite
89
- external_dependencies = [
90
- dep
91
- for dep in pyproject_toml_dependencies
92
- if dep not in suite_package_names
93
- ]
94
-
95
- # Install external packages first (normal install)
96
- if external_dependencies:
97
- self.subtitle(
98
- f"Installing {len(external_dependencies)} external packages",
99
- indentation=1,
100
- )
101
- for dep in external_dependencies:
102
- self.log(f"Installing {dep}", indentation=2)
103
- shell_run(
104
- cmd=[
105
- ".venv/bin/python",
106
- "-m",
107
- "pip",
108
- "install",
109
- dep,
110
- ],
111
- cwd=app_path,
112
- inherit_stdio=True,
113
- )
114
-
115
- # Install suite packages in editable mode (leaf -> trunk order)
116
- if suite_dependencies_ordered:
117
- self.subtitle(
118
- f"Installing {len(suite_dependencies_ordered)} suite packages in editable mode (leaf -> trunk)",
119
- indentation=1,
120
- )
121
- for pkg in suite_dependencies_ordered:
122
- package_path = pkg.get_path()
123
- package_name = pkg.get_package_name()
124
-
125
- # Check if package is already installed in editable mode at the correct path
126
- if not force and self._is_package_installed_editable(
127
- app_path, package_name, package_path
128
- ):
129
- self.log(
130
- f"Skipping {package_name} (already installed in editable mode)",
131
- indentation=2,
132
- )
133
- continue
134
-
135
- self.log(f"Installing {package_name}", indentation=2)
136
- shell_run(
137
- cmd=[
138
- ".venv/bin/python",
139
- "-m",
140
- "pip",
141
- "install",
142
- "-e",
143
- str(package_path),
144
- ],
145
- cwd=app_path,
146
- inherit_stdio=True,
147
- )
148
-
149
- # Avoid error using -G
150
- dev_group_name = "dev"
151
- if (
152
- len(
153
- self.get_project_config_file().optional_group_array(
154
- group=dev_group_name
155
- )
156
- )
157
- > 0
158
- ):
159
- self._pdm_update_lock_if_needed()
160
-
161
- self.log(f"Installing dev group dependencies")
162
- self._pdm_run_command(command=["install", "-G", dev_group_name])
163
- else:
164
- self.log(
165
- "Skipping dev group install: group 'dev' not defined in pyproject.toml"
166
- )
167
-
168
- return True
169
-
170
- # For non-local environments, use standard PDM install
171
- return super().app_install(
172
- env=env,
173
- force=force,
174
- )
175
-
176
- def depends_from(self, package: PythonPackageWorkdir) -> bool:
177
- for dependence_name in self.get_dependencies():
178
- if package.get_package_name() == dependence_name:
179
- return True
180
- return False
181
-
182
28
  def prepare_value(self, raw_value: DictConfig | None = None) -> DictConfig:
183
29
  from wexample_helpers.helpers.array import array_dict_get_by
184
30
 
@@ -318,7 +164,7 @@ class PythonPackageWorkdir(PythonWorkdir):
318
164
  pkg = suite_workdir.get_package(dep_name)
319
165
  if pkg:
320
166
  # Get dependencies of this suite package and recurse
321
- pkg_dependencies = pkg.get_dependencies()
167
+ pkg_dependencies = pkg.list_dependencies_names()
322
168
  collect_recursive(pkg_dependencies)
323
169
 
324
170
  # Start with direct dependencies from pyproject.toml
@@ -334,87 +180,111 @@ class PythonPackageWorkdir(PythonWorkdir):
334
180
 
335
181
  return suite_deps_ordered
336
182
 
337
- def _get_children_package_workdir_class(self) -> type[FrameworkPackageSuiteWorkdir]:
183
+ def _get_readme_content(self) -> ReadmeContentConfigValue | None:
184
+ from wexample_wex_addon_dev_python.config_value.python_package_readme_config_value import (
185
+ PythonPackageReadmeContentConfigValue,
186
+ )
187
+
188
+ return PythonPackageReadmeContentConfigValue(workdir=self)
189
+
190
+ def _get_suite_package_workdir_class(self) -> type[FrameworkPackageSuiteWorkdir]:
338
191
  from wexample_wex_addon_dev_python.workdir.python_packages_suite_workdir import (
339
192
  PythonPackagesSuiteWorkdir,
340
193
  )
341
194
 
342
195
  return PythonPackagesSuiteWorkdir
343
196
 
344
- def _get_readme_content(self) -> ReadmeContentConfigValue | None:
345
- from wexample_wex_addon_dev_python.config_value.python_package_readme_config_value import (
346
- PythonPackageReadmeContentConfigValue,
197
+ def _install_dependencies_in_venv(
198
+ self, venv_path: Path, env: str | None = None, force: bool = False
199
+ ) -> None:
200
+ from wexample_app.const.env import ENV_NAME_LOCAL
201
+ from wexample_wex_addon_app.helpers.python import (
202
+ python_install_dependencies_in_venv,
347
203
  )
348
204
 
349
- return PythonPackageReadmeContentConfigValue(workdir=self)
205
+ suite_workdir = self.get_shallow_suite_workdir()
206
+ toml_file = self.get_app_config_file()
350
207
 
351
- def _is_package_installed_editable(
352
- self,
353
- app_path,
354
- package_name: str,
355
- package_path,
356
- ) -> bool:
357
- """Check if a package is already installed in editable mode at the correct path."""
358
- import subprocess
359
-
360
- try:
361
- result = subprocess.run(
362
- [".venv/bin/python", "-m", "pip", "show", package_name],
363
- cwd=app_path,
364
- capture_output=True,
365
- text=True,
366
- timeout=5,
367
- )
208
+ # Check for suite only in local env.
209
+ if env == ENV_NAME_LOCAL:
210
+ # Package is part of a suite that may have a venv configured.
211
+ if suite_workdir:
212
+ # Get all dependencies from pyproject.toml
213
+ pyproject_toml_dependencies = toml_file.list_dependencies_names()
368
214
 
369
- if result.returncode != 0:
370
- return False
215
+ # Get all packages from the suite ordered by dependencies (leaf -> trunk)
216
+ suite_packages = suite_workdir.get_ordered_packages()
217
+ suite_package_names = {pkg.get_package_name() for pkg in suite_packages}
371
218
 
372
- # Parse pip show output
373
- output_lines = result.stdout.strip().split("\n")
374
- location = None
375
- editable_location = None
219
+ # Collect all suite packages that need to be installed (including transitive dependencies)
220
+ suite_dependencies_ordered = self._collect_suite_dependencies(
221
+ pyproject_toml_dependencies, suite_workdir, suite_package_names
222
+ )
376
223
 
377
- for line in output_lines:
378
- if line.startswith("Location:"):
379
- location = line.split(":", 1)[1].strip()
380
- elif line.startswith("Editable project location:"):
381
- editable_location = line.split(":", 1)[1].strip()
224
+ # External dependencies are those not in the suite
225
+ external_dependencies = [
226
+ dep
227
+ for dep in pyproject_toml_dependencies
228
+ if dep not in suite_package_names
229
+ ]
382
230
 
383
- # Check if installed in editable mode at the correct path
384
- if editable_location:
385
- from pathlib import Path
231
+ self.subtitle(
232
+ f"Installing {len(external_dependencies)} external packages",
233
+ indentation=1,
234
+ )
235
+ python_install_dependencies_in_venv(
236
+ venv_path=venv_path, names=external_dependencies
237
+ )
386
238
 
387
- return Path(editable_location).resolve() == Path(package_path).resolve()
239
+ # Install suite packages in editable mode (leaf -> trunk order)
240
+ if suite_dependencies_ordered:
241
+ self.subtitle(
242
+ f"Installing {len(suite_dependencies_ordered)} suite packages in editable mode (leaf -> trunk)",
243
+ indentation=1,
244
+ )
388
245
 
389
- return False
246
+ editable_paths = []
390
247
 
391
- except Exception:
392
- # If any error occurs, assume not installed
393
- return False
248
+ for pkg in suite_dependencies_ordered:
249
+ pkg_path = pkg.get_path()
250
+ pkg_name = pkg.get_package_name()
394
251
 
395
- def _pdm_run_command(self, command: list[str]) -> None:
396
- from wexample_helpers.helpers.shell import shell_run
252
+ if force or not python_is_package_installed_editable_in_venv(
253
+ venv_path=venv_path,
254
+ package_name=pkg_name,
255
+ package_path=pkg_path,
256
+ ):
257
+ editable_paths.append(str(pkg_path))
397
258
 
398
- # Install dev group
399
- shell_run(
400
- cmd=["pdm"] + command,
401
- cwd=self.get_path(),
402
- inherit_stdio=True,
403
- )
259
+ python_install_dependencies_in_venv(
260
+ venv_path=venv_path,
261
+ names=editable_paths,
262
+ editable=True,
263
+ )
404
264
 
405
- def _pdm_update_lock_if_needed(self) -> None:
406
- try:
407
- self._pdm_run_command(command=["lock", "--check"])
408
- self.log("pdm.lock is up to date")
265
+ self.subtitle(
266
+ "Installing dev group dependencies",
267
+ indentation=1,
268
+ )
269
+ python_install_dependencies_in_venv(
270
+ venv_path=venv_path,
271
+ names=self.get_app_config_file().optional_group_array(group="dev"),
272
+ )
273
+
274
+ self.subtitle(
275
+ "Installing itself in editable mode",
276
+ indentation=1,
277
+ )
278
+
279
+ # Install itself as editable.
280
+ python_install_dependency_in_venv(
281
+ venv_path=venv_path, name=self.get_path(), editable=True
282
+ )
409
283
 
410
- except Exception:
411
- self.log("pdm.lock is out of date")
284
+ return
412
285
 
413
- try:
414
- self._pdm_run_command(command=["lock"])
415
- self.success("pdm.lock updated")
416
- except Exception:
417
- self.failure("pdm.lock updated")
286
+ # Fallback to parent behaviour
287
+ super()._install_dependencies_in_venv(venv_path=venv_path, env=env, force=force)
418
288
 
419
289
  def _publish(self, force: bool = False) -> None:
420
290
  from wexample_filestate_python.common.pipy_gateway import PipyGateway
@@ -71,94 +71,6 @@ class PythonPackagesSuiteWorkdir(FrameworkPackageSuiteWorkdir):
71
71
 
72
72
  return stack if stack and stack[-1].get_package_name() == target else []
73
73
 
74
- def build_ordered_dependencies(self) -> list[str]:
75
- # Build and validate the dependency map, then compute a stable topological order
76
- return self.topological_order(self.build_dependencies_map())
77
-
78
- def get_dependents(
79
- self, package: PythonPackageWorkdir
80
- ) -> list[PythonPackageWorkdir]:
81
- dependents = []
82
- for neighbor_package in self.get_packages():
83
- if neighbor_package.depends_from(package):
84
- dependents.append(neighbor_package)
85
- return dependents
86
-
87
- def get_ordered_packages(self) -> list[PythonPackageWorkdir]:
88
- """Return package objects ordered leaves -> trunk."""
89
- order = self.build_ordered_dependencies()
90
- by_name = {p.get_package_name(): p for p in self.get_packages()}
91
- return [by_name[n] for n in order]
92
-
93
- def packages_validate_internal_dependencies_declarations(self) -> None:
94
- from wexample_wex_addon_app.exception.dependency_violation_exception import (
95
- DependencyViolationException,
96
- )
97
-
98
- dependencies_map = self.build_dependencies_map()
99
-
100
- self.io.log("Checking packages dependencies consistency...")
101
- self.io.indentation_up()
102
- progress = self.io.progress(
103
- total=len(dependencies_map), print_response=False
104
- ).get_handle()
105
-
106
- for package_name in dependencies_map:
107
- package = self.get_package(package_name)
108
-
109
- for package_name_search in dependencies_map:
110
- searched_package = self.get_package(package_name_search)
111
- imports = package.search_imports_in_codebase(searched_package)
112
- if len(imports) > 0:
113
- dependencies_stack = self.build_dependencies_stack(
114
- package, searched_package, dependencies_map
115
- )
116
-
117
- if len(dependencies_stack) == 0:
118
- # Build a readable list of import locations to help debugging
119
- import_locations = [
120
- f"{res.item.get_path()}:{res.line}:{res.column}"
121
- for res in imports
122
- ]
123
- raise DependencyViolationException(
124
- package_name=package_name,
125
- imported_package=package_name_search,
126
- import_locations=import_locations,
127
- )
128
-
129
- progress.advance(label=f"Package {package.get_project_name()}", step=1)
130
-
131
- self.io.success("Internal dependencies match.")
132
- self.io.indentation_down()
133
-
134
- def topological_order(self, dep_map: dict[str, list[str]]) -> list[str]:
135
- """Deterministic topological order using graphlib.TopologicalSorter.
136
- Returns a leaves -> trunk order (dependencies before dependents).
137
- Raises ValueError on cycles.
138
- """
139
- from graphlib import CycleError, TopologicalSorter
140
-
141
- # Normalize: include every mentioned node and sort for stable results
142
- nodes = set(dep_map.keys()) | {d for deps in dep_map.values() for d in deps}
143
- normalized: dict[str, list[str]] = {
144
- k: sorted([d for d in dep_map.get(k, []) if d in nodes])
145
- for k in sorted(nodes)
146
- }
147
-
148
- ts = TopologicalSorter()
149
- for k, deps in normalized.items():
150
- ts.add(k, *deps)
151
-
152
- try:
153
- order = list(ts.static_order())
154
- except CycleError as e:
155
- # Extract involved nodes if present, otherwise a generic message
156
- msg = getattr(e, "args", [None])[0] or "Cyclic dependencies detected"
157
- raise ValueError(str(msg)) from e
158
-
159
- # Return only local packages (original keys of dep_map)
160
- return [n for n in order if n in dep_map]
161
-
162
74
  def _child_is_package_directory(self, entry: Path) -> bool:
163
75
  return entry.is_dir() and (entry / "pyproject.toml").is_file()
164
76
 
@@ -3,7 +3,6 @@ from __future__ import annotations
3
3
  from pathlib import Path
4
4
  from typing import TYPE_CHECKING
5
5
 
6
- from tomlkit import TOMLDocument
7
6
  from wexample_app.item.file.iml_file import ImlFile
8
7
  from wexample_event.dataclass.event import Event
9
8
  from wexample_event.dataclass.listener_record import EventCallback
@@ -16,8 +15,6 @@ from wexample_filestate_python.const.python_file import (
16
15
  PYTHON_FILE_EXTENSION,
17
16
  PYTHON_FILE_PYTEST_COVERAGE_JSON,
18
17
  )
19
- from wexample_wex_addon_app.helpers.python import python_install_environment
20
- from wexample_wex_addon_app.item.file.python_app_iml_file import PythonAppImlFile
21
18
  from wexample_wex_addon_app.workdir.code_base_workdir import (
22
19
  CodeBaseWorkdir,
23
20
  )
@@ -27,6 +24,7 @@ from wexample_wex_addon_dev_python.const.python import (
27
24
  PYTHON_PYTEST_COV_FORMAT_JSON,
28
25
  PYTHON_PYTEST_COV_REPORT_DIR,
29
26
  )
27
+ from wexample_wex_addon_dev_python.file.python_app_iml_file import PythonAppImlFile
30
28
 
31
29
  if TYPE_CHECKING:
32
30
  from wexample_config.const.types import DictConfig
@@ -41,23 +39,51 @@ if TYPE_CHECKING:
41
39
  )
42
40
  from wexample_helpers.const.types import StructuredData
43
41
 
44
- from wexample_wex_addon_dev_python.file.python_package_toml_file import (
45
- PythonPackageTomlFile,
42
+ from wexample_wex_addon_dev_python.file.python_pyproject_toml_file import (
43
+ PythonPyprojectTomlFile,
46
44
  )
47
45
 
48
46
 
49
47
  class PythonWorkdir(CodeBaseWorkdir):
50
- def app_install(self, env: str | None = None, force: bool = False) -> bool:
48
+ def app_install(self, env: str | None = None, force: bool = False) -> Path:
49
+ from wexample_wex_addon_app.helpers.python import (
50
+ python_ensure_pip_or_fail,
51
+ python_install_environment,
52
+ )
53
+
54
+ # Check if a venv path is somewhere in the config hierarchy.
55
+ venv_path_config = self.search_app_or_suite_runtime_config("python.venv_path")
56
+
57
+ # There is no venv, so create a venv for this project.
58
+ if venv_path_config.is_none():
59
+ venv_path = python_install_environment(path=self.get_path())
60
+ else:
61
+ venv_path = Path(venv_path_config.get_str())
62
+
63
+ self.log(f"Using venv: @path{{{venv_path}}}")
64
+ python_ensure_pip_or_fail(venv_path)
65
+
66
+ self._install_dependencies_in_venv(
67
+ venv_path=venv_path,
68
+ env=env,
69
+ force=force,
70
+ )
71
+
51
72
  # Use standard PDM install
52
- return python_install_environment(path=self.get_path())
73
+ return venv_path
53
74
 
54
- def get_dependencies(self) -> list[str]:
55
- from packaging.requirements import Requirement
75
+ def get_app_config_file(self, reload: bool = True) -> PythonPyprojectTomlFile:
76
+ from wexample_wex_addon_dev_python.file.python_pyproject_toml_file import (
77
+ PythonPyprojectTomlFile,
78
+ )
79
+
80
+ config_file = self.find_by_type(PythonPyprojectTomlFile)
81
+ # Read once to populate content with file source.
82
+ config_file.read_text(reload=reload)
83
+ return config_file
56
84
 
57
- dependencies = []
58
- for dependency in self.get_project_config_file().list_dependency_names():
59
- dependencies.append(Requirement(dependency).name)
60
- return dependencies
85
+ def get_dependencies_versions(self) -> dict[str, str]:
86
+ return self.get_app_config_file().get_dependencies_versions()
61
87
 
62
88
  def get_main_code_file_extension(self) -> str:
63
89
  return PYTHON_FILE_EXTENSION
@@ -86,23 +112,6 @@ class PythonWorkdir(CodeBaseWorkdir):
86
112
 
87
113
  return string_to_kebab_case(self.get_package_import_name())
88
114
 
89
- def get_project_config(self, reload: bool = True) -> TOMLDocument:
90
- """
91
- Fetch the data from the pyproject.toml file.
92
- """
93
- return self.get_project_config_file(reload=reload).read_parsed()
94
-
95
- def get_project_config_file(self, reload: bool = True) -> PythonPackageTomlFile:
96
- from wexample_wex_addon_dev_python.file.python_package_toml_file import (
97
- PythonPackageTomlFile,
98
- )
99
-
100
- config_file = self.find_by_name("pyproject.toml")
101
- assert isinstance(config_file, PythonPackageTomlFile)
102
- # Read once to populate content with file source.
103
- config_file.read_text(reload=reload)
104
- return config_file
105
-
106
115
  def get_python_exec_module_command(self, module_name: str) -> list[str]:
107
116
  return [self.get_python_path(), "-m", module_name]
108
117
 
@@ -151,8 +160,8 @@ class PythonWorkdir(CodeBaseWorkdir):
151
160
  )
152
161
  from wexample_helpers.helpers.array import array_dict_get_by
153
162
 
154
- from wexample_wex_addon_dev_python.file.python_package_toml_file import (
155
- PythonPackageTomlFile,
163
+ from wexample_wex_addon_dev_python.file.python_pyproject_toml_file import (
164
+ PythonPyprojectTomlFile,
156
165
  )
157
166
 
158
167
  raw_value = super().prepare_value(raw_value=raw_value)
@@ -166,7 +175,6 @@ class PythonWorkdir(CodeBaseWorkdir):
166
175
  [
167
176
  ".pdm-python",
168
177
  ".python-version",
169
- ".venv",
170
178
  f"/{PYTHON_FILE_PYTEST_COVERAGE_JSON}",
171
179
  ]
172
180
  )
@@ -183,34 +191,7 @@ class PythonWorkdir(CodeBaseWorkdir):
183
191
  ],
184
192
  },
185
193
  {
186
- "name": ".venv",
187
- "type": DiskItemType.DIRECTORY,
188
- "should_exist": True,
189
- },
190
- # Replaced by pdm
191
- {
192
- "name": "requirements.in",
193
- "type": DiskItemType.FILE,
194
- "should_exist": False,
195
- },
196
- {
197
- "name": "requirements.txt",
198
- "type": DiskItemType.FILE,
199
- "should_exist": False,
200
- },
201
- {
202
- "name": "requirements-dev.in",
203
- "type": DiskItemType.FILE,
204
- "should_exist": False,
205
- },
206
- {
207
- "name": "requirements-dev.txt",
208
- "type": DiskItemType.FILE,
209
- "should_exist": False,
210
- },
211
- # Pdm versions
212
- {
213
- "class": PythonPackageTomlFile,
194
+ "class": PythonPyprojectTomlFile,
214
195
  "name": "pyproject.toml",
215
196
  "type": DiskItemType.FILE,
216
197
  "should_exist": True,
@@ -264,8 +245,8 @@ class PythonWorkdir(CodeBaseWorkdir):
264
245
 
265
246
  def save_dependency(self, package_name: str, version: str) -> bool:
266
247
  """Add or update a dependency with strict version."""
267
- config = self.get_project_config_file()
268
- updated = config.add_dependency(f"{package_name}=={version}")
248
+ config = self.get_app_config_file()
249
+ updated = config.add_dependency(package_name=package_name, version=version)
269
250
 
270
251
  if updated:
271
252
  config.write_parsed()
@@ -274,7 +255,7 @@ class PythonWorkdir(CodeBaseWorkdir):
274
255
 
275
256
  def save_project_config_file(self, config: StructuredData) -> None:
276
257
  """Save the project configuration to pyproject.toml."""
277
- config_file = self.get_project_config_file()
258
+ config_file = self.get_app_config_file()
278
259
  config_file.write(config)
279
260
 
280
261
  def test_get_command(
@@ -327,7 +308,7 @@ class PythonWorkdir(CodeBaseWorkdir):
327
308
  from packaging.requirements import Requirement
328
309
  from packaging.utils import canonicalize_name
329
310
 
330
- config_file = self.get_project_config_file()
311
+ config_file = self.get_app_config_file()
331
312
 
332
313
  # Canonicalize the keys in dependencies_map for consistent matching
333
314
  canonical_map = {
@@ -336,7 +317,7 @@ class PythonWorkdir(CodeBaseWorkdir):
336
317
  }
337
318
 
338
319
  # Get current dependencies
339
- current_deps = config_file.list_dependencies()
320
+ current_deps = config_file.list_dependencies_names()
340
321
 
341
322
  # Update each dependency if it's in the map
342
323
  for dep_spec in current_deps:
@@ -348,7 +329,9 @@ class PythonWorkdir(CodeBaseWorkdir):
348
329
  if canonical_name in canonical_map:
349
330
  new_version = canonical_map[canonical_name]
350
331
  # Use add_dependency which handles removal of old version
351
- config_file.add_dependency(f"{req.name}=={new_version}")
332
+ config_file.add_dependency(
333
+ package_name=req.name, version=new_version
334
+ )
352
335
  except Exception:
353
336
  # Skip unparsable dependencies
354
337
  continue
@@ -524,5 +507,18 @@ class PythonWorkdir(CodeBaseWorkdir):
524
507
  operation=FileRenameOperation, suffix="post", callback=self._on_test_event
525
508
  )
526
509
 
510
+ def _install_dependencies_in_venv(
511
+ self, venv_path: Path, env: str | None = None, force: bool = False
512
+ ) -> None:
513
+ from wexample_wex_addon_app.helpers.python import (
514
+ python_install_dependencies_in_venv,
515
+ )
516
+
517
+ toml_file = self.get_app_config_file()
518
+ # Get all dependencies from pyproject.toml
519
+ python_install_dependencies_in_venv(
520
+ venv_path=venv_path, names=toml_file.list_dependencies_names()
521
+ )
522
+
527
523
  def _on_test_event(self, event: Event) -> None:
528
524
  self.success("A python file has been renamed")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: wexample-wex-addon-dev-python
3
- Version: 0.0.61
3
+ Version: 0.0.62
4
4
  Summary: Python dev addon for wex
5
5
  Author-Email: weeger <contact@wexample.com>
6
6
  License: MIT
@@ -11,13 +11,12 @@ Project-URL: homepage, https://github.com/wexample/python-wex-dev-python
11
11
  Requires-Python: >=3.10
12
12
  Requires-Dist: attrs>=23.1.0
13
13
  Requires-Dist: cattrs>=23.1.0
14
- Requires-Dist: jinja2
15
14
  Requires-Dist: networkx
16
15
  Requires-Dist: pylint
17
16
  Requires-Dist: pyright
18
- Requires-Dist: wexample-filestate-python==0.0.56
19
- Requires-Dist: wexample-wex-addon-app==0.0.53
20
- Requires-Dist: wexample-wex-core==6.0.65
17
+ Requires-Dist: wexample-filestate-python==0.0.57
18
+ Requires-Dist: wexample-wex-addon-app==0.0.54
19
+ Requires-Dist: wexample-wex-core==6.0.66
21
20
  Provides-Extra: dev
22
21
  Requires-Dist: pytest; extra == "dev"
23
22
  Requires-Dist: pytest-cov; extra == "dev"
@@ -25,10 +24,49 @@ Description-Content-Type: text/markdown
25
24
 
26
25
  # wexample-wex-addon-dev-python
27
26
 
28
- Version: 0.0.61
27
+ Version: 0.0.62
29
28
 
30
29
  Python dev addon for wex
31
30
 
31
+ ## Table of Contents
32
+
33
+ - [Status Compatibility](#status-compatibility)
34
+ - [Api Reference](#api-reference)
35
+ - [Tests](#tests)
36
+ - [Code Quality](#code-quality)
37
+ - [Versioning](#versioning)
38
+ - [Changelog](#changelog)
39
+ - [Migration Notes](#migration-notes)
40
+ - [Roadmap](#roadmap)
41
+ - [Security](#security)
42
+ - [Privacy](#privacy)
43
+ - [Support](#support)
44
+ - [Contribution Guidelines](#contribution-guidelines)
45
+ - [Maintainers](#maintainers)
46
+ - [License](#license)
47
+ - [Useful Links](#useful-links)
48
+ - [Suite Integration](#suite-integration)
49
+ - [Compatibility Matrix](#compatibility-matrix)
50
+ - [Dependencies](#dependencies)
51
+ - [Suite Signature](#suite-signature)
52
+
53
+
54
+ ## Status & Compatibility
55
+
56
+ **Maturity**: Production-ready
57
+
58
+ **Python Support**: >=3.10
59
+
60
+ **OS Support**: Linux, macOS, Windows
61
+
62
+ **Status**: Actively maintained
63
+
64
+ ## API Reference
65
+
66
+ Full API documentation is available in the source code docstrings.
67
+
68
+ Key modules and classes are documented with type hints for better IDE support.
69
+
32
70
  ## Tests
33
71
 
34
72
  This project uses `pytest` for testing and `pytest-cov` for code coverage analysis.
@@ -115,13 +153,13 @@ Breaking changes are clearly documented with upgrade paths and examples.
115
153
 
116
154
  Current limitations and planned features are tracked in the GitHub issues.
117
155
 
118
- See the [project roadmap](https://github.com/wexample/python-wex-dev-python/issues) for upcoming features and improvements.
156
+ See the [project roadmap](https://github.com/wexample/python-wex_addon_dev_python/issues) for upcoming features and improvements.
119
157
 
120
158
  ## Security Policy
121
159
 
122
160
  ### Reporting Vulnerabilities
123
161
 
124
- If you discover a security vulnerability, please email security@wexample.com.
162
+ If you discover a security vulnerability, please email contact@wexample.com.
125
163
 
126
164
  **Do not** open public issues for security vulnerabilities.
127
165
 
@@ -144,7 +182,7 @@ Community support is available through GitHub Discussions.
144
182
 
145
183
  ## Contribution Guidelines
146
184
 
147
- We welcome contributions to the Wexample suite!
185
+ We welcome contributions to the Wexample suite!
148
186
 
149
187
  ### How to Contribute
150
188
 
@@ -162,14 +200,16 @@ See [CONTRIBUTORS.md](CONTRIBUTORS.md) for the full list of contributors.
162
200
 
163
201
  ## License
164
202
 
165
- MIT
203
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
204
+
205
+ Free to use in both personal and commercial projects.
166
206
 
167
207
  ## Useful Links
168
208
 
169
- - **Homepage**: https://github.com/wexample/python-wex-dev-python
209
+ - **Homepage**: https://github.com/wexample/python-wex-addon-dev-python
170
210
  - **Documentation**: [docs.wexample.com](https://docs.wexample.com)
171
- - **Issue Tracker**: https://github.com/wexample/python-wex-dev-python/issues
172
- - **Discussions**: https://github.com/wexample/python-wex-dev-python/discussions
211
+ - **Issue Tracker**: https://github.com/wexample/python-wex-addon-dev-python/issues
212
+ - **Discussions**: https://github.com/wexample/python-wex-addon-dev-python/discussions
173
213
  - **PyPI**: [pypi.org/project/wexample-wex-addon-dev-python](https://pypi.org/project/wexample-wex-addon-dev-python/)
174
214
 
175
215
  ## Integration in the Suite
@@ -182,9 +222,27 @@ The suite includes packages for configuration management, file handling, prompts
182
222
 
183
223
  Visit the [Wexample Suite documentation](https://docs.wexample.com) for the complete package ecosystem.
184
224
 
225
+ ## Compatibility Matrix
226
+
227
+ This package is part of the Wexample suite and is compatible with other suite packages.
228
+
229
+ Refer to each package's documentation for specific version compatibility requirements.
230
+
231
+ ## Dependencies
232
+
233
+ - attrs: >=23.1.0
234
+ - cattrs: >=23.1.0
235
+ - networkx:
236
+ - pylint:
237
+ - pyright:
238
+ - wexample-filestate-python: ==0.0.57
239
+ - wexample-wex-addon-app: ==0.0.54
240
+ - wexample-wex-core: ==6.0.66
241
+
242
+
185
243
  # About us
186
244
 
187
- Wexample stands as a cornerstone of the digital ecosystem — a collective of seasoned engineers, researchers, and creators driven by a relentless pursuit of technological excellence. More than a media platform, it has grown into a vibrant community where innovation meets craftsmanship, and where every line of code reflects a commitment to clarity, durability, and shared intelligence.
245
+ [Wexample](https://wexample.com) stands as a cornerstone of the digital ecosystem — a collective of seasoned engineers, researchers, and creators driven by a relentless pursuit of technological excellence. More than a media platform, it has grown into a vibrant community where innovation meets craftsmanship, and where every line of code reflects a commitment to clarity, durability, and shared intelligence.
188
246
 
189
247
  This packages suite embodies this spirit. Trusted by professionals and enthusiasts alike, it delivers a consistent, high-quality foundation for modern development — open, elegant, and battle-tested. Its reputation is built on years of collaboration, refinement, and rigorous attention to detail, making it a natural choice for those who demand both robustness and beauty in their tools.
190
248
 
@@ -1,6 +1,6 @@
1
- wexample_wex_addon_dev_python-0.0.61.dist-info/METADATA,sha256=yqx7BuSnRvvBEUgCXySFgzwZRJz9W_9jjXPa_E3EOF8,6559
2
- wexample_wex_addon_dev_python-0.0.61.dist-info/WHEEL,sha256=tsUv_t7BDeJeRHaSrczbGeuK-TtDpGsWi_JfpzD255I,90
3
- wexample_wex_addon_dev_python-0.0.61.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
1
+ wexample_wex_addon_dev_python-0.0.62.dist-info/METADATA,sha256=wfg0YuX89h46KwubjV5ys0GaCD4PuplKcQeBaALDRLM,8086
2
+ wexample_wex_addon_dev_python-0.0.62.dist-info/WHEEL,sha256=tsUv_t7BDeJeRHaSrczbGeuK-TtDpGsWi_JfpzD255I,90
3
+ wexample_wex_addon_dev_python-0.0.62.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
4
4
  wexample_wex_addon_dev_python/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  wexample_wex_addon_dev_python/__pycache__/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  wexample_wex_addon_dev_python/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -21,14 +21,15 @@ wexample_wex_addon_dev_python/commands/examples/validate.py,sha256=jHiWkEtETkVSY
21
21
  wexample_wex_addon_dev_python/commands/release/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
22
  wexample_wex_addon_dev_python/config_value/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
23
  wexample_wex_addon_dev_python/config_value/__pycache__/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
- wexample_wex_addon_dev_python/config_value/python_package_readme_config_value.py,sha256=Dwy_NBUd6bQoP37XpzVs9FC1WjeXTLH9xaOFHf6GazU,8573
24
+ wexample_wex_addon_dev_python/config_value/python_package_readme_config_value.py,sha256=9L67C6oGwebd8tMSqECmSSpah7nsDTkjEQMNMCWAyp0,1722
25
25
  wexample_wex_addon_dev_python/const/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
26
  wexample_wex_addon_dev_python/const/__pycache__/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
27
  wexample_wex_addon_dev_python/const/package.py,sha256=oRCPhaazJp5TujxF-35rrIYA4FJsqMqCns8lOFKOLeA,451
28
28
  wexample_wex_addon_dev_python/const/python.py,sha256=jxdPt5CnD0dcp4SmobEc_c7XcCkPFfX_lk3SVHsiVpM,203
29
29
  wexample_wex_addon_dev_python/file/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
30
  wexample_wex_addon_dev_python/file/__pycache__/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
- wexample_wex_addon_dev_python/file/python_package_toml_file.py,sha256=ShJrZiiJj8-HrsD92W_1WozE02fi6Avll8VuYPQ25r0,16357
31
+ wexample_wex_addon_dev_python/file/python_app_iml_file.py,sha256=l6YHEILxSGFjOvYWY20zIyAODjOIfuyHsuCfSMw0jVE,1201
32
+ wexample_wex_addon_dev_python/file/python_pyproject_toml_file.py,sha256=EGWwBy5W7zNf8-q7XqXHKV8Qb6FVFoW3qm6Rtwcn4NY,15911
32
33
  wexample_wex_addon_dev_python/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
34
  wexample_wex_addon_dev_python/middleware/__pycache__/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
35
  wexample_wex_addon_dev_python/middleware/each_python_file_middleware.py,sha256=UzNEpedCYf31aNONFl0SuSJnoXRzhJhgEiTCaPark6E,2311
@@ -37,10 +38,9 @@ wexample_wex_addon_dev_python/python_addon_manager.py,sha256=Mmr9F5lOS2gbb8JTB4-
37
38
  wexample_wex_addon_dev_python/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
39
  wexample_wex_addon_dev_python/resources/readme_templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
40
  wexample_wex_addon_dev_python/resources/readme_templates/tests.md.j2,sha256=tKLcDwx7jhQkryXIxWr12AK-hKEaP6Rusu2MrluiABs,1289
40
- wexample_wex_addon_dev_python/resources/readme_templates/title.md.j2,sha256=U-q_U_WhTTwz3enrht3UTfQ9fwioaKUuJqwYyhBtCiA,64
41
41
  wexample_wex_addon_dev_python/workdir/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
42
  wexample_wex_addon_dev_python/workdir/__pycache__/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
- wexample_wex_addon_dev_python/workdir/python_package_workdir.py,sha256=v-6B-rz1WBO94qC-4KzIVhkIBnabmAyV76du_Od2JGU,16398
44
- wexample_wex_addon_dev_python/workdir/python_packages_suite_workdir.py,sha256=41bzNISqVKHirOSVkwBD9wuZokrMDrtWKUjlMRT_GyU,6664
45
- wexample_wex_addon_dev_python/workdir/python_workdir.py,sha256=RE3neOVS8Nju9hTuTjalqe25Q4mZ4Uhx6x2y3XRv18s,19893
46
- wexample_wex_addon_dev_python-0.0.61.dist-info/RECORD,,
43
+ wexample_wex_addon_dev_python/workdir/python_package_workdir.py,sha256=djUfgI1MCn9KOgYCYiwFIpZaGQ28spaiOz6t8gL6FoA,11780
44
+ wexample_wex_addon_dev_python/workdir/python_packages_suite_workdir.py,sha256=ICHHewLpsXiheYzGDEahphBryH98ZVezWzSAy6A5w5I,2874
45
+ wexample_wex_addon_dev_python/workdir/python_workdir.py,sha256=ERpSw5qti1NmHiufOix6bS5lW2vIvclr82SKwlWOhKk,19681
46
+ wexample_wex_addon_dev_python-0.0.62.dist-info/RECORD,,
@@ -1,5 +0,0 @@
1
- # {{ package_name }}
2
-
3
- Version: {{ version }}
4
-
5
- {{ description }}