modern-python-guidance 0.4.3__tar.gz → 0.4.4__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.
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/.github/workflows/ci.yml +1 -1
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/CHANGELOG.md +8 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/PKG-INFO +4 -3
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/README.md +3 -2
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/docs/design.md +3 -4
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/pyproject.toml +1 -1
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/src/modern_python_guidance/__init__.py +1 -1
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/src/modern_python_guidance/version_detect.py +50 -15
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/tests/test_version_detect.py +86 -6
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/.github/workflows/check-python-release.yml +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/.github/workflows/publish.yml +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/.gitignore +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/CONTRIBUTING.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/LICENSE +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/LICENSE-MIT +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/SECURITY.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/fixtures/edge-cases/opus48_multiline.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/fixtures/edge-cases/valid_alt_patterns.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/fixtures/variant-a-modern/pyproject.toml +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/fixtures/variant-a-modern/src/app.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/fixtures/variant-a-modern/src/config.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/fixtures/variant-a-modern/src/crawler.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/fixtures/variant-a-modern/src/models.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/fixtures/variant-a-modern/src/scanner.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/fixtures/variant-a-modern/src/utils.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/fixtures/variant-a-outdated/pyproject.toml +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/fixtures/variant-a-outdated/setup.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/fixtures/variant-a-outdated/src/app.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/fixtures/variant-a-outdated/src/config.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/fixtures/variant-a-outdated/src/crawler.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/fixtures/variant-a-outdated/src/models.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/fixtures/variant-a-outdated/src/scanner.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/fixtures/variant-a-outdated/src/utils.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/fixtures/variant-b-modern/myapp/models.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/fixtures/variant-b-modern/myapp/views.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/fixtures/variant-b-outdated/myapp/models.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/fixtures/variant-b-outdated/myapp/views.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/fixtures/variant-c-modern/tests/test_calculator.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/fixtures/variant-c-outdated/tests/test_calculator.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/mcp-config.json +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/prompt-v2.txt +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/prompt-v3-mcp.txt +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/prompt-v3.txt +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/prompt-v4-a.txt +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/prompt-v4-b.txt +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/prompt-v4-c.txt +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/prompt.txt +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/prompts/v5-a-detailed.txt +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/prompts/v5-a-normal.txt +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/prompts/v5-a-terse.txt +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/prompts/v5-b-detailed.txt +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/prompts/v5-b-normal.txt +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/prompts/v5-b-terse.txt +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/prompts/v5-c-detailed.txt +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/prompts/v5-c-normal.txt +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/prompts/v5-c-terse.txt +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/run-mcp.sh +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/run-v4.sh +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/run-v5.sh +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/run.sh +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/score-v2.sh +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/score-v3.sh +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/score-v4.sh +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/score.sh +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/score_v5.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/test-scorer.sh +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/docs/benchmark-evaluation.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/docs/benchmark-procedure.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/docs/benchmark-v5.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/rules/modern-python.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/SKILL.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/async/async-timeout-context.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/async/exception-groups.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/async/taskgroup-over-gather.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/data-structures/dataclass-modern.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/data-structures/dict-merge-operator.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/data-structures/match-case-patterns.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/django/django-async-views.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/django/django-check-constraints.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/django/django-json-field.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/fastapi/fastapi-annotated-depends.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/fastapi/fastapi-lifespan.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/fastapi/fastapi-typed-state.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/httpx/httpx-async-client-reuse.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/httpx/httpx-streaming.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/pydantic/pydantic-v2-config.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/pydantic/pydantic-v2-model-api.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/pydantic/pydantic-v2-serialization.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/pydantic/pydantic-v2-validators.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/pytest/pytest-parametrize.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/pytest/pytest-raises-match.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/pytest/pytest-tmp-path.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/sqlalchemy/sqlalchemy-2-style.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/sqlalchemy/sqlalchemy-async-session.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/sqlalchemy/sqlalchemy-mapped-column.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/stdlib/datetime-utc.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/stdlib/pathlib-over-os-path.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/stdlib/removeprefix-removesuffix.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/stdlib/template-strings.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/stdlib/tomllib-builtin.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/toolchain/no-pickle.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/toolchain/pyproject-toml-over-setup.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/toolchain/ruff-over-flake8.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/toolchain/safe-subprocess.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/toolchain/uv-over-pip.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/typing/deferred-annotations.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/typing/override-decorator.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/typing/paramspec-decorators.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/typing/type-parameter-syntax.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/typing/typeis-vs-typeguard.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/typing/union-syntax.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/guides/typing/use-builtin-generics.md +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/src/modern_python_guidance/__main__.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/src/modern_python_guidance/check.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/src/modern_python_guidance/cli.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/src/modern_python_guidance/compat.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/src/modern_python_guidance/frontmatter.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/src/modern_python_guidance/guide_index.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/src/modern_python_guidance/mcp_server.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/src/modern_python_guidance/retrieve.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/src/modern_python_guidance/search.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/src/modern_python_guidance/setup_cmd.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/src/modern_python_guidance/uninstall_cmd.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/tests/test_check.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/tests/test_cli_integration.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/tests/test_cli_unit.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/tests/test_compat.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/tests/test_frontmatter.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/tests/test_guide_index.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/tests/test_guide_structure.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/tests/test_mcp_server.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/tests/test_mcp_unit.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/tests/test_retrieve.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/tests/test_scorer_v5.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/tests/test_search.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/tests/test_setup.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/tests/test_skill_sync.py +0 -0
- {modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/tests/test_uninstall.py +0 -0
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.4.4] — 2026-06-05
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Poetry constraint parsing: `detect_version()` now extracts the minimum Python version from `[tool.poetry.dependencies].python` instead of only logging a warning. Supported forms: caret (`^3.10`), tilde (`~3.11`), PEP 440 (`>=3.10,<3.14`), and dict-form (`{version = "^3.10"}`). Union operators (`||`) and unsupported formats warn and fall through to `.python-version` / default. (closes #95)
|
|
10
|
+
- Python 3.14 added to CI test matrix as a regular (non-allowed-failure) entry. Python 3.14 has been GA since 2025-10-07; pyproject.toml classifiers already declared support. (closes #94)
|
|
11
|
+
- 12 new tests (999 total).
|
|
12
|
+
|
|
5
13
|
## [0.4.3] — 2026-06-04
|
|
6
14
|
|
|
7
15
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: modern-python-guidance
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.4
|
|
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
|
|
@@ -166,8 +166,9 @@ Guides specify their minimum Python version. The CLI auto-detects your project's
|
|
|
166
166
|
|
|
167
167
|
1. `--python-version` flag
|
|
168
168
|
2. `pyproject.toml` `requires-python`
|
|
169
|
-
3.
|
|
170
|
-
4.
|
|
169
|
+
3. `pyproject.toml` Poetry `python` constraint (`^3.10`, `~3.11`, `>=3.10,<3.14`)
|
|
170
|
+
4. `.python-version` file
|
|
171
|
+
5. Default: 3.11
|
|
171
172
|
|
|
172
173
|
```bash
|
|
173
174
|
# Only shows guides compatible with Python 3.9
|
|
@@ -134,8 +134,9 @@ Guides specify their minimum Python version. The CLI auto-detects your project's
|
|
|
134
134
|
|
|
135
135
|
1. `--python-version` flag
|
|
136
136
|
2. `pyproject.toml` `requires-python`
|
|
137
|
-
3.
|
|
138
|
-
4.
|
|
137
|
+
3. `pyproject.toml` Poetry `python` constraint (`^3.10`, `~3.11`, `>=3.10,<3.14`)
|
|
138
|
+
4. `.python-version` file
|
|
139
|
+
5. Default: 3.11
|
|
139
140
|
|
|
140
141
|
```bash
|
|
141
142
|
# Only shows guides compatible with Python 3.9
|
|
@@ -150,10 +150,9 @@ Fuzzy results are marked with `fuzzy: true` in the output.
|
|
|
150
150
|
|
|
151
151
|
1. `--python-version` CLI flag (explicit override)
|
|
152
152
|
2. `pyproject.toml` `[project].requires-python` (PEP 621)
|
|
153
|
-
3.
|
|
154
|
-
4.
|
|
155
|
-
|
|
156
|
-
Poetry's caret syntax (`^3.11`) is detected but not parsed — the tool logs a warning and suggests using `--python-version` or adding `[project].requires-python`.
|
|
153
|
+
3. `pyproject.toml` `[tool.poetry.dependencies].python` — caret (`^3.10`), tilde (`~3.11`), and PEP 440 (`>=3.10,<3.14`) constraints are parsed to extract the minimum version. Dict-form (`{version = "^3.10"}`) is also supported. Union operators (`||`) are not supported and fall through with a warning.
|
|
154
|
+
4. `.python-version` file (pyenv/asdf convention)
|
|
155
|
+
5. Default: `3.11`
|
|
157
156
|
|
|
158
157
|
## Output format
|
|
159
158
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "modern-python-guidance"
|
|
7
|
-
version = "0.4.
|
|
7
|
+
version = "0.4.4"
|
|
8
8
|
description = "Version-aware BAD/GOOD pattern guides that help AI coding agents generate modern Python"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "Apache-2.0 OR MIT"
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
Precedence chain:
|
|
4
4
|
1. CLI --python-version flag (explicit override)
|
|
5
5
|
2. pyproject.toml [project].requires-python (PEP 621)
|
|
6
|
-
3. .python
|
|
7
|
-
4.
|
|
6
|
+
3. pyproject.toml [tool.poetry.dependencies].python (caret/tilde/PEP 440)
|
|
7
|
+
4. .python-version file (pyenv/asdf)
|
|
8
|
+
5. Default: 3.11
|
|
8
9
|
"""
|
|
9
10
|
|
|
10
11
|
from __future__ import annotations
|
|
@@ -24,6 +25,7 @@ DEFAULT_VERSION = "3.11"
|
|
|
24
25
|
_KNOWN_MINORS = [Version(f"3.{minor}") for minor in range(7, 20)]
|
|
25
26
|
|
|
26
27
|
_POETRY_CARET_RE = re.compile(r"\^(\d+\.\d+)")
|
|
28
|
+
_POETRY_TILDE_RE = re.compile(r"~(\d+\.\d+)")
|
|
27
29
|
|
|
28
30
|
|
|
29
31
|
def detect_version(
|
|
@@ -68,21 +70,54 @@ def _from_pyproject(path: Path) -> str | None:
|
|
|
68
70
|
|
|
69
71
|
poetry_python = data.get("tool", {}).get("poetry", {}).get("dependencies", {}).get("python")
|
|
70
72
|
if poetry_python:
|
|
71
|
-
|
|
72
|
-
if
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
73
|
+
result = _parse_poetry_python(poetry_python)
|
|
74
|
+
if result is not None:
|
|
75
|
+
return result
|
|
76
|
+
|
|
77
|
+
return None
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _parse_poetry_python(value: str | dict) -> str | None:
|
|
81
|
+
if isinstance(value, dict):
|
|
82
|
+
value = value.get("version")
|
|
83
|
+
if not value:
|
|
84
|
+
log.warning("Poetry python constraint has no 'version' key")
|
|
85
|
+
return None
|
|
86
|
+
|
|
87
|
+
if not isinstance(value, str):
|
|
88
|
+
log.warning("Poetry python constraint has unexpected type %s", type(value).__name__)
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
poetry_str = value
|
|
92
|
+
|
|
93
|
+
if "||" in poetry_str:
|
|
94
|
+
log.warning(
|
|
95
|
+
"Poetry union constraint '%s' is not supported. "
|
|
96
|
+
"Use --python-version or add [project].requires-python.",
|
|
97
|
+
poetry_str,
|
|
98
|
+
)
|
|
84
99
|
return None
|
|
85
100
|
|
|
101
|
+
m = _POETRY_CARET_RE.search(poetry_str)
|
|
102
|
+
if m:
|
|
103
|
+
log.info("Parsed Poetry caret constraint '%s' → %s", poetry_str, m.group(1))
|
|
104
|
+
return m.group(1)
|
|
105
|
+
|
|
106
|
+
m = _POETRY_TILDE_RE.search(poetry_str)
|
|
107
|
+
if m:
|
|
108
|
+
log.info("Parsed Poetry tilde constraint '%s' → %s", poetry_str, m.group(1))
|
|
109
|
+
return m.group(1)
|
|
110
|
+
|
|
111
|
+
result = _min_version_from_specifier(poetry_str)
|
|
112
|
+
if result is not None:
|
|
113
|
+
log.info("Parsed Poetry PEP 440 constraint '%s' → %s", poetry_str, result)
|
|
114
|
+
return result
|
|
115
|
+
|
|
116
|
+
log.warning(
|
|
117
|
+
"Poetry python constraint '%s' detected but not supported. "
|
|
118
|
+
"Use --python-version or add [project].requires-python.",
|
|
119
|
+
poetry_str,
|
|
120
|
+
)
|
|
86
121
|
return None
|
|
87
122
|
|
|
88
123
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import logging
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
|
|
5
6
|
import pytest
|
|
@@ -44,13 +45,87 @@ class TestPyprojectToml:
|
|
|
44
45
|
(tmp_project / "pyproject.toml").write_text('[project]\nrequires-python = ">=3.11.4"\n')
|
|
45
46
|
assert detect_version(project_dir=tmp_project) == "3.11"
|
|
46
47
|
|
|
47
|
-
def
|
|
48
|
+
def test_poetry_caret(self, tmp_project: Path, caplog):
|
|
48
49
|
(tmp_project / "pyproject.toml").write_text(
|
|
49
50
|
'[tool.poetry.dependencies]\npython = "^3.10"\n'
|
|
50
51
|
)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
assert "Poetry caret
|
|
52
|
+
with caplog.at_level(logging.INFO):
|
|
53
|
+
assert detect_version(project_dir=tmp_project) == "3.10"
|
|
54
|
+
assert "Parsed Poetry caret" in caplog.text
|
|
55
|
+
|
|
56
|
+
def test_poetry_caret_with_patch(self, tmp_project: Path):
|
|
57
|
+
(tmp_project / "pyproject.toml").write_text(
|
|
58
|
+
'[tool.poetry.dependencies]\npython = "^3.10.2"\n'
|
|
59
|
+
)
|
|
60
|
+
assert detect_version(project_dir=tmp_project) == "3.10"
|
|
61
|
+
|
|
62
|
+
def test_poetry_tilde(self, tmp_project: Path, caplog):
|
|
63
|
+
(tmp_project / "pyproject.toml").write_text(
|
|
64
|
+
'[tool.poetry.dependencies]\npython = "~3.11"\n'
|
|
65
|
+
)
|
|
66
|
+
with caplog.at_level(logging.INFO):
|
|
67
|
+
assert detect_version(project_dir=tmp_project) == "3.11"
|
|
68
|
+
assert "Parsed Poetry tilde" in caplog.text
|
|
69
|
+
|
|
70
|
+
def test_poetry_tilde_with_patch(self, tmp_project: Path):
|
|
71
|
+
(tmp_project / "pyproject.toml").write_text(
|
|
72
|
+
'[tool.poetry.dependencies]\npython = "~3.11.5"\n'
|
|
73
|
+
)
|
|
74
|
+
assert detect_version(project_dir=tmp_project) == "3.11"
|
|
75
|
+
|
|
76
|
+
def test_poetry_pep440_range(self, tmp_project: Path, caplog):
|
|
77
|
+
(tmp_project / "pyproject.toml").write_text(
|
|
78
|
+
'[tool.poetry.dependencies]\npython = ">=3.10,<3.14"\n'
|
|
79
|
+
)
|
|
80
|
+
with caplog.at_level(logging.INFO):
|
|
81
|
+
assert detect_version(project_dir=tmp_project) == "3.10"
|
|
82
|
+
assert "Parsed Poetry PEP 440" in caplog.text
|
|
83
|
+
|
|
84
|
+
def test_poetry_pep440_compatible(self, tmp_project: Path, caplog):
|
|
85
|
+
(tmp_project / "pyproject.toml").write_text(
|
|
86
|
+
'[tool.poetry.dependencies]\npython = "~=3.11"\n'
|
|
87
|
+
)
|
|
88
|
+
with caplog.at_level(logging.INFO):
|
|
89
|
+
assert detect_version(project_dir=tmp_project) == "3.11"
|
|
90
|
+
assert "Parsed Poetry PEP 440" in caplog.text
|
|
91
|
+
assert "Parsed Poetry tilde" not in caplog.text
|
|
92
|
+
|
|
93
|
+
def test_poetry_dict_form(self, tmp_project: Path):
|
|
94
|
+
(tmp_project / "pyproject.toml").write_text(
|
|
95
|
+
'[tool.poetry.dependencies.python]\nversion = "^3.10"\n'
|
|
96
|
+
)
|
|
97
|
+
assert detect_version(project_dir=tmp_project) == "3.10"
|
|
98
|
+
|
|
99
|
+
def test_poetry_dict_no_version_key(self, tmp_project: Path, caplog):
|
|
100
|
+
(tmp_project / "pyproject.toml").write_text(
|
|
101
|
+
"[tool.poetry.dependencies.python]\noptional = true\n"
|
|
102
|
+
)
|
|
103
|
+
assert detect_version(project_dir=tmp_project) == DEFAULT_VERSION
|
|
104
|
+
assert "no 'version' key" in caplog.text
|
|
105
|
+
|
|
106
|
+
def test_poetry_union_warns(self, tmp_project: Path, caplog):
|
|
107
|
+
(tmp_project / "pyproject.toml").write_text(
|
|
108
|
+
'[tool.poetry.dependencies]\npython = "^3.10 || ^3.12"\n'
|
|
109
|
+
)
|
|
110
|
+
assert detect_version(project_dir=tmp_project) == DEFAULT_VERSION
|
|
111
|
+
assert "union constraint" in caplog.text
|
|
112
|
+
|
|
113
|
+
def test_poetry_unsupported_warns(self, tmp_project: Path, caplog):
|
|
114
|
+
(tmp_project / "pyproject.toml").write_text('[tool.poetry.dependencies]\npython = "*"\n')
|
|
115
|
+
assert detect_version(project_dir=tmp_project) == DEFAULT_VERSION
|
|
116
|
+
assert "not supported" in caplog.text
|
|
117
|
+
|
|
118
|
+
def test_poetry_major_only_warns(self, tmp_project: Path, caplog):
|
|
119
|
+
(tmp_project / "pyproject.toml").write_text('[tool.poetry.dependencies]\npython = "^3"\n')
|
|
120
|
+
assert detect_version(project_dir=tmp_project) == DEFAULT_VERSION
|
|
121
|
+
assert "not supported" in caplog.text
|
|
122
|
+
|
|
123
|
+
def test_poetry_list_type_warns(self, tmp_project: Path, caplog):
|
|
124
|
+
(tmp_project / "pyproject.toml").write_text(
|
|
125
|
+
'[tool.poetry.dependencies]\npython = ["^3.10", "^3.12"]\n'
|
|
126
|
+
)
|
|
127
|
+
assert detect_version(project_dir=tmp_project) == DEFAULT_VERSION
|
|
128
|
+
assert "unexpected type" in caplog.text
|
|
54
129
|
|
|
55
130
|
def test_malformed_toml(self, tmp_project: Path, caplog):
|
|
56
131
|
(tmp_project / "pyproject.toml").write_text("this is not valid toml [[[")
|
|
@@ -91,12 +166,17 @@ class TestPrecedence:
|
|
|
91
166
|
(tmp_project / ".python-version").write_text("3.10\n")
|
|
92
167
|
assert detect_version(project_dir=tmp_project) == "3.12"
|
|
93
168
|
|
|
94
|
-
def
|
|
169
|
+
def test_poetry_wins_over_python_version_file(self, tmp_project: Path):
|
|
95
170
|
(tmp_project / "pyproject.toml").write_text(
|
|
96
171
|
'[tool.poetry.dependencies]\npython = "^3.10"\n'
|
|
97
172
|
)
|
|
98
173
|
(tmp_project / ".python-version").write_text("3.11\n")
|
|
99
|
-
assert detect_version(project_dir=tmp_project) == "3.
|
|
174
|
+
assert detect_version(project_dir=tmp_project) == "3.10"
|
|
175
|
+
|
|
176
|
+
def test_poetry_unsupported_falls_to_python_version_file(self, tmp_project: Path):
|
|
177
|
+
(tmp_project / "pyproject.toml").write_text('[tool.poetry.dependencies]\npython = "*"\n')
|
|
178
|
+
(tmp_project / ".python-version").write_text("3.12\n")
|
|
179
|
+
assert detect_version(project_dir=tmp_project) == "3.12"
|
|
100
180
|
|
|
101
181
|
|
|
102
182
|
class TestDefault:
|
|
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
|
|
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
|
{modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/prompts/v5-a-detailed.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/prompts/v5-b-detailed.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/bench/prompts/v5-c-detailed.txt
RENAMED
|
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
|
{modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/skills/modern-python-guidance/SKILL.md
RENAMED
|
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
|
|
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
|
{modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/src/modern_python_guidance/__main__.py
RENAMED
|
File without changes
|
{modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/src/modern_python_guidance/check.py
RENAMED
|
File without changes
|
{modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/src/modern_python_guidance/cli.py
RENAMED
|
File without changes
|
{modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/src/modern_python_guidance/compat.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/src/modern_python_guidance/retrieve.py
RENAMED
|
File without changes
|
{modern_python_guidance-0.4.3 → modern_python_guidance-0.4.4}/src/modern_python_guidance/search.py
RENAMED
|
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
|