modern-python-guidance 0.1.1__tar.gz → 0.1.2__tar.gz

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.
Files changed (60) hide show
  1. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/CHANGELOG.md +13 -0
  2. modern_python_guidance-0.1.2/LICENSE-MIT +21 -0
  3. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/PKG-INFO +11 -9
  4. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/README.md +7 -7
  5. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/pyproject.toml +3 -2
  6. modern_python_guidance-0.1.2/skills/modern-python-guidance/SKILL.md +62 -0
  7. modern_python_guidance-0.1.2/tests/test_skill_sync.py +126 -0
  8. modern_python_guidance-0.1.1/skills/modern-python-guidance/SKILL.md +0 -104
  9. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/.github/workflows/ci.yml +0 -0
  10. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/.github/workflows/publish.yml +0 -0
  11. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/.gitignore +0 -0
  12. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/LICENSE +0 -0
  13. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/SECURITY.md +0 -0
  14. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/docs/design.md +0 -0
  15. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/async/async-timeout-context.md +0 -0
  16. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/async/exception-groups.md +0 -0
  17. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/async/taskgroup-over-gather.md +0 -0
  18. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/data-structures/dataclass-modern.md +0 -0
  19. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/data-structures/dict-merge-operator.md +0 -0
  20. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/data-structures/match-case-patterns.md +0 -0
  21. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/fastapi/fastapi-annotated-depends.md +0 -0
  22. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/fastapi/fastapi-lifespan.md +0 -0
  23. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/fastapi/fastapi-typed-state.md +0 -0
  24. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/httpx/httpx-async-client-reuse.md +0 -0
  25. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/httpx/httpx-streaming.md +0 -0
  26. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/pydantic/pydantic-v2-config.md +0 -0
  27. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/pydantic/pydantic-v2-model-api.md +0 -0
  28. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/pydantic/pydantic-v2-serialization.md +0 -0
  29. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/pydantic/pydantic-v2-validators.md +0 -0
  30. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/stdlib/datetime-utc.md +0 -0
  31. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/stdlib/pathlib-over-os-path.md +0 -0
  32. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/stdlib/removeprefix-removesuffix.md +0 -0
  33. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/stdlib/tomllib-builtin.md +0 -0
  34. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/toolchain/no-pickle.md +0 -0
  35. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/toolchain/pyproject-toml-over-setup.md +0 -0
  36. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/toolchain/ruff-over-flake8.md +0 -0
  37. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/toolchain/safe-subprocess.md +0 -0
  38. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/toolchain/uv-over-pip.md +0 -0
  39. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/typing/override-decorator.md +0 -0
  40. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/typing/paramspec-decorators.md +0 -0
  41. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/typing/type-parameter-syntax.md +0 -0
  42. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/typing/typeis-vs-typeguard.md +0 -0
  43. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/typing/union-syntax.md +0 -0
  44. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/skills/modern-python-guidance/guides/typing/use-builtin-generics.md +0 -0
  45. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/src/modern_python_guidance/__init__.py +0 -0
  46. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/src/modern_python_guidance/__main__.py +0 -0
  47. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/src/modern_python_guidance/cli.py +0 -0
  48. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/src/modern_python_guidance/compat.py +0 -0
  49. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/src/modern_python_guidance/frontmatter.py +0 -0
  50. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/src/modern_python_guidance/guide_index.py +0 -0
  51. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/src/modern_python_guidance/mcp_server.py +0 -0
  52. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/src/modern_python_guidance/retrieve.py +0 -0
  53. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/src/modern_python_guidance/search.py +0 -0
  54. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/src/modern_python_guidance/version_detect.py +0 -0
  55. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/tests/test_cli_integration.py +0 -0
  56. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/tests/test_frontmatter.py +0 -0
  57. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/tests/test_mcp_server.py +0 -0
  58. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/tests/test_retrieve.py +0 -0
  59. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/tests/test_search.py +0 -0
  60. {modern_python_guidance-0.1.1 → modern_python_guidance-0.1.2}/tests/test_version_detect.py +0 -0
@@ -2,6 +2,18 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [0.1.2] — 2026-05-26
6
+
7
+ ### Changed
8
+
9
+ - SKILL.md: replace inventory tables with 9 embedded BAD→GOOD arrow-list patterns (high-frequency × Ruff-uncovered) for pre-generation injection without MCP tool calls
10
+ - README: Quick start example changed from `use-builtin-generics` to `pydantic-v2-validators` (Layer 2 differentiation)
11
+
12
+ ### Added
13
+
14
+ - MIT license (dual-licensed under Apache-2.0 OR MIT)
15
+ - `test_skill_sync.py`: 8 sync tests for SKILL.md ↔ guide file consistency (V-001/V-002/V-009/V-010)
16
+
5
17
  ## [0.1.1] — 2026-05-25
6
18
 
7
19
  ### Added
@@ -30,5 +42,6 @@ Initial release.
30
42
  - Strict YAML-subset frontmatter parser (no PyYAML dependency)
31
43
  - GitHub Actions CI (pytest + ruff on Python 3.11, 3.12, 3.13)
32
44
 
45
+ [0.1.2]: https://github.com/yottayoshida/modern-python-guidance/releases/tag/v0.1.2
33
46
  [0.1.1]: https://github.com/yottayoshida/modern-python-guidance/releases/tag/v0.1.1
34
47
  [0.1.0]: https://github.com/yottayoshida/modern-python-guidance/releases/tag/v0.1.0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Iori Yoshida
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,18 +1,20 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modern-python-guidance
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: Version-aware BAD/GOOD pattern guides that help AI coding agents generate modern Python
5
5
  Project-URL: Homepage, https://github.com/yottayoshida/modern-python-guidance
6
6
  Project-URL: Repository, https://github.com/yottayoshida/modern-python-guidance
7
7
  Project-URL: Issues, https://github.com/yottayoshida/modern-python-guidance/issues
8
8
  Author-email: Iori Yoshida <i.yoshida@raksul.com>
9
- License-Expression: Apache-2.0
9
+ License-Expression: Apache-2.0 OR MIT
10
10
  License-File: LICENSE
11
+ License-File: LICENSE-MIT
11
12
  Keywords: ai,coding-agent,guidance,linter,modernization,python
12
13
  Classifier: Development Status :: 3 - Alpha
13
14
  Classifier: Environment :: Console
14
15
  Classifier: Intended Audience :: Developers
15
16
  Classifier: License :: OSI Approved :: Apache Software License
17
+ Classifier: License :: OSI Approved :: MIT License
16
18
  Classifier: Programming Language :: Python :: 3
17
19
  Classifier: Programming Language :: Python :: 3.11
18
20
  Classifier: Programming Language :: Python :: 3.12
@@ -45,17 +47,17 @@ LLMs often produce outdated Python — `typing.List` instead of `list`, `@valida
45
47
  pip install modern-python-guidance
46
48
 
47
49
  # Search for a pattern
48
- mpg search "typing list"
49
- # use-builtin-generics score=18.0 [typing]
50
+ mpg search "pydantic validator"
51
+ # pydantic-v2-validators score=18.0 [pydantic]
50
52
 
51
53
  # Get the full guide
52
- mpg retrieve use-builtin-generics
53
- # --- use-builtin-generics (version match: YES) ---
54
+ mpg retrieve pydantic-v2-validators
55
+ # --- pydantic-v2-validators (version match: YES) ---
54
56
  # ## BAD
55
- # from typing import List, Dict, Optional
57
+ # @validator("name")
56
58
  # ...
57
59
  # ## GOOD
58
- # names: list[str] = []
60
+ # @field_validator("name")
59
61
  # ...
60
62
  ```
61
63
 
@@ -212,4 +214,4 @@ Contributions welcome! To add a new guide:
212
214
 
213
215
  ## License
214
216
 
215
- Apache-2.0 — see [LICENSE](LICENSE).
217
+ Apache-2.0 OR MIT — see [LICENSE](LICENSE) and [LICENSE-MIT](LICENSE-MIT).
@@ -16,17 +16,17 @@ LLMs often produce outdated Python — `typing.List` instead of `list`, `@valida
16
16
  pip install modern-python-guidance
17
17
 
18
18
  # Search for a pattern
19
- mpg search "typing list"
20
- # use-builtin-generics score=18.0 [typing]
19
+ mpg search "pydantic validator"
20
+ # pydantic-v2-validators score=18.0 [pydantic]
21
21
 
22
22
  # Get the full guide
23
- mpg retrieve use-builtin-generics
24
- # --- use-builtin-generics (version match: YES) ---
23
+ mpg retrieve pydantic-v2-validators
24
+ # --- pydantic-v2-validators (version match: YES) ---
25
25
  # ## BAD
26
- # from typing import List, Dict, Optional
26
+ # @validator("name")
27
27
  # ...
28
28
  # ## GOOD
29
- # names: list[str] = []
29
+ # @field_validator("name")
30
30
  # ...
31
31
  ```
32
32
 
@@ -183,4 +183,4 @@ Contributions welcome! To add a new guide:
183
183
 
184
184
  ## License
185
185
 
186
- Apache-2.0 — see [LICENSE](LICENSE).
186
+ Apache-2.0 OR MIT — see [LICENSE](LICENSE) and [LICENSE-MIT](LICENSE-MIT).
@@ -4,10 +4,10 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "modern-python-guidance"
7
- version = "0.1.1"
7
+ version = "0.1.2"
8
8
  description = "Version-aware BAD/GOOD pattern guides that help AI coding agents generate modern Python"
9
9
  readme = "README.md"
10
- license = "Apache-2.0"
10
+ license = "Apache-2.0 OR MIT"
11
11
  requires-python = ">=3.11"
12
12
  authors = [
13
13
  { name = "Iori Yoshida", email = "i.yoshida@raksul.com" },
@@ -18,6 +18,7 @@ classifiers = [
18
18
  "Environment :: Console",
19
19
  "Intended Audience :: Developers",
20
20
  "License :: OSI Approved :: Apache Software License",
21
+ "License :: OSI Approved :: MIT License",
21
22
  "Programming Language :: Python :: 3",
22
23
  "Programming Language :: Python :: 3.11",
23
24
  "Programming Language :: Python :: 3.12",
@@ -0,0 +1,62 @@
1
+ ---
2
+ name: modern-python-guidance
3
+ description: Version-aware BAD/GOOD pattern guides for modern Python. Use when writing, reviewing, or refactoring Python code to avoid outdated patterns (e.g. typing.List → list, @validator → @field_validator, setup.py → pyproject.toml). Triggers on "Python", "modernize", "upgrade", "deprecated", "pydantic", "fastapi", "httpx", "typing", "dataclass", "asyncio".
4
+ ---
5
+
6
+ # Modern Python Guidance
7
+
8
+ Pre-generation design patterns that Ruff cannot auto-fix. Apply these transformations when writing or reviewing Python code.
9
+
10
+ ## When to use
11
+
12
+ - Writing new Python code (use modern patterns from the start)
13
+ - Reviewing Python code (flag outdated patterns)
14
+ - Migrating from Pydantic V1 to V2
15
+ - Upgrading Python version (check which new features are available)
16
+ - Replacing legacy tooling (setup.py, flake8, pip)
17
+
18
+ ## Embedded patterns (high-frequency, Ruff-uncovered)
19
+
20
+ ### Pydantic V2 (>=3.9)
21
+
22
+ - `@validator("f")` → `@field_validator("f")`
23
+ - `@root_validator` → `@model_validator(mode="after")`
24
+ - `class Config:` → `model_config = ConfigDict(...)`
25
+ - `orm_mode` → `from_attributes`, `allow_population_by_field_name` → `populate_by_name`
26
+ - `.parse_obj(d)` → `.model_validate(d)`, `.parse_raw(j)` → `.model_validate_json(j)`
27
+ - `.dict()` → `.model_dump()`, `.json()` → `.model_dump_json()`
28
+ - `.schema()` → `.model_json_schema()`, `.copy()` → `.model_copy()`
29
+
30
+ ### FastAPI (>=3.9)
31
+
32
+ - `@app.on_event("startup")`/`"shutdown"` → `@asynccontextmanager` lifespan + `FastAPI(lifespan=lifespan)`; yield dict becomes `request.state`
33
+ - `db: Session = Depends(get_db)` → `DbDep = Annotated[Session, Depends(get_db)]`; reusable type alias per PEP 593
34
+
35
+ ### httpx
36
+
37
+ - Per-request `async with httpx.AsyncClient()` → shared `AsyncClient` with `base_url`
38
+ - Caveat: shared client must be closed via `async with` or lifespan management
39
+
40
+ ### asyncio (>=3.11)
41
+
42
+ - `await asyncio.gather(a(), b())` → `async with asyncio.TaskGroup() as tg:` + `tg.create_task()`; access results via `task.result()`
43
+ - Caveat: 3.11+ only. `TaskGroup` cancels siblings on error and raises `ExceptionGroup`; `gather` preserves return order and supports `return_exceptions=True`
44
+
45
+ ### Toolchain
46
+
47
+ - `setup.py` / `setup.cfg` → `pyproject.toml` with `[build-system]` + `[project]` (PEP 621)
48
+ - `subprocess.run(f"cmd {arg}", shell=True)` → `subprocess.run(["cmd", arg], check=True)`
49
+ - Caveat: `shell=True` is valid when pipes/globs are needed; use `shlex.quote()` for user input
50
+
51
+ ## All 30 guides by category
52
+
53
+ - **typing** (6): `use-builtin-generics`, `union-syntax`, `type-parameter-syntax`, `override-decorator`, `typeis-vs-typeguard`, `paramspec-decorators`
54
+ - **async** (3): `taskgroup-over-gather`, `exception-groups`, `async-timeout-context`
55
+ - **stdlib** (4): `datetime-utc`, `pathlib-over-os-path`, `tomllib-builtin`, `removeprefix-removesuffix`
56
+ - **data-structures** (3): `dict-merge-operator`, `match-case-patterns`, `dataclass-modern`
57
+ - **pydantic** (4): `pydantic-v2-validators`, `pydantic-v2-config`, `pydantic-v2-model-api`, `pydantic-v2-serialization`
58
+ - **fastapi** (3): `fastapi-lifespan`, `fastapi-annotated-depends`, `fastapi-typed-state`
59
+ - **httpx** (2): `httpx-async-client-reuse`, `httpx-streaming`
60
+ - **toolchain** (5): `pyproject-toml-over-setup`, `uv-over-pip`, `ruff-over-flake8`, `no-pickle`, `safe-subprocess`
61
+
62
+ For full code examples, use `mpg retrieve <guide-id>` or MCP tool `retrieve_guides`.
@@ -0,0 +1,126 @@
1
+ """Sync tests: verify SKILL.md stays consistent with guide files and README.
2
+
3
+ Verification IDs from /plan QA shift-left:
4
+ V-001 SKILL.md guide IDs reference existing guide files
5
+ V-002 SKILL.md token count <= 1300 (chars/4)
6
+ V-009 All embedded guide IDs have frequency: high
7
+ V-010 README Quick start guide IDs reference existing guide files
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import re
13
+ from pathlib import Path
14
+
15
+ import pytest
16
+
17
+ from modern_python_guidance.guide_index import build_index
18
+
19
+ REPO_ROOT = Path(__file__).resolve().parent.parent
20
+ SKILL_MD = REPO_ROOT / "skills" / "modern-python-guidance" / "SKILL.md"
21
+ README_MD = REPO_ROOT / "README.md"
22
+ GUIDES_DIR = REPO_ROOT / "skills" / "modern-python-guidance" / "guides"
23
+
24
+ EMBEDDED_GUIDE_IDS = [
25
+ "pydantic-v2-validators",
26
+ "pydantic-v2-config",
27
+ "pydantic-v2-model-api",
28
+ "fastapi-lifespan",
29
+ "fastapi-annotated-depends",
30
+ "httpx-async-client-reuse",
31
+ "taskgroup-over-gather",
32
+ "pyproject-toml-over-setup",
33
+ "safe-subprocess",
34
+ ]
35
+
36
+
37
+ @pytest.fixture(scope="module")
38
+ def guide_index():
39
+ return build_index(GUIDES_DIR)
40
+
41
+
42
+ @pytest.fixture(scope="module")
43
+ def skill_text():
44
+ return SKILL_MD.read_text(encoding="utf-8")
45
+
46
+
47
+ @pytest.fixture(scope="module")
48
+ def readme_text():
49
+ return README_MD.read_text(encoding="utf-8")
50
+
51
+
52
+ def _extract_backtick_ids(text: str) -> list[str]:
53
+ """Extract guide IDs from backtick-quoted strings in the catalog section."""
54
+ return re.findall(r"`([a-z][a-z0-9-]+)`", text)
55
+
56
+
57
+ class TestV001SkillGuideSync:
58
+ """V-001: SKILL.md guide IDs reference existing guide files."""
59
+
60
+ def test_embedded_guides_exist(self, guide_index):
61
+ for guide_id in EMBEDDED_GUIDE_IDS:
62
+ assert guide_index.get(guide_id) is not None, (
63
+ f"Embedded guide '{guide_id}' not found in guides/"
64
+ )
65
+
66
+ def test_catalog_heading_exists(self, skill_text):
67
+ assert "## All 30 guides by category" in skill_text, (
68
+ "Catalog heading missing from SKILL.md"
69
+ )
70
+
71
+ def test_catalog_ids_exist(self, skill_text, guide_index):
72
+ catalog_section = skill_text.split("## All 30 guides by category")[-1]
73
+ ids_in_catalog = _extract_backtick_ids(catalog_section)
74
+ for guide_id in ids_in_catalog:
75
+ assert guide_index.get(guide_id) is not None, (
76
+ f"Catalog guide '{guide_id}' not found in guides/"
77
+ )
78
+
79
+ def test_catalog_count_matches(self, guide_index, skill_text):
80
+ assert "30 guides" in skill_text
81
+ assert len(guide_index) == 30, (
82
+ f"SKILL.md says 30 guides but found {len(guide_index)}"
83
+ )
84
+
85
+ def test_catalog_covers_all_guides(self, skill_text, guide_index):
86
+ """Reverse check: every guide ID appears in the catalog section."""
87
+ catalog_section = skill_text.split("## All 30 guides by category")[-1]
88
+ catalog_ids = set(_extract_backtick_ids(catalog_section))
89
+ for guide_id in guide_index.guides:
90
+ assert guide_id in catalog_ids, (
91
+ f"Guide '{guide_id}' exists in guides/ but missing from catalog"
92
+ )
93
+
94
+
95
+ class TestV002TokenBudget:
96
+ """V-002: SKILL.md token count <= 1300 (chars/4)."""
97
+
98
+ def test_token_budget(self, skill_text):
99
+ tokens = len(skill_text) // 4
100
+ assert tokens <= 1300, (
101
+ f"SKILL.md is {tokens} tokens (chars/4), budget is 1300"
102
+ )
103
+
104
+
105
+ class TestV009EmbeddedFrequency:
106
+ """V-009: All embedded guide IDs have frequency: high."""
107
+
108
+ def test_all_high_frequency(self, guide_index):
109
+ for guide_id in EMBEDDED_GUIDE_IDS:
110
+ guide = guide_index.get(guide_id)
111
+ assert guide is not None, f"Guide '{guide_id}' not found"
112
+ assert guide.meta.frequency == "high", (
113
+ f"Embedded guide '{guide_id}' has frequency "
114
+ f"'{guide.meta.frequency}', expected 'high'"
115
+ )
116
+
117
+
118
+ class TestV010ReadmeGuideIds:
119
+ """V-010: README Quick start guide IDs reference existing guide files."""
120
+
121
+ def test_readme_guide_ids_exist(self, readme_text, guide_index):
122
+ ids_in_readme = re.findall(r"mpg retrieve\s+([a-z][a-z0-9-]+)", readme_text)
123
+ for guide_id in ids_in_readme:
124
+ assert guide_index.get(guide_id) is not None, (
125
+ f"README references guide '{guide_id}' which doesn't exist"
126
+ )
@@ -1,104 +0,0 @@
1
- ---
2
- name: modern-python-guidance
3
- description: Version-aware BAD/GOOD pattern guides for modern Python. Use when writing, reviewing, or refactoring Python code to avoid outdated patterns (e.g. typing.List → list, @validator → @field_validator, setup.py → pyproject.toml). Triggers on "Python", "modernize", "upgrade", "deprecated", "pydantic", "fastapi", "httpx", "typing", "dataclass", "asyncio".
4
- ---
5
-
6
- # Modern Python Guidance
7
-
8
- Version-aware BAD → GOOD pattern guides for modern Python (3.9–3.13+).
9
-
10
- When writing or reviewing Python code, consult these guides to ensure modern idioms are used instead of deprecated or outdated patterns. Each guide shows a concrete BAD example, the modern GOOD replacement, and explains why the change matters.
11
-
12
- ## When to use
13
-
14
- - Writing new Python code (use modern patterns from the start)
15
- - Reviewing Python code (flag outdated patterns)
16
- - Migrating from Pydantic V1 to V2
17
- - Upgrading Python version (check which new features are available)
18
- - Replacing legacy tooling (setup.py, flake8, pip)
19
-
20
- ## Guide inventory (30 guides)
21
-
22
- ### Layer 1 — Standard Library & Language Features
23
-
24
- | Category | Guide | Python | What it replaces |
25
- |----------|-------|--------|-----------------|
26
- | typing | `use-builtin-generics` | >=3.9 | `typing.List` → `list` |
27
- | typing | `union-syntax` | >=3.10 | `Optional[X]` → `X \| None` |
28
- | typing | `type-parameter-syntax` | >=3.12 | `TypeVar("T")` → `[T]` |
29
- | typing | `override-decorator` | >=3.12 | manual override → `@override` |
30
- | typing | `typeis-vs-typeguard` | >=3.13 | `TypeGuard` → `TypeIs` |
31
- | typing | `paramspec-decorators` | >=3.10 | untyped decorators → `ParamSpec` |
32
- | async | `taskgroup-over-gather` | >=3.11 | `gather()` → `TaskGroup` |
33
- | async | `exception-groups` | >=3.11 | multi-error handling → `except*` |
34
- | async | `async-timeout-context` | >=3.11 | `wait_for()` → `asyncio.timeout` |
35
- | stdlib | `datetime-utc` | >=3.11 | `utcnow()` → `now(UTC)` |
36
- | stdlib | `pathlib-over-os-path` | >=3.9 | `os.path` → `pathlib.Path` |
37
- | stdlib | `tomllib-builtin` | >=3.11 | `toml` package → `tomllib` |
38
- | stdlib | `removeprefix-removesuffix` | >=3.9 | `lstrip()`/slicing → `removeprefix()` |
39
- | data-structures | `dict-merge-operator` | >=3.9 | `{**d1, **d2}` → `d1 \| d2` |
40
- | data-structures | `match-case-patterns` | >=3.10 | nested if/isinstance → `match`/`case` |
41
- | data-structures | `dataclass-modern` | >=3.10 | basic dataclass → `slots=True, kw_only=True` |
42
-
43
- ### Layer 2 — Popular Frameworks
44
-
45
- | Category | Guide | What it replaces |
46
- |----------|-------|-----------------|
47
- | pydantic | `pydantic-v2-model-api` | `parse_obj()` → `model_validate()` |
48
- | pydantic | `pydantic-v2-validators` | `@validator` → `@field_validator` |
49
- | pydantic | `pydantic-v2-config` | `class Config` → `model_config = ConfigDict(...)` |
50
- | pydantic | `pydantic-v2-serialization` | `json_encoders` → `@field_serializer` |
51
- | fastapi | `fastapi-lifespan` | `@on_event` → lifespan context manager |
52
- | fastapi | `fastapi-annotated-depends` | `Depends()` default → `Annotated[T, Depends()]` |
53
- | fastapi | `fastapi-typed-state` | untyped `app.state` → typed state via lifespan |
54
- | httpx | `httpx-async-client-reuse` | per-request client → shared `AsyncClient` |
55
- | httpx | `httpx-streaming` | `response.content` → `client.stream()` |
56
-
57
- ### Layer 3 — Toolchain & Security
58
-
59
- | Category | Guide | What it replaces |
60
- |----------|-------|-----------------|
61
- | toolchain | `pyproject-toml-over-setup` | `setup.py` → `pyproject.toml` |
62
- | toolchain | `uv-over-pip` | `pip` → `uv` |
63
- | toolchain | `ruff-over-flake8` | flake8+isort+black → `ruff` |
64
- | toolchain | `no-pickle` | `pickle.load()` → safe alternatives |
65
- | toolchain | `safe-subprocess` | `shell=True` → list arguments |
66
-
67
- ## How to look up a guide
68
-
69
- Each guide is a markdown file in `guides/<category>/<id>.md` with YAML frontmatter containing:
70
- - `id`: unique identifier
71
- - `python`: minimum Python version (e.g. `">=3.11"`)
72
- - `frequency`: how often LLMs generate the outdated pattern (`high`/`medium`/`low`)
73
- - `layer`: 1 (stdlib), 2 (frameworks), 3 (toolchain)
74
-
75
- ### Reading a guide directly
76
-
77
- Open `guides/<category>/<guide-id>.md` for the full BAD/GOOD comparison.
78
-
79
- ### Using the CLI
80
-
81
- ```bash
82
- # Search by keyword
83
- mpg search "typing list"
84
-
85
- # Retrieve full guide content
86
- mpg retrieve use-builtin-generics
87
-
88
- # List all guides for a Python version
89
- mpg list --python-version 3.11
90
-
91
- # Detect project Python version
92
- mpg detect-version
93
- ```
94
-
95
- ## Integration pattern for agents
96
-
97
- When generating Python code:
98
-
99
- 1. Check if the target Python version is known (from `pyproject.toml`, `.python-version`, or context)
100
- 2. For each pattern you're about to write, check if a guide exists for a modern replacement
101
- 3. Use the GOOD pattern instead of the BAD pattern
102
- 4. If the target Python version is too old for the modern pattern, use the older pattern and note it
103
-
104
- Example: if writing `from typing import List` for a Python 3.9+ project, use `list` instead (see `use-builtin-generics`).