wexample-wex-addon-dev-python 0.0.49__py3-none-any.whl → 0.0.53__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.
- wexample_wex_addon_dev_python/config_value/python_package_readme_config_value.py +40 -34
- wexample_wex_addon_dev_python/file/python_package_toml_file.py +1 -0
- wexample_wex_addon_dev_python/resources/readme_templates/tests.md.j2 +48 -0
- wexample_wex_addon_dev_python/workdir/python_package_workdir.py +29 -31
- wexample_wex_addon_dev_python/workdir/python_packages_suite_workdir.py +2 -5
- wexample_wex_addon_dev_python/workdir/python_workdir.py +68 -14
- {wexample_wex_addon_dev_python-0.0.49.dist-info → wexample_wex_addon_dev_python-0.0.53.dist-info}/METADATA +54 -5
- {wexample_wex_addon_dev_python-0.0.49.dist-info → wexample_wex_addon_dev_python-0.0.53.dist-info}/RECORD +10 -9
- {wexample_wex_addon_dev_python-0.0.49.dist-info → wexample_wex_addon_dev_python-0.0.53.dist-info}/WHEEL +0 -0
- {wexample_wex_addon_dev_python-0.0.49.dist-info → wexample_wex_addon_dev_python-0.0.53.dist-info}/entry_points.txt +0 -0
|
@@ -21,41 +21,8 @@ class PythonPackageReadmeContentConfigValue(ReadmeContentConfigValue):
|
|
|
21
21
|
)
|
|
22
22
|
|
|
23
23
|
def get_templates(self) -> list[str] | None:
|
|
24
|
-
# Use TOMLDocument from the workdir
|
|
25
|
-
doc = self.workdir.get_project_config()
|
|
26
|
-
project = doc.get("project", {}) if isinstance(doc, dict) else {}
|
|
27
|
-
|
|
28
|
-
# Extract information
|
|
29
|
-
description = project.get("description", "")
|
|
30
|
-
python_version = project.get("requires-python", "")
|
|
31
|
-
dependencies = project.get("dependencies", [])
|
|
32
|
-
urls = (
|
|
33
|
-
project.get("urls", {}) if isinstance(project.get("urls", {}), dict) else {}
|
|
34
|
-
)
|
|
35
|
-
# Accept both lowercase and capitalized homepage key variants
|
|
36
|
-
homepage = urls.get("homepage") or urls.get("Homepage") or ""
|
|
37
|
-
license_field = project.get("license", {})
|
|
38
|
-
if isinstance(license_field, dict):
|
|
39
|
-
license_info = license_field.get("text", "") or license_field.get(
|
|
40
|
-
"file", ""
|
|
41
|
-
)
|
|
42
|
-
else:
|
|
43
|
-
license_info = str(license_field) if license_field else ""
|
|
44
|
-
|
|
45
|
-
# Format dependencies list
|
|
46
|
-
deps_list = "\n".join([f"- {dep}" for dep in dependencies])
|
|
47
|
-
|
|
48
24
|
# Prepare context for Jinja2 rendering
|
|
49
|
-
context =
|
|
50
|
-
"package_name": self.workdir.get_package_name(),
|
|
51
|
-
"version": self.workdir.get_project_version(),
|
|
52
|
-
"description": description,
|
|
53
|
-
"python_version": python_version,
|
|
54
|
-
"dependencies": dependencies,
|
|
55
|
-
"deps_list": deps_list,
|
|
56
|
-
"homepage": homepage,
|
|
57
|
-
"license_info": license_info,
|
|
58
|
-
}
|
|
25
|
+
context = self._get_template_context()
|
|
59
26
|
|
|
60
27
|
# Define fixed order of README sections
|
|
61
28
|
section_names = [
|
|
@@ -118,6 +85,43 @@ class PythonPackageReadmeContentConfigValue(ReadmeContentConfigValue):
|
|
|
118
85
|
|
|
119
86
|
return [rendered_content]
|
|
120
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", [])
|
|
97
|
+
urls = (
|
|
98
|
+
project.get("urls", {}) if isinstance(project.get("urls", {}), dict) else {}
|
|
99
|
+
)
|
|
100
|
+
# Accept both lowercase and capitalized homepage key variants
|
|
101
|
+
homepage = urls.get("homepage") or urls.get("Homepage") or ""
|
|
102
|
+
license_field = project.get("license", {})
|
|
103
|
+
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
|
+
|
|
121
125
|
def _render_readme_section(self, section_name: str, context: dict) -> str | None:
|
|
122
126
|
"""
|
|
123
127
|
Render a README section from .md or .md.j2 file with Jinja2 support.
|
|
@@ -137,6 +141,7 @@ class PythonPackageReadmeContentConfigValue(ReadmeContentConfigValue):
|
|
|
137
141
|
Rendered content or None if section file not found
|
|
138
142
|
"""
|
|
139
143
|
from pathlib import Path
|
|
144
|
+
|
|
140
145
|
from jinja2 import Environment, FileSystemLoader, TemplateNotFound
|
|
141
146
|
from wexample_app.const.globals import WORKDIR_SETUP_DIR
|
|
142
147
|
|
|
@@ -201,6 +206,7 @@ class PythonPackageReadmeContentConfigValue(ReadmeContentConfigValue):
|
|
|
201
206
|
True if section file exists, False otherwise
|
|
202
207
|
"""
|
|
203
208
|
from pathlib import Path
|
|
209
|
+
|
|
204
210
|
from wexample_app.const.globals import WORKDIR_SETUP_DIR
|
|
205
211
|
|
|
206
212
|
workdir_path = self.workdir.get_path()
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
## Tests
|
|
2
|
+
|
|
3
|
+
This project uses `pytest` for testing and `pytest-cov` for code coverage analysis.
|
|
4
|
+
|
|
5
|
+
### Installation
|
|
6
|
+
|
|
7
|
+
First, install the required testing dependencies:
|
|
8
|
+
```bash
|
|
9
|
+
.venv/bin/python -m pip install pytest pytest-cov
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
### Basic Usage
|
|
13
|
+
|
|
14
|
+
Run all tests with coverage:
|
|
15
|
+
```bash
|
|
16
|
+
.venv/bin/python -m pytest --cov
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Common Commands
|
|
20
|
+
```bash
|
|
21
|
+
# Run tests with coverage for a specific module
|
|
22
|
+
.venv/bin/python -m pytest --cov=your_module
|
|
23
|
+
|
|
24
|
+
# Show which lines are not covered
|
|
25
|
+
.venv/bin/python -m pytest --cov=your_module --cov-report=term-missing
|
|
26
|
+
|
|
27
|
+
# Generate an HTML coverage report
|
|
28
|
+
.venv/bin/python -m pytest --cov=your_module --cov-report=html
|
|
29
|
+
|
|
30
|
+
# Combine terminal and HTML reports
|
|
31
|
+
.venv/bin/python -m pytest --cov=your_module --cov-report=term-missing --cov-report=html
|
|
32
|
+
|
|
33
|
+
# Run specific test file with coverage
|
|
34
|
+
.venv/bin/python -m pytest tests/test_file.py --cov=your_module --cov-report=term-missing
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Viewing HTML Reports
|
|
38
|
+
|
|
39
|
+
After generating an HTML report, open `htmlcov/index.html` in your browser to view detailed line-by-line coverage information.
|
|
40
|
+
|
|
41
|
+
### Coverage Threshold
|
|
42
|
+
|
|
43
|
+
To enforce a minimum coverage percentage:
|
|
44
|
+
```bash
|
|
45
|
+
.venv/bin/python -m pytest --cov=your_module --cov-fail-under=80
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
This will cause the test suite to fail if coverage drops below 80%.
|
|
@@ -10,8 +10,6 @@ if TYPE_CHECKING:
|
|
|
10
10
|
ReadmeContentConfigValue,
|
|
11
11
|
)
|
|
12
12
|
from wexample_filestate.utils.search_result import SearchResult
|
|
13
|
-
from wexample_helpers.const.types import PathOrString
|
|
14
|
-
from wexample_prompt.common.progress.progress_handle import ProgressHandle
|
|
15
13
|
from wexample_wex_addon_app.workdir.framework_packages_suite_workdir import (
|
|
16
14
|
FrameworkPackageSuiteWorkdir,
|
|
17
15
|
)
|
|
@@ -98,7 +96,9 @@ class PythonPackageWorkdir(PythonWorkdir):
|
|
|
98
96
|
|
|
99
97
|
if env == ENV_NAME_LOCAL:
|
|
100
98
|
# Get all dependencies from pyproject.toml
|
|
101
|
-
pyproject_toml_dependencies =
|
|
99
|
+
pyproject_toml_dependencies = (
|
|
100
|
+
self.get_project_config_file().list_dependency_names()
|
|
101
|
+
)
|
|
102
102
|
suite_workdir = self.get_suite_workdir()
|
|
103
103
|
|
|
104
104
|
# Ensure venv is created and configured
|
|
@@ -114,6 +114,7 @@ class PythonPackageWorkdir(PythonWorkdir):
|
|
|
114
114
|
if venv_path.exists():
|
|
115
115
|
self.io.log(f"Removing invalid venv at {venv_path}", indentation=1)
|
|
116
116
|
import shutil
|
|
117
|
+
|
|
117
118
|
shutil.rmtree(venv_path)
|
|
118
119
|
|
|
119
120
|
# Create new venv
|
|
@@ -149,14 +150,13 @@ class PythonPackageWorkdir(PythonWorkdir):
|
|
|
149
150
|
|
|
150
151
|
# Collect all suite packages that need to be installed (including transitive dependencies)
|
|
151
152
|
suite_dependencies_ordered = self._collect_suite_dependencies(
|
|
152
|
-
pyproject_toml_dependencies,
|
|
153
|
-
suite_workdir,
|
|
154
|
-
suite_package_names
|
|
153
|
+
pyproject_toml_dependencies, suite_workdir, suite_package_names
|
|
155
154
|
)
|
|
156
155
|
|
|
157
156
|
# External dependencies are those not in the suite
|
|
158
157
|
external_dependencies = [
|
|
159
|
-
dep
|
|
158
|
+
dep
|
|
159
|
+
for dep in pyproject_toml_dependencies
|
|
160
160
|
if dep not in suite_package_names
|
|
161
161
|
]
|
|
162
162
|
|
|
@@ -191,8 +191,13 @@ class PythonPackageWorkdir(PythonWorkdir):
|
|
|
191
191
|
package_name = pkg.get_package_name()
|
|
192
192
|
|
|
193
193
|
# Check if package is already installed in editable mode at the correct path
|
|
194
|
-
if not force and self._is_package_installed_editable(
|
|
195
|
-
|
|
194
|
+
if not force and self._is_package_installed_editable(
|
|
195
|
+
app_path, package_name, package_path
|
|
196
|
+
):
|
|
197
|
+
self.io.log(
|
|
198
|
+
f"Skipping {package_name} (already installed in editable mode)",
|
|
199
|
+
indentation=2,
|
|
200
|
+
)
|
|
196
201
|
continue
|
|
197
202
|
|
|
198
203
|
self.io.log(f"Installing {package_name}", indentation=2)
|
|
@@ -217,10 +222,10 @@ class PythonPackageWorkdir(PythonWorkdir):
|
|
|
217
222
|
)
|
|
218
223
|
|
|
219
224
|
def _is_package_installed_editable(
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
225
|
+
self,
|
|
226
|
+
app_path,
|
|
227
|
+
package_name: str,
|
|
228
|
+
package_path,
|
|
224
229
|
) -> bool:
|
|
225
230
|
"""Check if a package is already installed in editable mode at the correct path."""
|
|
226
231
|
import subprocess
|
|
@@ -251,6 +256,7 @@ class PythonPackageWorkdir(PythonWorkdir):
|
|
|
251
256
|
# Check if installed in editable mode at the correct path
|
|
252
257
|
if editable_location:
|
|
253
258
|
from pathlib import Path
|
|
259
|
+
|
|
254
260
|
return Path(editable_location).resolve() == Path(package_path).resolve()
|
|
255
261
|
|
|
256
262
|
return False
|
|
@@ -260,13 +266,13 @@ class PythonPackageWorkdir(PythonWorkdir):
|
|
|
260
266
|
return False
|
|
261
267
|
|
|
262
268
|
def _collect_suite_dependencies(
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
269
|
+
self,
|
|
270
|
+
direct_dependencies: list[str],
|
|
271
|
+
suite_workdir,
|
|
272
|
+
suite_package_names: set[str],
|
|
267
273
|
) -> list:
|
|
268
274
|
"""Collect all suite packages recursively that need to be installed in editable mode.
|
|
269
|
-
|
|
275
|
+
|
|
270
276
|
Returns a list of suite package objects ordered leaf -> trunk.
|
|
271
277
|
"""
|
|
272
278
|
suite_deps_to_install = set()
|
|
@@ -293,18 +299,17 @@ class PythonPackageWorkdir(PythonWorkdir):
|
|
|
293
299
|
# Order suite packages by dependency (leaf -> trunk)
|
|
294
300
|
all_ordered_packages = suite_workdir.get_ordered_packages()
|
|
295
301
|
suite_deps_ordered = [
|
|
296
|
-
pkg
|
|
302
|
+
pkg
|
|
303
|
+
for pkg in all_ordered_packages
|
|
297
304
|
if pkg.get_package_name() in suite_deps_to_install
|
|
298
305
|
]
|
|
299
306
|
|
|
300
307
|
return suite_deps_ordered
|
|
301
308
|
|
|
302
|
-
def
|
|
309
|
+
def _publish(self, force: bool = False) -> None:
|
|
303
310
|
from wexample_filestate_python.common.pipy_gateway import PipyGateway
|
|
304
311
|
from wexample_helpers.helpers.shell import shell_run
|
|
305
312
|
|
|
306
|
-
super().publish()
|
|
307
|
-
|
|
308
313
|
client = PipyGateway(parent_io_handler=self)
|
|
309
314
|
|
|
310
315
|
package_name = self.get_package_name()
|
|
@@ -327,15 +332,8 @@ class PythonPackageWorkdir(PythonWorkdir):
|
|
|
327
332
|
|
|
328
333
|
shell_run(publish_cmd, inherit_stdio=True, cwd=self.get_path())
|
|
329
334
|
|
|
330
|
-
def save_dependency_from_package(self, package: PythonPackageWorkdir) -> None:
|
|
331
|
-
"""Add a dependency from another package, use strict version as this is the intended internal management."""
|
|
332
|
-
self.save_dependency(
|
|
333
|
-
package_name=package.get_package_name(),
|
|
334
|
-
version=package.get_project_version()
|
|
335
|
-
)
|
|
336
|
-
|
|
337
335
|
def search_imports_in_codebase(
|
|
338
|
-
|
|
336
|
+
self, searched_package: PythonPackageWorkdir
|
|
339
337
|
) -> list[SearchResult]:
|
|
340
338
|
"""Find import statements that reference the given package.
|
|
341
339
|
|
|
@@ -357,7 +355,7 @@ class PythonPackageWorkdir(PythonWorkdir):
|
|
|
357
355
|
return self.search_in_codebase(pattern, regex=True, flags=re.MULTILINE)
|
|
358
356
|
|
|
359
357
|
def search_in_codebase(
|
|
360
|
-
|
|
358
|
+
self, string: str, *, regex: bool = False, flags: int = 0
|
|
361
359
|
) -> list[SearchResult]:
|
|
362
360
|
from wexample_filestate.utils.search_result import SearchResult
|
|
363
361
|
from wexample_filestate_python.file.python_file import PythonFile
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from typing import TYPE_CHECKING
|
|
4
|
-
|
|
5
|
-
from wexample_wex_addon_app.exception.dependency_violation_exception import (
|
|
6
|
-
DependencyViolationException,
|
|
7
|
-
)
|
|
8
4
|
from wexample_wex_addon_app.workdir.framework_packages_suite_workdir import (
|
|
9
5
|
FrameworkPackageSuiteWorkdir,
|
|
10
6
|
)
|
|
@@ -93,12 +89,13 @@ class PythonPackagesSuiteWorkdir(FrameworkPackageSuiteWorkdir):
|
|
|
93
89
|
return [by_name[n] for n in order]
|
|
94
90
|
|
|
95
91
|
def packages_validate_internal_dependencies_declarations(self) -> None:
|
|
92
|
+
from wexample_wex_addon_app.exception.dependency_violation_exception import DependencyViolationException
|
|
96
93
|
dependencies_map = self.build_dependencies_map()
|
|
97
94
|
|
|
98
95
|
self.io.log("Checking packages dependencies consistency...")
|
|
99
96
|
self.io.indentation_up()
|
|
100
97
|
progress = self.io.progress(
|
|
101
|
-
total=len(dependencies_map),
|
|
98
|
+
total=len(dependencies_map), print_response=False
|
|
102
99
|
).get_handle()
|
|
103
100
|
|
|
104
101
|
for package_name in dependencies_map:
|
|
@@ -1,9 +1,12 @@
|
|
|
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 tomlkit import TOMLDocument
|
|
6
7
|
|
|
8
|
+
from wexample_filestate.item.file.json_file import JsonFile
|
|
9
|
+
from wexample_filestate_python.const.python_file import PYTHON_FILE_PYTEST_COVERAGE_JSON
|
|
7
10
|
from wexample_wex_addon_app.helpers.python import python_install_environment
|
|
8
11
|
from wexample_wex_addon_app.workdir.code_base_workdir import (
|
|
9
12
|
CodeBaseWorkdir,
|
|
@@ -20,13 +23,58 @@ if TYPE_CHECKING:
|
|
|
20
23
|
from wexample_filestate.option.children_file_factory_option import (
|
|
21
24
|
ChildrenFileFactoryOption,
|
|
22
25
|
)
|
|
23
|
-
from wexample_helpers.const.types import StructuredData
|
|
26
|
+
from wexample_helpers.const.types import StructuredData
|
|
24
27
|
from wexample_wex_addon_dev_python.file.python_package_toml_file import (
|
|
25
28
|
PythonPackageTomlFile,
|
|
26
29
|
)
|
|
27
30
|
|
|
28
31
|
|
|
29
32
|
class PythonWorkdir(CodeBaseWorkdir):
|
|
33
|
+
def get_venv_path(self) -> Path:
|
|
34
|
+
return self.get_path() / ".venv"
|
|
35
|
+
|
|
36
|
+
def get_venv_bin_path(self) -> Path:
|
|
37
|
+
return self.get_venv_path() / "bin"
|
|
38
|
+
|
|
39
|
+
def get_python_path(self) -> Path:
|
|
40
|
+
return self.get_venv_bin_path() / "python"
|
|
41
|
+
|
|
42
|
+
def get_python_exec_module_command(self, module_name: str) -> list[str]:
|
|
43
|
+
return [self.get_python_path(), "-m", module_name]
|
|
44
|
+
|
|
45
|
+
def test_run(self) -> None:
|
|
46
|
+
self.shell_run_for_app(cmd=self.test_get_command())
|
|
47
|
+
|
|
48
|
+
json_file = JsonFile.create_from_path(
|
|
49
|
+
path=self.get_path() / PYTHON_FILE_PYTEST_COVERAGE_JSON
|
|
50
|
+
)
|
|
51
|
+
totals = json_file.read_config().search("totals", default={}).get_dict()
|
|
52
|
+
|
|
53
|
+
config_file = self.get_config_file()
|
|
54
|
+
config = config_file.read_config()
|
|
55
|
+
config.set_by_path(
|
|
56
|
+
"test.coverage.last_report",
|
|
57
|
+
{
|
|
58
|
+
"covered": totals.get("covered_lines", 0),
|
|
59
|
+
"excluded": totals.get("excluded_lines", 0),
|
|
60
|
+
"missing": totals.get("missing_lines", 0),
|
|
61
|
+
"percent": totals.get("percent_covered", 0),
|
|
62
|
+
"total": totals.get("num_statements", 0),
|
|
63
|
+
},
|
|
64
|
+
)
|
|
65
|
+
config_file.write_config()
|
|
66
|
+
|
|
67
|
+
def test_get_command(self) -> list[str]:
|
|
68
|
+
cmd = self.get_python_exec_module_command("pytest")
|
|
69
|
+
cmd.extend(
|
|
70
|
+
[
|
|
71
|
+
"--cov",
|
|
72
|
+
"--cov-report=json",
|
|
73
|
+
]
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
return cmd
|
|
77
|
+
|
|
30
78
|
def app_install(self, env: str | None = None, force: bool = False) -> bool:
|
|
31
79
|
# Use standard PDM install
|
|
32
80
|
return python_install_environment(path=self.get_path())
|
|
@@ -55,8 +103,7 @@ class PythonWorkdir(CodeBaseWorkdir):
|
|
|
55
103
|
return options
|
|
56
104
|
|
|
57
105
|
def get_package_import_name(self) -> str:
|
|
58
|
-
"""Get the full package import name with vendor prefix.
|
|
59
|
-
"""
|
|
106
|
+
"""Get the full package import name with vendor prefix."""
|
|
60
107
|
return f"{self.get_vendor_name()}_{self.get_project_name()}"
|
|
61
108
|
|
|
62
109
|
def get_package_name(self) -> str:
|
|
@@ -89,6 +136,7 @@ class PythonWorkdir(CodeBaseWorkdir):
|
|
|
89
136
|
".pdm-python",
|
|
90
137
|
".python-version",
|
|
91
138
|
".venv",
|
|
139
|
+
f"/{PYTHON_FILE_PYTEST_COVERAGE_JSON}",
|
|
92
140
|
]
|
|
93
141
|
)
|
|
94
142
|
|
|
@@ -213,9 +261,15 @@ class PythonWorkdir(CodeBaseWorkdir):
|
|
|
213
261
|
from wexample_helpers.helpers.string import string_to_snake_case
|
|
214
262
|
|
|
215
263
|
vendor_prefix = self.get_vendor_name()
|
|
216
|
-
return
|
|
217
|
-
|
|
218
|
-
|
|
264
|
+
return (
|
|
265
|
+
vendor_prefix
|
|
266
|
+
+ "_"
|
|
267
|
+
+ string_to_snake_case(
|
|
268
|
+
os.path.basename(
|
|
269
|
+
os.path.dirname(
|
|
270
|
+
os.path.realpath(option.get_parent_item().get_path())
|
|
271
|
+
)
|
|
272
|
+
)
|
|
219
273
|
)
|
|
220
274
|
)
|
|
221
275
|
|
|
@@ -259,7 +313,7 @@ class PythonWorkdir(CodeBaseWorkdir):
|
|
|
259
313
|
name_pattern=r"^.*\.py$",
|
|
260
314
|
recursive=True,
|
|
261
315
|
)
|
|
262
|
-
|
|
316
|
+
|
|
263
317
|
def get_project_config_file(self, reload: bool = True) -> PythonPackageTomlFile:
|
|
264
318
|
from wexample_wex_addon_dev_python.file.python_package_toml_file import (
|
|
265
319
|
PythonPackageTomlFile,
|
|
@@ -290,7 +344,7 @@ class PythonWorkdir(CodeBaseWorkdir):
|
|
|
290
344
|
|
|
291
345
|
def update_dependencies(self, dependencies_map: dict[str, str]) -> None:
|
|
292
346
|
"""Update dependencies versions based on the provided map.
|
|
293
|
-
|
|
347
|
+
|
|
294
348
|
Args:
|
|
295
349
|
dependencies_map: Dictionary mapping package names to their new versions.
|
|
296
350
|
Example: {"wexample-helpers": "0.2.3", "attrs": "23.1.0"}
|
|
@@ -302,19 +356,19 @@ class PythonWorkdir(CodeBaseWorkdir):
|
|
|
302
356
|
|
|
303
357
|
# Canonicalize the keys in dependencies_map for consistent matching
|
|
304
358
|
canonical_map = {
|
|
305
|
-
canonicalize_name(name): version
|
|
359
|
+
canonicalize_name(name): version
|
|
306
360
|
for name, version in dependencies_map.items()
|
|
307
361
|
}
|
|
308
|
-
|
|
362
|
+
|
|
309
363
|
# Get current dependencies
|
|
310
364
|
current_deps = config_file.list_dependencies()
|
|
311
|
-
|
|
365
|
+
|
|
312
366
|
# Update each dependency if it's in the map
|
|
313
367
|
for dep_spec in current_deps:
|
|
314
368
|
try:
|
|
315
369
|
req = Requirement(dep_spec)
|
|
316
370
|
canonical_name = canonicalize_name(req.name)
|
|
317
|
-
|
|
371
|
+
|
|
318
372
|
# If this dependency is in our update map, update it
|
|
319
373
|
if canonical_name in canonical_map:
|
|
320
374
|
new_version = canonical_map[canonical_name]
|
|
@@ -323,6 +377,6 @@ class PythonWorkdir(CodeBaseWorkdir):
|
|
|
323
377
|
except Exception:
|
|
324
378
|
# Skip unparsable dependencies
|
|
325
379
|
continue
|
|
326
|
-
|
|
380
|
+
|
|
327
381
|
# Save the updated config
|
|
328
|
-
config_file.write_parsed()
|
|
382
|
+
config_file.write_parsed()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: wexample-wex-addon-dev-python
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.53
|
|
4
4
|
Summary: Python dev addon for wex
|
|
5
5
|
Author-Email: weeger <contact@wexample.com>
|
|
6
6
|
License: MIT
|
|
@@ -15,9 +15,9 @@ Requires-Dist: jinja2
|
|
|
15
15
|
Requires-Dist: networkx
|
|
16
16
|
Requires-Dist: pylint
|
|
17
17
|
Requires-Dist: pyright
|
|
18
|
-
Requires-Dist: wexample-filestate-python==0.0.
|
|
19
|
-
Requires-Dist: wexample-wex-addon-app==0.0.
|
|
20
|
-
Requires-Dist: wexample-wex-core==6.0.
|
|
18
|
+
Requires-Dist: wexample-filestate-python==0.0.48
|
|
19
|
+
Requires-Dist: wexample-wex-addon-app==0.0.49
|
|
20
|
+
Requires-Dist: wexample-wex-core==6.0.55
|
|
21
21
|
Provides-Extra: dev
|
|
22
22
|
Requires-Dist: pytest; extra == "dev"
|
|
23
23
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
@@ -25,10 +25,59 @@ Description-Content-Type: text/markdown
|
|
|
25
25
|
|
|
26
26
|
# wexample-wex-addon-dev-python
|
|
27
27
|
|
|
28
|
-
Version: 0.0.
|
|
28
|
+
Version: 0.0.53
|
|
29
29
|
|
|
30
30
|
Python dev addon for wex
|
|
31
31
|
|
|
32
|
+
## Tests
|
|
33
|
+
|
|
34
|
+
This project uses `pytest` for testing and `pytest-cov` for code coverage analysis.
|
|
35
|
+
|
|
36
|
+
### Installation
|
|
37
|
+
|
|
38
|
+
First, install the required testing dependencies:
|
|
39
|
+
```bash
|
|
40
|
+
.venv/bin/python -m pip install pytest pytest-cov
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Basic Usage
|
|
44
|
+
|
|
45
|
+
Run all tests with coverage:
|
|
46
|
+
```bash
|
|
47
|
+
.venv/bin/python -m pytest --cov
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Common Commands
|
|
51
|
+
```bash
|
|
52
|
+
# Run tests with coverage for a specific module
|
|
53
|
+
.venv/bin/python -m pytest --cov=your_module
|
|
54
|
+
|
|
55
|
+
# Show which lines are not covered
|
|
56
|
+
.venv/bin/python -m pytest --cov=your_module --cov-report=term-missing
|
|
57
|
+
|
|
58
|
+
# Generate an HTML coverage report
|
|
59
|
+
.venv/bin/python -m pytest --cov=your_module --cov-report=html
|
|
60
|
+
|
|
61
|
+
# Combine terminal and HTML reports
|
|
62
|
+
.venv/bin/python -m pytest --cov=your_module --cov-report=term-missing --cov-report=html
|
|
63
|
+
|
|
64
|
+
# Run specific test file with coverage
|
|
65
|
+
.venv/bin/python -m pytest tests/test_file.py --cov=your_module --cov-report=term-missing
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Viewing HTML Reports
|
|
69
|
+
|
|
70
|
+
After generating an HTML report, open `htmlcov/index.html` in your browser to view detailed line-by-line coverage information.
|
|
71
|
+
|
|
72
|
+
### Coverage Threshold
|
|
73
|
+
|
|
74
|
+
To enforce a minimum coverage percentage:
|
|
75
|
+
```bash
|
|
76
|
+
.venv/bin/python -m pytest --cov=your_module --cov-fail-under=80
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
This will cause the test suite to fail if coverage drops below 80%.
|
|
80
|
+
|
|
32
81
|
## Code Quality & Typing
|
|
33
82
|
|
|
34
83
|
All the suite packages follow strict quality standards:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
wexample_wex_addon_dev_python-0.0.
|
|
2
|
-
wexample_wex_addon_dev_python-0.0.
|
|
3
|
-
wexample_wex_addon_dev_python-0.0.
|
|
1
|
+
wexample_wex_addon_dev_python-0.0.53.dist-info/METADATA,sha256=9CcuVf5ioaUJoFnBrKESDfwlL_5XRgATtWrFYZ3gTck,6541
|
|
2
|
+
wexample_wex_addon_dev_python-0.0.53.dist-info/WHEEL,sha256=9P2ygRxDrTJz3gsagc0Z96ukrxjr-LFBGOgv3AuKlCA,90
|
|
3
|
+
wexample_wex_addon_dev_python-0.0.53.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,13 +21,13 @@ 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=
|
|
24
|
+
wexample_wex_addon_dev_python/config_value/python_package_readme_config_value.py,sha256=Dwy_NBUd6bQoP37XpzVs9FC1WjeXTLH9xaOFHf6GazU,8573
|
|
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/file/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
29
29
|
wexample_wex_addon_dev_python/file/__pycache__/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
30
|
-
wexample_wex_addon_dev_python/file/python_package_toml_file.py,sha256=
|
|
30
|
+
wexample_wex_addon_dev_python/file/python_package_toml_file.py,sha256=OjN0CJP6eKyEs54dp_KZW086G0r5RmM-lQPyLhLP8oc,14936
|
|
31
31
|
wexample_wex_addon_dev_python/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
32
32
|
wexample_wex_addon_dev_python/middleware/__pycache__/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
33
33
|
wexample_wex_addon_dev_python/middleware/each_python_file_middleware.py,sha256=UzNEpedCYf31aNONFl0SuSJnoXRzhJhgEiTCaPark6E,2311
|
|
@@ -35,10 +35,11 @@ wexample_wex_addon_dev_python/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
|
|
|
35
35
|
wexample_wex_addon_dev_python/python_addon_manager.py,sha256=Mmr9F5lOS2gbb8JTB4-vTri4Qn5Cd2Jukjk7uiTr1Hs,582
|
|
36
36
|
wexample_wex_addon_dev_python/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
37
37
|
wexample_wex_addon_dev_python/resources/readme_templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
+
wexample_wex_addon_dev_python/resources/readme_templates/tests.md.j2,sha256=6ellaBCay9Ggk61MgcyPok6euJMi-2cUboE2z8REzBs,1271
|
|
38
39
|
wexample_wex_addon_dev_python/resources/readme_templates/title.md.j2,sha256=U-q_U_WhTTwz3enrht3UTfQ9fwioaKUuJqwYyhBtCiA,64
|
|
39
40
|
wexample_wex_addon_dev_python/workdir/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
40
41
|
wexample_wex_addon_dev_python/workdir/__pycache__/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
41
|
-
wexample_wex_addon_dev_python/workdir/python_package_workdir.py,sha256=
|
|
42
|
-
wexample_wex_addon_dev_python/workdir/python_packages_suite_workdir.py,sha256=
|
|
43
|
-
wexample_wex_addon_dev_python/workdir/python_workdir.py,sha256
|
|
44
|
-
wexample_wex_addon_dev_python-0.0.
|
|
42
|
+
wexample_wex_addon_dev_python/workdir/python_package_workdir.py,sha256=l7-Qaei_MGvVSvXqNk4nE-3cpq49MB10wT71FOYmjT8,14317
|
|
43
|
+
wexample_wex_addon_dev_python/workdir/python_packages_suite_workdir.py,sha256=5_wmwhJ3Ox0ougYyQJtybu6WijTTvtzjutcVvuXFY6E,6636
|
|
44
|
+
wexample_wex_addon_dev_python/workdir/python_workdir.py,sha256=bNg7PA3danMdOdMZpv0VV4DGS1jCzxqvcB5VRf5BBmU,14547
|
|
45
|
+
wexample_wex_addon_dev_python-0.0.53.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|