wexample-wex-addon-dev-python 15.2.1__py3-none-any.whl → 15.3.1__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/commands/code/rename.py +0 -0
- wexample_wex_addon_dev_python/common/__init__.py +0 -0
- wexample_wex_addon_dev_python/common/pypi_registry_gateway.py +13 -3
- wexample_wex_addon_dev_python/const/tags.py +0 -0
- wexample_wex_addon_dev_python/formatter/__init__.py +0 -0
- wexample_wex_addon_dev_python/formatter/python_code_formatter.py +0 -0
- wexample_wex_addon_dev_python/refactor/__init__.py +0 -0
- wexample_wex_addon_dev_python/refactor/abstract_python_path_rename_handler.py +0 -0
- wexample_wex_addon_dev_python/refactor/abstract_python_rename_handler.py +0 -0
- wexample_wex_addon_dev_python/refactor/abstract_python_symbol_rename_handler.py +0 -0
- wexample_wex_addon_dev_python/refactor/applier.py +0 -0
- wexample_wex_addon_dev_python/refactor/python_class_rename_handler.py +0 -0
- wexample_wex_addon_dev_python/refactor/python_common.py +0 -0
- wexample_wex_addon_dev_python/refactor/python_constant_rename_handler.py +0 -0
- wexample_wex_addon_dev_python/refactor/python_function_rename_handler.py +0 -0
- wexample_wex_addon_dev_python/refactor/python_module_rename_handler.py +0 -0
- wexample_wex_addon_dev_python/refactor/python_package_rename_handler.py +0 -0
- wexample_wex_addon_dev_python/refactor/rename_plan.py +0 -0
- wexample_wex_addon_dev_python/refactor/symbol_kind.py +0 -0
- wexample_wex_addon_dev_python/resources/ai/agents/describer/agent.yml +0 -0
- wexample_wex_addon_dev_python/resources/ai/agents/describer/context.j2 +0 -0
- wexample_wex_addon_dev_python/resources/design_rules/python_code.md +0 -0
- wexample_wex_addon_dev_python/resources/package_gitlab_ci.yml +4 -0
- wexample_wex_addon_dev_python/resources/readme_templates/install.md.j2 +0 -0
- wexample_wex_addon_dev_python/resources/writing_rules/python_code.md +0 -0
- wexample_wex_addon_dev_python/selection/__init__.py +0 -0
- wexample_wex_addon_dev_python/selection/abstract_python_code_selection.py +0 -0
- wexample_wex_addon_dev_python/selection/package_description_missing_selection.py +0 -0
- wexample_wex_addon_dev_python/selection/python_code_performance_selection.py +0 -0
- wexample_wex_addon_dev_python/workdir/python_package_workdir.py +105 -17
- wexample_wex_addon_dev_python/workdir/python_workdir.py +18 -0
- wexample_wex_addon_dev_python-15.3.1.dist-info/METADATA +121 -0
- {wexample_wex_addon_dev_python-15.2.1.dist-info → wexample_wex_addon_dev_python-15.3.1.dist-info}/RECORD +8 -13
- wexample_wex_addon_dev_python/__pycache__/__init__.py +0 -0
- wexample_wex_addon_dev_python/config_value/__pycache__/__init__.py +0 -0
- wexample_wex_addon_dev_python/const/__pycache__/__init__.py +0 -0
- wexample_wex_addon_dev_python/file/__pycache__/__init__.py +0 -0
- wexample_wex_addon_dev_python/middleware/__pycache__/__init__.py +0 -0
- wexample_wex_addon_dev_python/workdir/__pycache__/__init__.py +0 -0
- wexample_wex_addon_dev_python-15.2.1.dist-info/METADATA +0 -315
- {wexample_wex_addon_dev_python-15.2.1.dist-info → wexample_wex_addon_dev_python-15.3.1.dist-info}/WHEEL +0 -0
- {wexample_wex_addon_dev_python-15.2.1.dist-info → wexample_wex_addon_dev_python-15.3.1.dist-info}/entry_points.txt +0 -0
|
File without changes
|
|
File without changes
|
|
@@ -39,12 +39,22 @@ class PypiRegistryGateway(AbstractGateway):
|
|
|
39
39
|
def has_version(self, package: str, version: str) -> bool:
|
|
40
40
|
"""Return True iff `package==version` is published on this index.
|
|
41
41
|
|
|
42
|
-
Uses the
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
Uses the Simple index and matches the version string against its
|
|
43
|
+
body. Returns False on 404 (unknown package, or version not yet
|
|
44
|
+
propagated through the registry's CDN).
|
|
45
|
+
|
|
46
|
+
Asked for exactly as pip asks for it: PyPI's CDN caches each
|
|
47
|
+
representation of the page apart, and they do not refresh together.
|
|
48
|
+
The gzipped HTML one stayed without a release for over ten minutes
|
|
49
|
+
after pip's JSON one listed it, so a check reading the former waited
|
|
50
|
+
on a version pip could already install.
|
|
45
51
|
"""
|
|
46
52
|
response = self.make_request(
|
|
47
53
|
endpoint=f"simple/{package}/",
|
|
54
|
+
headers={
|
|
55
|
+
"Accept": "application/vnd.pypi.simple.v1+json, "
|
|
56
|
+
"application/vnd.pypi.simple.v1+html; q=0.1, text/html; q=0.01",
|
|
57
|
+
},
|
|
48
58
|
expected_status_codes=_HAS_VERSION_STATUS_CODES,
|
|
49
59
|
fatal_if_unexpected=False,
|
|
50
60
|
quiet=True,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -58,6 +58,18 @@ class PythonPackageWorkdir(PythonWorkdir):
|
|
|
58
58
|
"Install: pipx install pdm — then run: wex core::env/configure"
|
|
59
59
|
)
|
|
60
60
|
|
|
61
|
+
def get_gitlab_registry_url(self) -> str | None:
|
|
62
|
+
"""The GitLab PyPI registry of the group, when the suite publishes there.
|
|
63
|
+
|
|
64
|
+
`python.gitlab_registry.url`, the group's endpoint without `/simple`:
|
|
65
|
+
`https://gitlab.example.com/api/v4/groups/<group>/-/packages/pypi`.
|
|
66
|
+
Installs read its `/simple` index; each package uploads to its own
|
|
67
|
+
project's registry, which the group one gathers.
|
|
68
|
+
"""
|
|
69
|
+
return self.search_app_or_suite_runtime_config(
|
|
70
|
+
"python.gitlab_registry.url", default=None
|
|
71
|
+
).get_str_or_none()
|
|
72
|
+
|
|
61
73
|
def get_required_knowledge_pages(self) -> dict[str, str]:
|
|
62
74
|
return {
|
|
63
75
|
**super().get_required_knowledge_pages(),
|
|
@@ -77,22 +89,28 @@ class PythonPackageWorkdir(PythonWorkdir):
|
|
|
77
89
|
raw_value = super().prepare_value(raw_value=raw_value)
|
|
78
90
|
children = raw_value.get("children")
|
|
79
91
|
|
|
80
|
-
#
|
|
81
|
-
#
|
|
82
|
-
#
|
|
92
|
+
# Every package is tested by GitLab at each push, the way a consumer
|
|
93
|
+
# installs it: the file only includes the job, which lives in the ci-cd
|
|
94
|
+
# repository. A package published to the private GitLab registry gets
|
|
95
|
+
# the job publishing it on a tag instead, and no tests: the base
|
|
96
|
+
# template's workflow runs no tag pipeline. Created when absent, never
|
|
97
|
+
# rewritten. The callback is evaluated after configure() completes, so
|
|
98
|
+
# the runtime config is available at that point.
|
|
83
99
|
children.append(
|
|
84
100
|
{
|
|
85
101
|
"name": ".gitlab-ci.yml",
|
|
86
102
|
"type": DiskItemType.FILE,
|
|
87
|
-
"should_exist":
|
|
88
|
-
|
|
89
|
-
"pdm.repository.url", default=None
|
|
90
|
-
).get_str_or_none()
|
|
91
|
-
),
|
|
92
|
-
"default_content": lambda _: file_read(
|
|
103
|
+
"should_exist": True,
|
|
104
|
+
"default_content": lambda _, _self=self: file_read(
|
|
93
105
|
module_get_path(wexample_wex_addon_dev_python)
|
|
94
106
|
/ "resources"
|
|
95
|
-
/
|
|
107
|
+
/ (
|
|
108
|
+
"package_publish_gitlab.yml"
|
|
109
|
+
if _self.search_app_or_suite_runtime_config(
|
|
110
|
+
"pdm.repository.url", default=None
|
|
111
|
+
).get_str_or_none()
|
|
112
|
+
else "package_gitlab_ci.yml"
|
|
113
|
+
)
|
|
96
114
|
),
|
|
97
115
|
}
|
|
98
116
|
)
|
|
@@ -414,8 +432,9 @@ class PythonPackageWorkdir(PythonWorkdir):
|
|
|
414
432
|
self.push_to_deployment_remote(branch_name=GIT_BRANCH_MAIN)
|
|
415
433
|
|
|
416
434
|
def _publish(self, force: bool = False) -> None:
|
|
417
|
-
from
|
|
418
|
-
|
|
435
|
+
from wexample_helpers.exception.shell_command_failed_exception import (
|
|
436
|
+
ShellCommandFailedException,
|
|
437
|
+
)
|
|
419
438
|
from wexample_helpers_git.helper.git import (
|
|
420
439
|
git_push_tag,
|
|
421
440
|
git_tag_annotated,
|
|
@@ -443,10 +462,75 @@ class PythonPackageWorkdir(PythonWorkdir):
|
|
|
443
462
|
git_push_tag(tag, cwd=cwd, remote=remote, inherit_stdio=True)
|
|
444
463
|
return
|
|
445
464
|
|
|
446
|
-
#
|
|
447
|
-
#
|
|
448
|
-
#
|
|
449
|
-
|
|
465
|
+
# Our own registry first, where the release counts: what installs read
|
|
466
|
+
# (wex, the apps) and what the publication waits for. PyPI then only
|
|
467
|
+
# mirrors it, and a refusal there no longer stops the suite.
|
|
468
|
+
if self.get_gitlab_registry_url():
|
|
469
|
+
self._publish_to_gitlab_registry(force=force)
|
|
470
|
+
try:
|
|
471
|
+
self._publish_to_pypi(force=force)
|
|
472
|
+
except ShellCommandFailedException as exception:
|
|
473
|
+
self.warning(
|
|
474
|
+
f"PyPI refused {package_name} {version}, published on the GitLab "
|
|
475
|
+
f"registry all the same: {exception}"
|
|
476
|
+
)
|
|
477
|
+
return
|
|
478
|
+
|
|
479
|
+
self._publish_to_pypi(force=force)
|
|
480
|
+
|
|
481
|
+
def _publish_to_gitlab_registry(self, force: bool = False) -> None:
|
|
482
|
+
import re
|
|
483
|
+
from urllib.parse import quote
|
|
484
|
+
|
|
485
|
+
from wexample_helpers.helper.shell import shell_run
|
|
486
|
+
from wexample_helpers_git.helper.git import git_get_remote_url
|
|
487
|
+
|
|
488
|
+
from wexample_wex_addon_dev_python.common.pypi_registry_gateway import (
|
|
489
|
+
PypiRegistryGateway,
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
package_name = self.get_package_name()
|
|
493
|
+
version = self.get_setup_version()
|
|
494
|
+
registry_url = self.get_gitlab_registry_url()
|
|
495
|
+
|
|
496
|
+
index = PypiRegistryGateway(base_url=registry_url, io=self.io, timeout=10)
|
|
497
|
+
if index.has_version(package_name, version) and not force:
|
|
498
|
+
self.log(f"{package_name} {version} is already on the GitLab registry.")
|
|
499
|
+
return
|
|
500
|
+
|
|
501
|
+
# The project is the package's origin: ssh://git@host:port/<group>/<name>.git
|
|
502
|
+
project_path = re.sub(
|
|
503
|
+
r"^(ssh://[^/]+/|git@[^:]+:|https?://[^/]+/)",
|
|
504
|
+
"",
|
|
505
|
+
git_get_remote_url(cwd=self.get_path()),
|
|
506
|
+
).removesuffix(".git")
|
|
507
|
+
project = quote(project_path, safe="")
|
|
508
|
+
api_url = registry_url.split("/groups/", 1)[0]
|
|
509
|
+
|
|
510
|
+
self.subtitle("Publishing to the GitLab registry")
|
|
511
|
+
shell_run(
|
|
512
|
+
[
|
|
513
|
+
"pdm",
|
|
514
|
+
"publish",
|
|
515
|
+
"--repository",
|
|
516
|
+
f"{api_url}/projects/{project}/packages/pypi",
|
|
517
|
+
# GitLab authenticates the token alone; the name is required, not read.
|
|
518
|
+
"--username",
|
|
519
|
+
"__token__",
|
|
520
|
+
"--password",
|
|
521
|
+
self.get_env_parameter_or_suite_fallback("GITLAB_API_TOKEN"),
|
|
522
|
+
],
|
|
523
|
+
inherit_stdio=True,
|
|
524
|
+
cwd=self.get_path(),
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
def _publish_to_pypi(self, force: bool = False) -> None:
|
|
528
|
+
from wexample_filestate_python.common.pipy_gateway import PipyGateway
|
|
529
|
+
from wexample_helpers.helper.shell import shell_run
|
|
530
|
+
|
|
531
|
+
package_name = self.get_package_name()
|
|
532
|
+
version = self.get_setup_version()
|
|
533
|
+
|
|
450
534
|
client = PipyGateway(parent_io_handler=self)
|
|
451
535
|
if client.package_release_exists(package_name=package_name, version=version):
|
|
452
536
|
self.warning(
|
|
@@ -487,8 +571,12 @@ class PythonPackageWorkdir(PythonWorkdir):
|
|
|
487
571
|
PypiRegistryGateway,
|
|
488
572
|
)
|
|
489
573
|
|
|
574
|
+
# The GitLab registry, when the suite publishes there, is what installs
|
|
575
|
+
# read: it serves a version as soon as it is uploaded, where PyPI's CDN
|
|
576
|
+
# can lag for many minutes.
|
|
490
577
|
repository_url = (
|
|
491
|
-
self.
|
|
578
|
+
self.get_gitlab_registry_url()
|
|
579
|
+
or self.search_app_or_suite_runtime_config(
|
|
492
580
|
"pdm.repository.url", default=None
|
|
493
581
|
).get_str_or_none()
|
|
494
582
|
or "https://pypi.org"
|
|
@@ -476,6 +476,23 @@ class PythonWorkdir(
|
|
|
476
476
|
|
|
477
477
|
pyproject_path = self.get_path() / "pyproject.toml"
|
|
478
478
|
|
|
479
|
+
# The suite's GitLab registry beside PyPI: the wexample packages are
|
|
480
|
+
# there first. Written into requirements.txt, so `pip install -r` reads
|
|
481
|
+
# it too. Best match across both, as pip does: a package published
|
|
482
|
+
# before the registry existed is only on PyPI.
|
|
483
|
+
index_arguments: list[str] = []
|
|
484
|
+
registry_url = self.search_app_or_suite_runtime_config(
|
|
485
|
+
"python.gitlab_registry.url", default=None
|
|
486
|
+
).get_str_or_none()
|
|
487
|
+
if registry_url:
|
|
488
|
+
index_arguments = [
|
|
489
|
+
"--extra-index-url",
|
|
490
|
+
f"{registry_url}/simple",
|
|
491
|
+
"--index-strategy",
|
|
492
|
+
"unsafe-best-match",
|
|
493
|
+
"--emit-index-url",
|
|
494
|
+
]
|
|
495
|
+
|
|
479
496
|
def _run_uv_compile() -> None:
|
|
480
497
|
result = subprocess.run(
|
|
481
498
|
[
|
|
@@ -494,6 +511,7 @@ class PythonWorkdir(
|
|
|
494
511
|
# long as they satisfy constraints — causing stale
|
|
495
512
|
# transitives even after their direct dependents bumped.
|
|
496
513
|
"--upgrade",
|
|
514
|
+
*index_arguments,
|
|
497
515
|
],
|
|
498
516
|
capture_output=True,
|
|
499
517
|
text=True,
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: wexample-wex-addon-dev-python
|
|
3
|
+
Version: 15.3.1
|
|
4
|
+
Summary: Extends wex with commands to format, lint, and rename Python symbols, plus workdir types for Python packages and suites
|
|
5
|
+
Author-Email: weeger <contact@wexample.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Project-URL: homepage, https://github.com/wexample/python-wex-dev-python
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Requires-Dist: attrs>=23.1.0
|
|
13
|
+
Requires-Dist: black
|
|
14
|
+
Requires-Dist: cattrs>=23.1.0
|
|
15
|
+
Requires-Dist: griffe>=2.0.2
|
|
16
|
+
Requires-Dist: isort
|
|
17
|
+
Requires-Dist: networkx
|
|
18
|
+
Requires-Dist: pylint
|
|
19
|
+
Requires-Dist: pyright
|
|
20
|
+
Requires-Dist: wexample-api>=6.8.0
|
|
21
|
+
Requires-Dist: wexample-filestate-python>=9.1.4
|
|
22
|
+
Requires-Dist: wexample-wex-addon-ai>=19.0.2
|
|
23
|
+
Requires-Dist: wexample-wex-addon-app>=34.2.0
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest; extra == "dev"
|
|
26
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# wex_addon_dev_python
|
|
30
|
+
|
|
31
|
+
Version: 15.3.1
|
|
32
|
+
|
|
33
|
+
Extends wex with commands to format, lint, and rename Python symbols, plus workdir types for Python packages and suites
|
|
34
|
+
|
|
35
|
+
## Table of Contents
|
|
36
|
+
|
|
37
|
+
- [Install](#install)
|
|
38
|
+
- [Tests](#tests)
|
|
39
|
+
- [Roadmap](#roadmap)
|
|
40
|
+
- [Status Compatibility](#status-compatibility)
|
|
41
|
+
- [Useful Links](#useful-links)
|
|
42
|
+
|
|
43
|
+
## Installation
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install wexample-wex-addon-dev-python
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Requires Python >=3.10.
|
|
50
|
+
|
|
51
|
+
## Tests
|
|
52
|
+
|
|
53
|
+
This project uses `pytest` for testing and `pytest-cov` for code coverage analysis.
|
|
54
|
+
|
|
55
|
+
### Installation
|
|
56
|
+
|
|
57
|
+
First, install the required testing dependencies:
|
|
58
|
+
```bash
|
|
59
|
+
.venv/bin/python -m pip install pytest pytest-cov
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Basic Usage
|
|
63
|
+
|
|
64
|
+
Run all tests with coverage:
|
|
65
|
+
```bash
|
|
66
|
+
.venv/bin/python -m pytest --cov --cov-report=html
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Common Commands
|
|
70
|
+
```bash
|
|
71
|
+
# Run tests with coverage for a specific module
|
|
72
|
+
.venv/bin/python -m pytest --cov=your_module
|
|
73
|
+
|
|
74
|
+
# Show which lines are not covered
|
|
75
|
+
.venv/bin/python -m pytest --cov=your_module --cov-report=term-missing
|
|
76
|
+
|
|
77
|
+
# Generate an HTML coverage report
|
|
78
|
+
.venv/bin/python -m pytest --cov=your_module --cov-report=html
|
|
79
|
+
|
|
80
|
+
# Combine terminal and HTML reports
|
|
81
|
+
.venv/bin/python -m pytest --cov=your_module --cov-report=term-missing --cov-report=html
|
|
82
|
+
|
|
83
|
+
# Run specific test file with coverage
|
|
84
|
+
.venv/bin/python -m pytest tests/test_file.py --cov=your_module --cov-report=term-missing
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Viewing HTML Reports
|
|
88
|
+
|
|
89
|
+
After generating an HTML report, open `htmlcov/index.html` in your browser to view detailed line-by-line coverage information.
|
|
90
|
+
|
|
91
|
+
### Coverage Threshold
|
|
92
|
+
|
|
93
|
+
To enforce a minimum coverage percentage:
|
|
94
|
+
```bash
|
|
95
|
+
.venv/bin/python -m pytest --cov=your_module --cov-fail-under=80
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
This will cause the test suite to fail if coverage drops below 80%.
|
|
99
|
+
|
|
100
|
+
## Known Limitations & Roadmap
|
|
101
|
+
|
|
102
|
+
Current limitations and planned features are tracked in the GitHub issues.
|
|
103
|
+
|
|
104
|
+
See the [project roadmap](https://github.com/wexample/python-wex_addon_dev_python/issues) for upcoming features and improvements.
|
|
105
|
+
|
|
106
|
+
## Status & Compatibility
|
|
107
|
+
|
|
108
|
+
**Maturity**: Production-ready
|
|
109
|
+
|
|
110
|
+
**Python Support**: >=3.10
|
|
111
|
+
|
|
112
|
+
**OS Support**: Linux, macOS, Windows
|
|
113
|
+
|
|
114
|
+
**Status**: Actively maintained
|
|
115
|
+
|
|
116
|
+
## Useful Links
|
|
117
|
+
|
|
118
|
+
- **Homepage**: https://github.com/wexample/python-wex-addon-dev-python
|
|
119
|
+
- **Issue Tracker**: https://github.com/wexample/python-wex-addon-dev-python/issues
|
|
120
|
+
- **Discussions**: https://github.com/wexample/python-wex-addon-dev-python/discussions
|
|
121
|
+
- **PyPI**: [pypi.org/project/wexample-wex-addon-dev-python](https://pypi.org/project/wexample-wex-addon-dev-python/)
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
wexample_wex_addon_dev_python-15.
|
|
2
|
-
wexample_wex_addon_dev_python-15.
|
|
3
|
-
wexample_wex_addon_dev_python-15.
|
|
1
|
+
wexample_wex_addon_dev_python-15.3.1.dist-info/METADATA,sha256=-7678Y_0wsDsSxdGcKV69ZBZol1sLzmHQV8eyF6PkoI,3508
|
|
2
|
+
wexample_wex_addon_dev_python-15.3.1.dist-info/WHEEL,sha256=IuIk3wbaEnFH1cx75iM2R1CmliRI3dFLXTbRakgvgFQ,90
|
|
3
|
+
wexample_wex_addon_dev_python-15.3.1.dist-info/entry_points.txt,sha256=4WCjdYUa09eJPSM1ahhnId0BTg7ncgmLgeKtfKjuUy4,127
|
|
4
4
|
wexample_wex_addon_dev_python/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
-
wexample_wex_addon_dev_python/__pycache__/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
5
|
wexample_wex_addon_dev_python/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
6
|
wexample_wex_addon_dev_python/commands/code/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
7
|
wexample_wex_addon_dev_python/commands/code/check.py,sha256=TN2gsIjcte11cOCVq4hq4-8cJPemft7iIffYqsTqyus,3662
|
|
@@ -21,17 +20,14 @@ wexample_wex_addon_dev_python/commands/examples/utils/some_example_type.py,sha25
|
|
|
21
20
|
wexample_wex_addon_dev_python/commands/examples/validate.py,sha256=Ab_rIX2XElVSLLGypNPsuxRX6100vsfffaUaPgRV_wc,917
|
|
22
21
|
wexample_wex_addon_dev_python/commands/release/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
22
|
wexample_wex_addon_dev_python/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
|
-
wexample_wex_addon_dev_python/common/pypi_registry_gateway.py,sha256=
|
|
23
|
+
wexample_wex_addon_dev_python/common/pypi_registry_gateway.py,sha256=U-E21T_244IX66HKFFa2bwX1U6vxSgOfjZBPLh2Ku_s,2511
|
|
25
24
|
wexample_wex_addon_dev_python/config_value/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
|
-
wexample_wex_addon_dev_python/config_value/__pycache__/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
27
25
|
wexample_wex_addon_dev_python/config_value/python_package_readme_config_value.py,sha256=Teq9-j7NCXsZpkxAM9FHilV7fL_BtQS70xmIFRJWCGc,1865
|
|
28
26
|
wexample_wex_addon_dev_python/const/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
29
|
-
wexample_wex_addon_dev_python/const/__pycache__/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
30
27
|
wexample_wex_addon_dev_python/const/package.py,sha256=oRCPhaazJp5TujxF-35rrIYA4FJsqMqCns8lOFKOLeA,451
|
|
31
28
|
wexample_wex_addon_dev_python/const/python.py,sha256=swBdTT6GHCZMdFMBLL0eiqt1PKZUwyRCqEpBYbKcvEo,330
|
|
32
29
|
wexample_wex_addon_dev_python/const/tags.py,sha256=isvxhaBbendC6k_rluiJkQ2-yO7OwCEw9wse4wgEOIo,338
|
|
33
30
|
wexample_wex_addon_dev_python/file/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
|
-
wexample_wex_addon_dev_python/file/__pycache__/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
31
|
wexample_wex_addon_dev_python/file/python_app_iml_file.py,sha256=O6OpMjB_Zn4Mt1sIi9AaGvQP4jM1I2_L_6O6S5-syJM,1206
|
|
36
32
|
wexample_wex_addon_dev_python/file/python_pyproject_toml_file.py,sha256=2H0XismkdR-fF4wvCcGON3b1SKbnbG_GaRx5gW9bMOk,17108
|
|
37
33
|
wexample_wex_addon_dev_python/formatter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -39,7 +35,6 @@ wexample_wex_addon_dev_python/formatter/python_code_formatter.py,sha256=KIv2ddw0
|
|
|
39
35
|
wexample_wex_addon_dev_python/helper/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
40
36
|
wexample_wex_addon_dev_python/helper/pdm.py,sha256=Q75RgR4xnJCQaGSY24Y4O3Y7qLSyUl0iUyYIbBtXUho,473
|
|
41
37
|
wexample_wex_addon_dev_python/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
42
|
-
wexample_wex_addon_dev_python/middleware/__pycache__/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
43
38
|
wexample_wex_addon_dev_python/middleware/each_python_file_middleware.py,sha256=0BmUS1BlL8NY_imwBtykWxf4dnabA3nTrpVHhl9qjLE,2314
|
|
44
39
|
wexample_wex_addon_dev_python/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
45
40
|
wexample_wex_addon_dev_python/python_addon_manager.py,sha256=0ayvkqn9uJYyK7IXqHKcWRP9w8WmRJwcJDWjef-Nh94,2725
|
|
@@ -61,6 +56,7 @@ wexample_wex_addon_dev_python/resources/ai/agents/describer/agent.yml,sha256=afT
|
|
|
61
56
|
wexample_wex_addon_dev_python/resources/ai/agents/describer/context.j2,sha256=OTFLDQhKikePO20fRviui74Upwiyp42bMi6KPw_h1ws,2026
|
|
62
57
|
wexample_wex_addon_dev_python/resources/design_rules/python_code.md,sha256=Xl6oGZnhuWIzmlcCI49p6SJmLFaQAQeGpr4hqjquWC4,974
|
|
63
58
|
wexample_wex_addon_dev_python/resources/docker/Dockerfile.python-profiling,sha256=5ujzXfWNN2nv8qQzCLel6dlOwLQ1XhIGZuqplCNB_vQ,290
|
|
59
|
+
wexample_wex_addon_dev_python/resources/package_gitlab_ci.yml,sha256=X2aaKG710DyiWlMaeO1sMHs8YbtRDOj_1kMROKdS4s8,155
|
|
64
60
|
wexample_wex_addon_dev_python/resources/package_publish_gitlab.yml,sha256=WVMj-DeFedQak5s5FraEnM1Rx1-ut4KgPodvAVv25ZM,304
|
|
65
61
|
wexample_wex_addon_dev_python/resources/readme_templates/install.md.j2,sha256=PDfD-2_HQXKtgq8seSRDL5SGbZ2BRrxDEKoFpO7qhb8,104
|
|
66
62
|
wexample_wex_addon_dev_python/resources/readme_templates/tests.md.j2,sha256=tKLcDwx7jhQkryXIxWr12AK-hKEaP6Rusu2MrluiABs,1289
|
|
@@ -87,10 +83,9 @@ wexample_wex_addon_dev_python/services/python/samples/docker/images/Dockerfile.d
|
|
|
87
83
|
wexample_wex_addon_dev_python/services/python/samples/docker/images/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
88
84
|
wexample_wex_addon_dev_python/services/python/service.yml,sha256=28gvNQIzZ0T0PBwQLH-Mbn-_T92sbfhXe0tZHMBairs,105
|
|
89
85
|
wexample_wex_addon_dev_python/workdir/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
90
|
-
wexample_wex_addon_dev_python/workdir/__pycache__/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
91
86
|
wexample_wex_addon_dev_python/workdir/mixin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
92
87
|
wexample_wex_addon_dev_python/workdir/mixin/with_profiling_python_workdir_mixin.py,sha256=PA_kqwe2kF5t1nKakX5Q-6fant2R18aEnZfkjr6iJ6k,3097
|
|
93
|
-
wexample_wex_addon_dev_python/workdir/python_package_workdir.py,sha256=
|
|
88
|
+
wexample_wex_addon_dev_python/workdir/python_package_workdir.py,sha256=zxPagcd9CvYc0nvX6Ry7kJj_0VLcI7fmvBAisvDQy4M,23310
|
|
94
89
|
wexample_wex_addon_dev_python/workdir/python_packages_suite_workdir.py,sha256=7Jb5mKGNV2RjFtrFhUm6jbkiTfLDmy7sA97UGHITVdw,2902
|
|
95
|
-
wexample_wex_addon_dev_python/workdir/python_workdir.py,sha256=
|
|
96
|
-
wexample_wex_addon_dev_python-15.
|
|
90
|
+
wexample_wex_addon_dev_python/workdir/python_workdir.py,sha256=1t4k3GX-3aNaV4aUb-NSstlDNtwPvmeGolPs9g3laI8,32833
|
|
91
|
+
wexample_wex_addon_dev_python-15.3.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -1,315 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: wexample-wex-addon-dev-python
|
|
3
|
-
Version: 15.2.1
|
|
4
|
-
Summary: Extends wex with commands to format, lint, and rename Python symbols, plus workdir types for Python packages and suites
|
|
5
|
-
Author-Email: weeger <contact@wexample.com>
|
|
6
|
-
License: MIT
|
|
7
|
-
Classifier: Programming Language :: Python :: 3
|
|
8
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
-
Classifier: Operating System :: OS Independent
|
|
10
|
-
Project-URL: homepage, https://github.com/wexample/python-wex-dev-python
|
|
11
|
-
Requires-Python: >=3.10
|
|
12
|
-
Requires-Dist: attrs>=23.1.0
|
|
13
|
-
Requires-Dist: black
|
|
14
|
-
Requires-Dist: cattrs>=23.1.0
|
|
15
|
-
Requires-Dist: griffe>=2.0.2
|
|
16
|
-
Requires-Dist: isort
|
|
17
|
-
Requires-Dist: networkx
|
|
18
|
-
Requires-Dist: pylint
|
|
19
|
-
Requires-Dist: pyright
|
|
20
|
-
Requires-Dist: wexample-api>=6.8.0
|
|
21
|
-
Requires-Dist: wexample-filestate-python>=9.1.0
|
|
22
|
-
Requires-Dist: wexample-wex-addon-ai>=19.0.0
|
|
23
|
-
Requires-Dist: wexample-wex-addon-app>=34.0.0
|
|
24
|
-
Provides-Extra: dev
|
|
25
|
-
Requires-Dist: pytest; extra == "dev"
|
|
26
|
-
Requires-Dist: pytest-cov; extra == "dev"
|
|
27
|
-
Description-Content-Type: text/markdown
|
|
28
|
-
|
|
29
|
-
# wex_addon_dev_python
|
|
30
|
-
|
|
31
|
-
Version: 15.2.1
|
|
32
|
-
|
|
33
|
-
`wex_addon_dev_python` extends the wex CLI with a `python::` command group that formats source files with isort and black (`python::code/format`), checks them with mypy, pylint, and pyright (`python::code/check`), and renames Python symbols — packages, modules, classes, functions, constants — while propagating every affected import statement across the workdir (`python::code/rename`). It also ships `PythonPackageWorkdir` and `PythonPackagesSuiteWorkdir`, two workdir types that let wex manage a single PDM-based package or a multi-package suite as a first-class project structure. The intended users are wex operators who develop and maintain Python packages within the wexample ecosystem.
|
|
34
|
-
|
|
35
|
-
## Table of Contents
|
|
36
|
-
|
|
37
|
-
- [Installation](#installation)
|
|
38
|
-
- [Quickstart](#quickstart)
|
|
39
|
-
- [Tests](#tests)
|
|
40
|
-
- [Architecture](#architecture)
|
|
41
|
-
- [Integration in the Suite](#integration-in-the-suite)
|
|
42
|
-
- [Dependencies](#dependencies)
|
|
43
|
-
- [Versioning & Compatibility Policy](#versioning--compatibility-policy)
|
|
44
|
-
- [License](#license)
|
|
45
|
-
- [About us](#about-us)
|
|
46
|
-
- [Known Limitations & Roadmap](#known-limitations--roadmap)
|
|
47
|
-
- [Status & Compatibility](#status--compatibility)
|
|
48
|
-
- [Useful Links](#useful-links)
|
|
49
|
-
- [Migration Notes](#migration-notes)
|
|
50
|
-
|
|
51
|
-
## Installation
|
|
52
|
-
|
|
53
|
-
```bash
|
|
54
|
-
pip install wexample-wex-addon-dev-python
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
Requires Python >=3.10.
|
|
58
|
-
|
|
59
|
-
## Quickstart
|
|
60
|
-
|
|
61
|
-
Install from PyPI:
|
|
62
|
-
|
|
63
|
-
```bash
|
|
64
|
-
pip install wexample-wex-addon-dev-python
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
That makes the `python::` command group available to wex. Pass any file or directory to `--file`; the middleware expands globs and recurses automatically.
|
|
68
|
-
|
|
69
|
-
Format a source tree — isort sorts imports, black reformats the rest:
|
|
70
|
-
|
|
71
|
-
```bash
|
|
72
|
-
wex python::code/format --file src/mypackage/
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
Run the three static-analysis tools (mypy, pylint, pyright) against the same tree:
|
|
76
|
-
|
|
77
|
-
```bash
|
|
78
|
-
wex python::code/check --file src/mypackage/
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
Rename a Python symbol and rewrite every import statement that references it across the workdir. The `--dry_run` flag prints which files would change without touching them:
|
|
82
|
-
|
|
83
|
-
```bash
|
|
84
|
-
wex python::code/rename \
|
|
85
|
-
--source mylib.helpers \
|
|
86
|
-
--target mylib.helper \
|
|
87
|
-
--kind package \
|
|
88
|
-
--dry_run
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
Drop `--dry_run` to apply. Valid `--kind` values are `package`, `module`, `class`, `function`, and `constant`.
|
|
92
|
-
|
|
93
|
-
## Tests
|
|
94
|
-
|
|
95
|
-
This project uses `pytest` for testing and `pytest-cov` for code coverage analysis.
|
|
96
|
-
|
|
97
|
-
### Installation
|
|
98
|
-
|
|
99
|
-
First, install the required testing dependencies:
|
|
100
|
-
```bash
|
|
101
|
-
.venv/bin/python -m pip install pytest pytest-cov
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
### Basic Usage
|
|
105
|
-
|
|
106
|
-
Run all tests with coverage:
|
|
107
|
-
```bash
|
|
108
|
-
.venv/bin/python -m pytest --cov --cov-report=html
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
### Common Commands
|
|
112
|
-
```bash
|
|
113
|
-
# Run tests with coverage for a specific module
|
|
114
|
-
.venv/bin/python -m pytest --cov=your_module
|
|
115
|
-
|
|
116
|
-
# Show which lines are not covered
|
|
117
|
-
.venv/bin/python -m pytest --cov=your_module --cov-report=term-missing
|
|
118
|
-
|
|
119
|
-
# Generate an HTML coverage report
|
|
120
|
-
.venv/bin/python -m pytest --cov=your_module --cov-report=html
|
|
121
|
-
|
|
122
|
-
# Combine terminal and HTML reports
|
|
123
|
-
.venv/bin/python -m pytest --cov=your_module --cov-report=term-missing --cov-report=html
|
|
124
|
-
|
|
125
|
-
# Run specific test file with coverage
|
|
126
|
-
.venv/bin/python -m pytest tests/test_file.py --cov=your_module --cov-report=term-missing
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
### Viewing HTML Reports
|
|
130
|
-
|
|
131
|
-
After generating an HTML report, open `htmlcov/index.html` in your browser to view detailed line-by-line coverage information.
|
|
132
|
-
|
|
133
|
-
### Coverage Threshold
|
|
134
|
-
|
|
135
|
-
To enforce a minimum coverage percentage:
|
|
136
|
-
```bash
|
|
137
|
-
.venv/bin/python -m pytest --cov=your_module --cov-fail-under=80
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
This will cause the test suite to fail if coverage drops below 80%.
|
|
141
|
-
|
|
142
|
-
## Architecture
|
|
143
|
-
|
|
144
|
-
The package is a wex addon. Its single registration point is src/wexample_wex_addon_dev_python/python_addon_manager.py — a `PythonAddonManager` that inherits `AbstractAddonManager` and declares four contributions:
|
|
145
|
-
|
|
146
|
-
- **middlewares** — `EachPythonFileMiddleware`
|
|
147
|
-
- **selections** — `PythonCodePerformanceSelection`
|
|
148
|
-
- **workdir types** — `python`, `python-package`, `python-packages-suite`
|
|
149
|
-
- **configurable keys** — `PDM_BIN_DIR`, detected and applied by src/wexample_wex_addon_dev_python/helper/pdm.py
|
|
150
|
-
|
|
151
|
-
Everything else in the package implements one of those four surfaces.
|
|
152
|
-
|
|
153
|
-
### Middleware
|
|
154
|
-
|
|
155
|
-
src/wexample_wex_addon_dev_python/middleware/each_python_file_middleware.py extends `EachFileMiddleware`. It filters `.py` files by extension and refuses to recurse into `__pycache__`, `.git`, `.venv`, `.mypy_cache`, and similar directories. Commands that operate file-by-file declare `@middleware(name="each_python_file")` to let the kernel expand a path argument into individual files before calling the command function.
|
|
156
|
-
|
|
157
|
-
### Commands
|
|
158
|
-
|
|
159
|
-
All three live under `src/wexample_wex_addon_dev_python/commands/code/`.
|
|
160
|
-
|
|
161
|
-
**Format** — src/wexample_wex_addon_dev_python/commands/code/format.py runs isort then black in that order on one file. An optional `--tool` flag restricts execution to a single tool. The command returns `False` on the first failure when `--stop_on_failure` is set.
|
|
162
|
-
|
|
163
|
-
**Check** — src/wexample_wex_addon_dev_python/commands/code/check.py runs mypy, pylint, and pyright in sequence. Pylint is invoked with `--output-format=json`; only messages of type `error` or `fatal` cause a `False` return; warnings and conventions are reported but do not fail the check.
|
|
164
|
-
|
|
165
|
-
**Rename** — src/wexample_wex_addon_dev_python/commands/code/rename.py accepts `--source`, `--target`, and `--kind`. It resolves the `SymbolKind` enum, picks a handler class, delegates to `handler.run()`, and returns a `TableResponse` listing the changed files. A `--dry_run` flag stops the handler after the plan phase, touching nothing on disk.
|
|
166
|
-
|
|
167
|
-
### Refactor subsystem
|
|
168
|
-
|
|
169
|
-
The refactor package implements a three-phase rename pipeline.
|
|
170
|
-
|
|
171
|
-
src/wexample_wex_addon_dev_python/refactor/abstract_python_rename_handler.py defines the pipeline: `compute_plan()` (scan only, no writes) → `preflight()` (detect permission errors and missing parents) → `apply()` (hand the plan to a `RenameApplier`). Subclasses implement `compute_plan` and `_build_report`.
|
|
172
|
-
|
|
173
|
-
src/wexample_wex_addon_dev_python/refactor/rename_plan.py is the data structure. It carries:
|
|
174
|
-
- `rewrites` — per-file new source text
|
|
175
|
-
- `path_renames` — `(old, new)` pairs for files or directories
|
|
176
|
-
- `directories_to_create` — dirs that must exist before rewrites run
|
|
177
|
-
- `files_to_delete`, `directories_to_remove`, `directories_to_purge` — cleanup after moves
|
|
178
|
-
|
|
179
|
-
src/wexample_wex_addon_dev_python/refactor/applier.py defines `RenameApplier` (abstract strategy) and `NaiveRenameApplier` (direct `os`/`shutil` calls). The sequencing is: create dirs → write files → rename paths → delete files → purge artefact dirs → remove empty dirs.
|
|
180
|
-
|
|
181
|
-
src/wexample_wex_addon_dev_python/refactor/symbol_kind.py lists the supported kinds: `package`, `module`, `class`, `function`, `constant`. `method` is declared in the enum but not implemented; the comment explains why (dynamic typing makes safe method-rename impossible without a full type-inference pipeline).
|
|
182
|
-
|
|
183
|
-
The package handler adds merge logic: when the target directory already exists, each child of the source is moved in, identical `__init__.py` files are dropped, and the empty source dir is removed. Non-identical file conflicts abort with a descriptive error.
|
|
184
|
-
|
|
185
|
-
### Workdir hierarchy
|
|
186
|
-
|
|
187
|
-
**`PythonWorkdir`** — src/wexample_wex_addon_dev_python/workdir/python_workdir.py is the base for every Python project. It mixes in `WithAiWorkdirMixin`, `WithProfilingPythonWorkdirMixin`, and `CodeBaseWorkdir`. Responsibilities:
|
|
188
|
-
|
|
189
|
-
- venv path resolution (local `.venv` or a suite-level `python.venv_path` from runtime config)
|
|
190
|
-
- `app_install` — creates the venv, then calls `_install_dependencies_in_venv`
|
|
191
|
-
- test execution via `test_run` / `test_get_command` (pytest + pytest-cov, JSON report stored in `.wex/local/`)
|
|
192
|
-
- filestate layout declaration in `prepare_value`: `src/{vendor}_{name}/`, `tests/`, `helpers/`, plus the full set of Python file options (future annotations, sort imports, modernize typing, order class members, format, …)
|
|
193
|
-
- `get_app_config_file` — returns the managed `PythonPyprojectTomlFile`
|
|
194
|
-
- `update_dependencies` — updates `pyproject.toml` then re-pins `requirements.txt` via `uv pip compile` with retry logic for PyPI propagation races
|
|
195
|
-
|
|
196
|
-
**`PythonPackageWorkdir`** — src/wexample_wex_addon_dev_python/workdir/python_package_workdir.py extends `PythonWorkdir` for distributable packages. Additional responsibilities:
|
|
197
|
-
|
|
198
|
-
- publish via `pdm publish` (PyPI) or a git tag push (private GitLab registry when `pdm.repository.url` is set)
|
|
199
|
-
- `_wait_for_registry` — polls the PEP 503 Simple index through `PypiRegistryGateway` until the new version appears
|
|
200
|
-
- `_classify_version_bump` — uses `griffe` to compare the current AST against the previous git tag and determine major vs. intermediate bump
|
|
201
|
-
- suite-aware install in local env: external deps are pip-installed normally; suite packages are installed in editable mode, leaf-to-trunk order, skipping packages already editable at the right path
|
|
202
|
-
- `get_required_knowledge_pages` adds `usage/quickstart` to the pages the workdir expects to exist
|
|
203
|
-
|
|
204
|
-
**`PythonPackagesSuiteWorkdir`** — src/wexample_wex_addon_dev_python/workdir/python_packages_suite_workdir.py extends `FrameworkPackageSuiteWorkdir`. It treats any subdirectory of `pip/` that contains a `pyproject.toml` as a package. `build_dependencies_stack` builds a `networkx` directed graph from the local dependency map and returns the shortest path from one package to another as concrete `PythonPackageWorkdir` objects.
|
|
205
|
-
|
|
206
|
-
**`WithProfilingPythonWorkdirMixin`** — src/wexample_wex_addon_dev_python/workdir/mixin/with_profiling_python_workdir_mixin.py runs `pytest --benchmark-only` in the project's venv, writes a temporary JSON report, parses benchmark stats (min, mean, median, max in ms, rounds), and returns the result as a structured dict.
|
|
207
|
-
|
|
208
|
-
### File objects
|
|
209
|
-
|
|
210
|
-
src/wexample_wex_addon_dev_python/file/python_pyproject_toml_file.py wraps `pyproject.toml` as a managed filestate object. Its `dumps` method enforces canonical structure on every write: build-system pinned to `pdm-backend`, PDM build config derived from the workdir's `get_src_import_name()`, project metadata from wex config, dependencies sorted, dev group always containing `pytest` and `pytest-cov`, pytest and coverage tool config injected, and sections reordered (`build-system` → `project` → `tool`).
|
|
211
|
-
|
|
212
|
-
### Selection
|
|
213
|
-
|
|
214
|
-
src/wexample_wex_addon_dev_python/selection/abstract_python_code_selection.py establishes the Python-file baseline for AI agent selections: `.py` extension only, skip `__init__.py`, skip `__pycache__` / `/tests/` / `/build/` / `/dist/` / `/.venv/` / `/.pdm-build/`, 50-byte minimum.
|
|
215
|
-
|
|
216
|
-
src/wexample_wex_addon_dev_python/selection/python_code_performance_selection.py inherits that baseline and adds two constraints: the file must be under `/src/`, and its AST must contain at least one non-trivial function. Trivial is defined as: `pass`, `...`, docstring-only, `raise NotImplementedError`, a same-name `super()` pass-through, or any `@abstractmethod`.
|
|
217
|
-
|
|
218
|
-
src/wexample_wex_addon_dev_python/selection/package_description_missing_selection.py selects the package's own `.wex/config.yml`, and only when `global.description` is empty. Selecting the config file rather than the package makes the item what is to be edited, so the agent writes that one field and leaves the rest of the file alone. The description is copied verbatim into `pyproject.toml` and shown on the package registry.
|
|
219
|
-
|
|
220
|
-
This replaced a filestate operation that called the agent from inside `apply_operation`. Writing a description is a job given to an agent, not a file state to converge on: rectification retried it every pass, and an agent declining to invent a description for a package with no code kept the hole open, which kept the retry going.
|
|
221
|
-
|
|
222
|
-
### Supporting pieces
|
|
223
|
-
|
|
224
|
-
src/wexample_wex_addon_dev_python/common/pypi_registry_gateway.py is a thin HTTP client over the PEP 503 Simple index. It accepts optional Basic auth (`username` + `token`) and exposes `has_version(package, version) -> bool`.
|
|
225
|
-
|
|
226
|
-
src/wexample_wex_addon_dev_python/services/python/app_service.py contributes the initial `pyproject.toml` and `src/app/` scaffolding when a new Python app service is set up.
|
|
227
|
-
|
|
228
|
-
src/wexample_wex_addon_dev_python/const/tags.py declares the `DomainTag` constants (`domain:format`, `domain:language-python`, `domain:lint`, `domain:service`) used in command `@command(tags=[…])` decorators.
|
|
229
|
-
|
|
230
|
-
### Call path for a rename
|
|
231
|
-
|
|
232
|
-
1. `python__code__rename` validates `--kind`, selects a handler class, calls `app_workdir.get_code_scope_paths(kernel)` for the set of roots.
|
|
233
|
-
2. The handler's `__attrs_post_init__` splits the dotted names and validates they are non-empty.
|
|
234
|
-
3. `handler.run()` calls `compute_plan()` — the handler scans every root for the source pattern, builds a `RenamePlan` (rewrites + path moves), returns it.
|
|
235
|
-
4. If not `dry_run`, `plan.preflight()` checks writability and parent existence. Any problem raises `RuntimeError` before a single file is touched.
|
|
236
|
-
5. `NaiveRenameApplier().apply(plan)` executes the six steps in sequence.
|
|
237
|
-
6. The command formats the report into a `TableResponse`.
|
|
238
|
-
|
|
239
|
-
## Integration in the Suite
|
|
240
|
-
|
|
241
|
-
This package is part of the Wexample Suite — a collection of high-quality, modular tools designed to work seamlessly together across multiple languages and environments.
|
|
242
|
-
|
|
243
|
-
### Related Packages
|
|
244
|
-
|
|
245
|
-
The suite includes packages for configuration management, file handling, prompts, and more. Each package can be used independently or as part of the integrated suite.
|
|
246
|
-
|
|
247
|
-
## Dependencies
|
|
248
|
-
|
|
249
|
-
- attrs: >=23.1.0
|
|
250
|
-
- black:
|
|
251
|
-
- cattrs: >=23.1.0
|
|
252
|
-
- griffe: >=2.0.2
|
|
253
|
-
- isort:
|
|
254
|
-
- networkx:
|
|
255
|
-
- pylint:
|
|
256
|
-
- pyright:
|
|
257
|
-
- wexample-api: >=6.8.0
|
|
258
|
-
- wexample-filestate-python: >=9.1.0
|
|
259
|
-
- wexample-wex-addon-ai: >=19.0.0
|
|
260
|
-
- wexample-wex-addon-app: >=34.0.0
|
|
261
|
-
|
|
262
|
-
## Versioning & Compatibility Policy
|
|
263
|
-
|
|
264
|
-
Wexample packages follow **Semantic Versioning** (SemVer):
|
|
265
|
-
|
|
266
|
-
- **MAJOR**: Breaking changes
|
|
267
|
-
- **MINOR**: New features, backward compatible
|
|
268
|
-
- **PATCH**: Bug fixes, backward compatible
|
|
269
|
-
|
|
270
|
-
We maintain backward compatibility within major versions and provide clear migration guides for breaking changes.
|
|
271
|
-
|
|
272
|
-
## License
|
|
273
|
-
|
|
274
|
-
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
275
|
-
|
|
276
|
-
Free to use in both personal and commercial projects.
|
|
277
|
-
|
|
278
|
-
## About us
|
|
279
|
-
|
|
280
|
-
[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.
|
|
281
|
-
|
|
282
|
-
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.
|
|
283
|
-
|
|
284
|
-
Wexample cultivates a culture of mastery. Each package, each contribution carries the mark of a community that values precision, ethics, and innovation — a community proud to shape the future of digital craftsmanship.
|
|
285
|
-
|
|
286
|
-
Built and maintained by [Romain Weeger](https://weeger.io).
|
|
287
|
-
|
|
288
|
-
## Known Limitations & Roadmap
|
|
289
|
-
|
|
290
|
-
Current limitations and planned features are tracked in the GitHub issues.
|
|
291
|
-
|
|
292
|
-
See the [project roadmap](https://github.com/wexample/python-wex_addon_dev_python/issues) for upcoming features and improvements.
|
|
293
|
-
|
|
294
|
-
## Status & Compatibility
|
|
295
|
-
|
|
296
|
-
**Maturity**: Production-ready
|
|
297
|
-
|
|
298
|
-
**Python Support**: >=3.10
|
|
299
|
-
|
|
300
|
-
**OS Support**: Linux, macOS, Windows
|
|
301
|
-
|
|
302
|
-
**Status**: Actively maintained
|
|
303
|
-
|
|
304
|
-
## Useful Links
|
|
305
|
-
|
|
306
|
-
- **Homepage**: https://github.com/wexample/python-wex-addon-dev-python
|
|
307
|
-
- **Issue Tracker**: https://github.com/wexample/python-wex-addon-dev-python/issues
|
|
308
|
-
- **Discussions**: https://github.com/wexample/python-wex-addon-dev-python/discussions
|
|
309
|
-
- **PyPI**: [pypi.org/project/wexample-wex-addon-dev-python](https://pypi.org/project/wexample-wex-addon-dev-python/)
|
|
310
|
-
|
|
311
|
-
## Migration Notes
|
|
312
|
-
|
|
313
|
-
When upgrading between major versions, refer to the migration guides in the documentation.
|
|
314
|
-
|
|
315
|
-
Breaking changes are clearly documented with upgrade paths and examples.
|
|
File without changes
|
|
File without changes
|