wexample-wex-addon-dev-python 0.0.44__py3-none-any.whl → 0.0.45__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/__init__.py +0 -0
- wexample_wex_addon_dev_python/commands/__init__.py +0 -0
- wexample_wex_addon_dev_python/commands/code/__init__.py +0 -0
- wexample_wex_addon_dev_python/commands/code/check/__init__.py +0 -0
- wexample_wex_addon_dev_python/commands/code/check/mypy.py +46 -0
- wexample_wex_addon_dev_python/commands/code/check/pylint.py +108 -0
- wexample_wex_addon_dev_python/commands/code/check/pyright.py +101 -0
- wexample_wex_addon_dev_python/commands/code/check.py +105 -0
- wexample_wex_addon_dev_python/commands/code/format/__init__.py +1 -0
- wexample_wex_addon_dev_python/commands/code/format/black.py +43 -0
- wexample_wex_addon_dev_python/commands/code/format/isort.py +44 -0
- wexample_wex_addon_dev_python/commands/code/format.py +77 -0
- wexample_wex_addon_dev_python/commands/examples/__init__.py +0 -0
- wexample_wex_addon_dev_python/commands/examples/classes/__init__.py +0 -0
- wexample_wex_addon_dev_python/commands/examples/classes/example_pydantic_class_with_public_var_internaly_defined.py +42 -0
- wexample_wex_addon_dev_python/commands/examples/utils/__init__.py +0 -0
- wexample_wex_addon_dev_python/commands/examples/utils/some_example_type.py +7 -0
- wexample_wex_addon_dev_python/commands/examples/validate.py +20 -0
- wexample_wex_addon_dev_python/commands/release/__init__.py +0 -0
- wexample_wex_addon_dev_python/config_value/__init__.py +0 -0
- wexample_wex_addon_dev_python/config_value/python_package_readme_config_value.py +81 -0
- wexample_wex_addon_dev_python/const/__init__.py +0 -0
- wexample_wex_addon_dev_python/const/package.py +20 -0
- wexample_wex_addon_dev_python/file/__init__.py +0 -0
- wexample_wex_addon_dev_python/file/python_package_toml_file.py +304 -0
- wexample_wex_addon_dev_python/middleware/__init__.py +0 -0
- wexample_wex_addon_dev_python/middleware/each_python_file_middleware.py +79 -0
- wexample_wex_addon_dev_python/python_addon_manager.py +15 -0
- wexample_wex_addon_dev_python/workdir/__init__.py +0 -0
- wexample_wex_addon_dev_python/workdir/python_package_workdir.py +206 -0
- wexample_wex_addon_dev_python/workdir/python_packages_suite_workdir.py +165 -0
- wexample_wex_addon_dev_python/workdir/python_workdir.py +240 -0
- {wexample_wex_addon_dev_python-0.0.44.dist-info → wexample_wex_addon_dev_python-0.0.45.dist-info}/METADATA +8 -8
- wexample_wex_addon_dev_python-0.0.45.dist-info/RECORD +37 -0
- wexample_wex_addon_dev_python-0.0.44.dist-info/RECORD +0 -5
- {wexample_wex_addon_dev_python-0.0.44.dist-info → wexample_wex_addon_dev_python-0.0.45.dist-info}/WHEEL +0 -0
- {wexample_wex_addon_dev_python-0.0.44.dist-info → wexample_wex_addon_dev_python-0.0.45.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from wexample_filestate.config_value.readme_content_config_value import (
|
|
6
|
+
ReadmeContentConfigValue,
|
|
7
|
+
)
|
|
8
|
+
from wexample_wex_addon_dev_python.workdir.python_package_workdir import (
|
|
9
|
+
PythonPackageWorkdir,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PythonPackageReadmeContentConfigValue(ReadmeContentConfigValue):
|
|
14
|
+
workdir: PythonPackageWorkdir
|
|
15
|
+
|
|
16
|
+
def _get_doc_path(self, section: str) -> str:
|
|
17
|
+
"""
|
|
18
|
+
Returns the path to a documentation section file
|
|
19
|
+
"""
|
|
20
|
+
return os.path.join(
|
|
21
|
+
self.workdir.get_path(), ".wex", "doc", "readme", f"{section}.md"
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
def _add_section_if_exists(self, section: str) -> str:
|
|
25
|
+
"""
|
|
26
|
+
Returns section content if the documentation file exists
|
|
27
|
+
"""
|
|
28
|
+
doc_path = self._get_doc_path(section)
|
|
29
|
+
|
|
30
|
+
if os.path.exists(doc_path):
|
|
31
|
+
with open(doc_path, encoding="utf-8") as file:
|
|
32
|
+
content = file.read()
|
|
33
|
+
return f"## {section.title()}\n\n{content}\n\n"
|
|
34
|
+
|
|
35
|
+
return ""
|
|
36
|
+
|
|
37
|
+
def get_templates(self) -> list[str] | None:
|
|
38
|
+
# Use TOMLDocument from the workdir
|
|
39
|
+
doc = self.workdir.get_project_config()
|
|
40
|
+
project = doc.get("project", {}) if isinstance(doc, dict) else {}
|
|
41
|
+
|
|
42
|
+
# Extract information
|
|
43
|
+
description = project.get("description", "")
|
|
44
|
+
python_version = project.get("requires-python", "")
|
|
45
|
+
dependencies = project.get("dependencies", [])
|
|
46
|
+
urls = (
|
|
47
|
+
project.get("urls", {}) if isinstance(project.get("urls", {}), dict) else {}
|
|
48
|
+
)
|
|
49
|
+
# Accept both lowercase and capitalized homepage key variants
|
|
50
|
+
homepage = urls.get("homepage") or urls.get("Homepage") or ""
|
|
51
|
+
license_field = project.get("license", {})
|
|
52
|
+
if isinstance(license_field, dict):
|
|
53
|
+
license_info = license_field.get("text", "") or license_field.get(
|
|
54
|
+
"file", ""
|
|
55
|
+
)
|
|
56
|
+
else:
|
|
57
|
+
license_info = str(license_field) if license_field else ""
|
|
58
|
+
|
|
59
|
+
# Format dependencies list
|
|
60
|
+
deps_list = "\n".join([f"- {dep}" for dep in dependencies])
|
|
61
|
+
|
|
62
|
+
package_name = self.workdir.get_package_name()
|
|
63
|
+
return [
|
|
64
|
+
f"# {package_name}\n\n"
|
|
65
|
+
f"{description}\n\n"
|
|
66
|
+
f"Version: {self.workdir.get_project_version()}\n\n"
|
|
67
|
+
f'{self._add_section_if_exists("features")}'
|
|
68
|
+
"## Requirements\n\n"
|
|
69
|
+
f"- Python {python_version}\n\n"
|
|
70
|
+
"## Dependencies\n\n"
|
|
71
|
+
f"{deps_list}\n\n"
|
|
72
|
+
"## Installation\n\n"
|
|
73
|
+
"```bash\n"
|
|
74
|
+
f"pip install {package_name}\n"
|
|
75
|
+
"```\n\n"
|
|
76
|
+
f'{self._add_section_if_exists("usage")}'
|
|
77
|
+
"## Links\n\n"
|
|
78
|
+
f"- Homepage: {homepage}\n\n"
|
|
79
|
+
"## License\n\n"
|
|
80
|
+
f"{license_info}"
|
|
81
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
# Names of dev/build tools to remove from runtime [project.dependencies]
|
|
4
|
+
# Keep this list in sync with tooling expectations.
|
|
5
|
+
RUNTIME_DEPENDENCY_REMOVE_NAMES: set[str] = {
|
|
6
|
+
"pytest",
|
|
7
|
+
"pip-tools",
|
|
8
|
+
"black",
|
|
9
|
+
"ruff",
|
|
10
|
+
"flake8",
|
|
11
|
+
"mypy",
|
|
12
|
+
"isort",
|
|
13
|
+
"coverage",
|
|
14
|
+
"build",
|
|
15
|
+
"twine",
|
|
16
|
+
"pip",
|
|
17
|
+
"setuptools",
|
|
18
|
+
"wheel",
|
|
19
|
+
"typing-extensions",
|
|
20
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from wexample_filestate.item.file.toml_file import TomlFile
|
|
6
|
+
from wexample_wex_core.workdir.mixin.as_suite_package_item import (
|
|
7
|
+
AsSuitePackageItem,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from tomlkit import TOMLDocument
|
|
12
|
+
from wexample_wex_core.workdir.code_base_workdir import (
|
|
13
|
+
CodeBaseWorkdir,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class PythonPackageTomlFile(AsSuitePackageItem, TomlFile):
|
|
18
|
+
|
|
19
|
+
def _project_table(self):
|
|
20
|
+
"""Ensure and return the [project] table."""
|
|
21
|
+
from wexample_filestate_python.helpers.toml import toml_ensure_table
|
|
22
|
+
|
|
23
|
+
doc = self.read_parsed()
|
|
24
|
+
project, _ = toml_ensure_table(doc, ["project"])
|
|
25
|
+
return project
|
|
26
|
+
|
|
27
|
+
def _dependencies_array(self):
|
|
28
|
+
"""Ensure and return project.dependencies as a multi-line TOML array."""
|
|
29
|
+
from wexample_filestate_python.helpers.toml import (
|
|
30
|
+
toml_ensure_array,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
project = self._project_table()
|
|
34
|
+
deps, _ = toml_ensure_array(project, "dependencies")
|
|
35
|
+
deps.multiline(True)
|
|
36
|
+
return deps
|
|
37
|
+
|
|
38
|
+
def _optional_group_array(self, group: str):
|
|
39
|
+
"""Ensure and return project.optional-dependencies[group] as multi-line array."""
|
|
40
|
+
from wexample_filestate_python.helpers.toml import (
|
|
41
|
+
toml_ensure_array,
|
|
42
|
+
toml_ensure_table,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
project = self._project_table()
|
|
46
|
+
opt, _ = toml_ensure_table(project, ["optional-dependencies"])
|
|
47
|
+
arr, _ = toml_ensure_array(opt, group)
|
|
48
|
+
arr.multiline(True)
|
|
49
|
+
return arr
|
|
50
|
+
|
|
51
|
+
# --- Unified dependency accessors (runtime vs optional) ---
|
|
52
|
+
def _get_deps_array(self, optional: bool = False, group: str = "dev"):
|
|
53
|
+
"""Return TOML array for runtime deps or optional group (multiline)."""
|
|
54
|
+
return (
|
|
55
|
+
self._optional_group_array(group)
|
|
56
|
+
if optional
|
|
57
|
+
else self._dependencies_array()
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
def list_dependencies(
|
|
61
|
+
self, optional: bool = False, group: str = "dev"
|
|
62
|
+
) -> list[str]:
|
|
63
|
+
deps = self._get_deps_array(optional=optional, group=group)
|
|
64
|
+
return [str(x) for x in list(deps)]
|
|
65
|
+
|
|
66
|
+
def list_dependency_names(
|
|
67
|
+
self,
|
|
68
|
+
canonicalize_names: bool = True,
|
|
69
|
+
optional: bool = False,
|
|
70
|
+
group: str = "dev",
|
|
71
|
+
) -> list[str]:
|
|
72
|
+
"""Return dependency package names derived from list_dependencies().
|
|
73
|
+
|
|
74
|
+
If canonicalize_names is True, names are normalized using packaging's
|
|
75
|
+
canonicalize_name for robust comparisons (dash/underscore, case, etc.).
|
|
76
|
+
"""
|
|
77
|
+
from packaging.requirements import Requirement
|
|
78
|
+
from packaging.utils import canonicalize_name
|
|
79
|
+
|
|
80
|
+
names: list[str] = []
|
|
81
|
+
for spec in self.list_dependencies(optional=optional, group=group):
|
|
82
|
+
try:
|
|
83
|
+
name = Requirement(spec).name
|
|
84
|
+
names.append(canonicalize_name(name) if canonicalize_names else name)
|
|
85
|
+
except Exception:
|
|
86
|
+
# Skip unparsable entries when deriving names
|
|
87
|
+
continue
|
|
88
|
+
return names
|
|
89
|
+
|
|
90
|
+
def add_dependency(
|
|
91
|
+
self, spec: str, optional: bool = False, group: str = "dev"
|
|
92
|
+
) -> bool:
|
|
93
|
+
from packaging.requirements import Requirement
|
|
94
|
+
from packaging.utils import canonicalize_name
|
|
95
|
+
from wexample_filestate_python.helpers.toml import toml_sort_string_array
|
|
96
|
+
|
|
97
|
+
deps = self._get_deps_array(optional=optional, group=group)
|
|
98
|
+
# Remove existing entries for the same package name before adding the new spec.
|
|
99
|
+
new_name = canonicalize_name(Requirement(spec).name)
|
|
100
|
+
removed = self.remove_dependency_by_name(
|
|
101
|
+
new_name, optional=optional, group=group
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Append (or re-append) the new spec if it is not already present verbatim
|
|
105
|
+
if spec not in deps:
|
|
106
|
+
deps.append(spec)
|
|
107
|
+
toml_sort_string_array(deps)
|
|
108
|
+
return True
|
|
109
|
+
|
|
110
|
+
return removed
|
|
111
|
+
|
|
112
|
+
def remove_dependency_by_name(
|
|
113
|
+
self, package_name: str, optional: bool = False, group: str = "dev"
|
|
114
|
+
) -> bool:
|
|
115
|
+
"""Remove all dependency entries that match the given package name.
|
|
116
|
+
|
|
117
|
+
The provided package_name can be raw; it will be canonicalized to ensure
|
|
118
|
+
consistent matching against entries parsed from list_dependencies().
|
|
119
|
+
"""
|
|
120
|
+
from packaging.requirements import Requirement
|
|
121
|
+
from packaging.utils import canonicalize_name
|
|
122
|
+
|
|
123
|
+
deps = self._get_deps_array(optional=optional, group=group)
|
|
124
|
+
|
|
125
|
+
target = canonicalize_name(package_name)
|
|
126
|
+
filtered: list[str] = []
|
|
127
|
+
for existing in list(deps):
|
|
128
|
+
try:
|
|
129
|
+
existing_name = canonicalize_name(Requirement(str(existing)).name)
|
|
130
|
+
except Exception:
|
|
131
|
+
# Keep unparsable entries untouched
|
|
132
|
+
filtered.append(existing)
|
|
133
|
+
continue
|
|
134
|
+
if existing_name != target:
|
|
135
|
+
filtered.append(existing)
|
|
136
|
+
|
|
137
|
+
if len(filtered) != len(deps):
|
|
138
|
+
deps.clear()
|
|
139
|
+
deps.extend(filtered)
|
|
140
|
+
return True
|
|
141
|
+
return False
|
|
142
|
+
|
|
143
|
+
def find_package_workdir(self) -> CodeBaseWorkdir | None:
|
|
144
|
+
from wexample_wex_core.workdir.code_base_workdir import (
|
|
145
|
+
CodeBaseWorkdir,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
return self.find_closest(CodeBaseWorkdir)
|
|
149
|
+
|
|
150
|
+
def dumps(self, content: TOMLDocument | dict | None = None) -> str:
|
|
151
|
+
"""Serialize a TOMLDocument (preferred) or a plain dict to TOML.
|
|
152
|
+
Using tomlkit.dumps preserves comments/formatting when content is a TOMLDocument.
|
|
153
|
+
"""
|
|
154
|
+
from tomlkit import dumps, table
|
|
155
|
+
from wexample_filestate_python.helpers.package import package_normalize_name
|
|
156
|
+
from wexample_filestate_python.helpers.toml import (
|
|
157
|
+
toml_ensure_array_multiline,
|
|
158
|
+
toml_ensure_table,
|
|
159
|
+
toml_get_string_value,
|
|
160
|
+
toml_set_array_multiline,
|
|
161
|
+
toml_sort_string_array,
|
|
162
|
+
)
|
|
163
|
+
from wexample_wex_addon_dev_python.const.package import (
|
|
164
|
+
RUNTIME_DEPENDENCY_REMOVE_NAMES,
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
# Obtain the current TOML document (preserving formatting) if not provided
|
|
168
|
+
content = content or self.read_parsed()
|
|
169
|
+
|
|
170
|
+
# Try to get current package/workdir context
|
|
171
|
+
package = self.find_package_workdir()
|
|
172
|
+
import_name: str | None = None
|
|
173
|
+
project_version: str | None = None
|
|
174
|
+
project_name: str | None = None
|
|
175
|
+
if package:
|
|
176
|
+
project_name = package.get_package_name()
|
|
177
|
+
project_version = package.get_project_version()
|
|
178
|
+
import_name = package.get_package_import_name()
|
|
179
|
+
|
|
180
|
+
# --- [build-system] enforcement ---
|
|
181
|
+
build_tbl = content.get("build-system") if isinstance(content, dict) else None
|
|
182
|
+
if not build_tbl or not isinstance(build_tbl, dict):
|
|
183
|
+
build_tbl = table()
|
|
184
|
+
content["build-system"] = build_tbl
|
|
185
|
+
desired_requires = ["pdm-backend"]
|
|
186
|
+
if build_tbl.get("requires") != desired_requires:
|
|
187
|
+
build_tbl["requires"] = desired_requires
|
|
188
|
+
if build_tbl.get("build-backend") != "pdm.backend":
|
|
189
|
+
build_tbl["build-backend"] = "pdm.backend"
|
|
190
|
+
|
|
191
|
+
# --- [tool.pdm.build] enforcement ---
|
|
192
|
+
tool_tbl, _ = toml_ensure_table(content, ["tool"])
|
|
193
|
+
pdm_tbl, _ = toml_ensure_table(tool_tbl, ["pdm"])
|
|
194
|
+
build_pdm_tbl, _ = toml_ensure_table(pdm_tbl, ["build"])
|
|
195
|
+
includes_arr, _ = toml_ensure_array_multiline(build_pdm_tbl, "includes")
|
|
196
|
+
|
|
197
|
+
pdm_tbl["distribution"] = True
|
|
198
|
+
# Enforce src layout, packages, and includes (py.typed)
|
|
199
|
+
if build_pdm_tbl.get("package-dir") != "src":
|
|
200
|
+
build_pdm_tbl["package-dir"] = "src"
|
|
201
|
+
if import_name:
|
|
202
|
+
desired_pkgs = [{"include": import_name, "from": "src"}]
|
|
203
|
+
if build_pdm_tbl.get("packages") != desired_pkgs:
|
|
204
|
+
build_pdm_tbl["packages"] = desired_pkgs
|
|
205
|
+
desired_includes = [f"src/{import_name}/*"]
|
|
206
|
+
current_includes = [str(x) for x in list(includes_arr)]
|
|
207
|
+
if current_includes != desired_includes:
|
|
208
|
+
toml_set_array_multiline(build_pdm_tbl, "includes", desired_includes)
|
|
209
|
+
|
|
210
|
+
# --- [project] table and basic fields ---
|
|
211
|
+
project_tbl, _ = toml_ensure_table(content, ["project"])
|
|
212
|
+
# Name sync (best-effort)
|
|
213
|
+
if project_name:
|
|
214
|
+
project_tbl["name"] = project_name
|
|
215
|
+
# Version sync (best-effort)
|
|
216
|
+
if project_version:
|
|
217
|
+
project_tbl["version"] = project_version
|
|
218
|
+
# Python requirement
|
|
219
|
+
target_requires_python = ">=3.10"
|
|
220
|
+
if project_tbl.get("requires-python") != target_requires_python:
|
|
221
|
+
project_tbl["requires-python"] = target_requires_python
|
|
222
|
+
|
|
223
|
+
# --- Dependencies normalization ---
|
|
224
|
+
# Use class helper to ensure multiline dependencies array
|
|
225
|
+
deps_arr = self._dependencies_array()
|
|
226
|
+
# Sort dependencies array
|
|
227
|
+
toml_sort_string_array(deps_arr)
|
|
228
|
+
|
|
229
|
+
# Optional dependency groups
|
|
230
|
+
opt_tbl, _ = toml_ensure_table(project_tbl, ["optional-dependencies"])
|
|
231
|
+
# Ensure dev group exists (multiline)
|
|
232
|
+
dev_arr = self._optional_group_array("dev")
|
|
233
|
+
|
|
234
|
+
# Filestate configuration for keep/exclude-add
|
|
235
|
+
filestate_tbl = None
|
|
236
|
+
if isinstance(tool_tbl, dict):
|
|
237
|
+
filestate_tbl = tool_tbl.get("filestate")
|
|
238
|
+
keep_names: set[str] = set()
|
|
239
|
+
exclude_add: set[str] = set()
|
|
240
|
+
if isinstance(filestate_tbl, dict):
|
|
241
|
+
keep_list = filestate_tbl.get("keep")
|
|
242
|
+
if isinstance(keep_list, list):
|
|
243
|
+
keep_names = {package_normalize_name(str(x)) for x in keep_list}
|
|
244
|
+
ex_list = filestate_tbl.get("exclude-add")
|
|
245
|
+
if isinstance(ex_list, list):
|
|
246
|
+
exclude_add = {str(x).strip().lower() for x in ex_list}
|
|
247
|
+
|
|
248
|
+
# Remove unwanted dev/build tools from runtime deps (unless kept)
|
|
249
|
+
|
|
250
|
+
def _should_remove(item: object) -> bool:
|
|
251
|
+
name = package_normalize_name(toml_get_string_value(item))
|
|
252
|
+
if name in keep_names:
|
|
253
|
+
return False
|
|
254
|
+
if name == "typing-extensions":
|
|
255
|
+
# Safe to drop when python >= 3.10 and we manage deps
|
|
256
|
+
return True
|
|
257
|
+
return name in RUNTIME_DEPENDENCY_REMOVE_NAMES
|
|
258
|
+
|
|
259
|
+
to_keep = []
|
|
260
|
+
for it in list(deps_arr):
|
|
261
|
+
if not _should_remove(it):
|
|
262
|
+
to_keep.append(it)
|
|
263
|
+
if len(to_keep) != len(deps_arr):
|
|
264
|
+
deps_arr.clear()
|
|
265
|
+
deps_arr.extend(to_keep)
|
|
266
|
+
toml_sort_string_array(deps_arr)
|
|
267
|
+
|
|
268
|
+
# Normalize any pydantic spec to pydantic>=2,<3
|
|
269
|
+
normalized = False
|
|
270
|
+
new_deps = []
|
|
271
|
+
for it in list(deps_arr):
|
|
272
|
+
val = toml_get_string_value(it).strip()
|
|
273
|
+
base = package_normalize_name(val)
|
|
274
|
+
if base == "pydantic":
|
|
275
|
+
new_deps.append("pydantic>=2,<3")
|
|
276
|
+
normalized = True
|
|
277
|
+
else:
|
|
278
|
+
new_deps.append(it)
|
|
279
|
+
if normalized:
|
|
280
|
+
deps_arr.clear()
|
|
281
|
+
deps_arr.extend(new_deps)
|
|
282
|
+
toml_sort_string_array(deps_arr)
|
|
283
|
+
|
|
284
|
+
# Ensure pydantic>=2,<3 present unless excluded
|
|
285
|
+
existing_norm = {
|
|
286
|
+
package_normalize_name(toml_get_string_value(it)) for it in list(deps_arr)
|
|
287
|
+
}
|
|
288
|
+
if "pydantic" not in exclude_add and "pydantic" not in existing_norm:
|
|
289
|
+
deps_arr.append("pydantic>=2,<3")
|
|
290
|
+
toml_sort_string_array(deps_arr)
|
|
291
|
+
|
|
292
|
+
# Ensure optional dev group contains pytest unless already in runtime deps
|
|
293
|
+
runtime_has_pytest = any(
|
|
294
|
+
package_normalize_name(toml_get_string_value(it)) == "pytest"
|
|
295
|
+
for it in list(deps_arr)
|
|
296
|
+
)
|
|
297
|
+
dev_values = [toml_get_string_value(it) for it in list(dev_arr)]
|
|
298
|
+
if not runtime_has_pytest and not any(
|
|
299
|
+
v.strip() == "pytest" for v in dev_values
|
|
300
|
+
):
|
|
301
|
+
dev_arr.append("pytest")
|
|
302
|
+
toml_sort_string_array(dev_arr)
|
|
303
|
+
|
|
304
|
+
return dumps(content)
|
|
File without changes
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os.path
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from wexample_wex_core.middleware.each_file_middleware import EachFileMiddleware
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from wexample_wex_core.common.command_request import CommandRequest
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class EachPythonFileMiddleware(EachFileMiddleware):
|
|
13
|
+
"""
|
|
14
|
+
Middleware for processing Python files only.
|
|
15
|
+
- Filters files by .py extension by default
|
|
16
|
+
- Ignores special directories like __pycache__ during recursion
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
# Default extension to filter
|
|
20
|
+
python_extension_only: bool = True
|
|
21
|
+
|
|
22
|
+
# Default list of directories to ignore during recursion
|
|
23
|
+
ignored_directories: set[str] = {
|
|
24
|
+
"__pycache__",
|
|
25
|
+
".git",
|
|
26
|
+
".idea",
|
|
27
|
+
".vscode",
|
|
28
|
+
"venv",
|
|
29
|
+
"env",
|
|
30
|
+
"node_modules",
|
|
31
|
+
".pytest_cache",
|
|
32
|
+
".mypy_cache",
|
|
33
|
+
".ruff_cache",
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
def __init__(self, **kwargs) -> None:
|
|
37
|
+
# Allow overriding the default settings
|
|
38
|
+
if "python_extension_only" in kwargs:
|
|
39
|
+
self.python_extension_only = kwargs.pop("python_extension_only")
|
|
40
|
+
|
|
41
|
+
if "ignored_directories" in kwargs:
|
|
42
|
+
self.ignored_directories = set(kwargs.pop("ignored_directories"))
|
|
43
|
+
|
|
44
|
+
super().__init__(**kwargs)
|
|
45
|
+
|
|
46
|
+
def _should_process_item(self, request: CommandRequest, item_path: str) -> bool:
|
|
47
|
+
"""
|
|
48
|
+
Only process Python files based on extension.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
item_path: Path to the item to check
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
True if the item should be processed, False otherwise
|
|
55
|
+
"""
|
|
56
|
+
# First check if it's a file (parent class behavior)
|
|
57
|
+
if not os.path.isfile(item_path):
|
|
58
|
+
return False
|
|
59
|
+
|
|
60
|
+
# If python_extension_only is enabled, check file extension
|
|
61
|
+
if self.python_extension_only:
|
|
62
|
+
return item_path.endswith(".py")
|
|
63
|
+
|
|
64
|
+
# Otherwise, accept all files
|
|
65
|
+
return True
|
|
66
|
+
|
|
67
|
+
def _should_explore_directory(
|
|
68
|
+
self, request: CommandRequest, directory_name: str
|
|
69
|
+
) -> bool:
|
|
70
|
+
"""
|
|
71
|
+
Skip directories that are in the ignored_directories list.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
directory_name: Name of the directory to check
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
False if the directory is in the ignored list, True otherwise
|
|
78
|
+
"""
|
|
79
|
+
return directory_name not in self.ignored_directories
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from wexample_wex_core.common.abstract_addon_manager import AbstractAddonManager
|
|
4
|
+
from wexample_wex_core.middleware.abstract_middleware import AbstractMiddleware
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class PythonAddonManager(AbstractAddonManager):
|
|
8
|
+
def get_middlewares_classes(self) -> list[type[AbstractMiddleware]]:
|
|
9
|
+
from wexample_wex_addon_dev_python.middleware.each_python_file_middleware import (
|
|
10
|
+
EachPythonFileMiddleware,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
return [
|
|
14
|
+
EachPythonFileMiddleware,
|
|
15
|
+
]
|
|
File without changes
|