modern-python-guidance 0.3.1__tar.gz → 0.3.3__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.3.3/.github/workflows/check-python-release.yml +93 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/CHANGELOG.md +31 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/CONTRIBUTING.md +1 -1
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/PKG-INFO +7 -7
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/README.md +6 -6
- modern_python_guidance-0.3.3/bench/fixtures/edge-cases/opus48_multiline.py +68 -0
- modern_python_guidance-0.3.3/bench/fixtures/edge-cases/valid_alt_patterns.py +41 -0
- modern_python_guidance-0.3.3/bench/prompts/v5-a-detailed.txt +15 -0
- modern_python_guidance-0.3.3/bench/prompts/v5-a-normal.txt +15 -0
- modern_python_guidance-0.3.3/bench/prompts/v5-a-terse.txt +1 -0
- modern_python_guidance-0.3.3/bench/prompts/v5-b-detailed.txt +7 -0
- modern_python_guidance-0.3.3/bench/prompts/v5-b-normal.txt +7 -0
- modern_python_guidance-0.3.3/bench/prompts/v5-b-terse.txt +1 -0
- modern_python_guidance-0.3.3/bench/prompts/v5-c-detailed.txt +3 -0
- modern_python_guidance-0.3.3/bench/prompts/v5-c-normal.txt +3 -0
- modern_python_guidance-0.3.3/bench/prompts/v5-c-terse.txt +1 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/run-v4.sh +12 -2
- modern_python_guidance-0.3.3/bench/run-v5.sh +274 -0
- modern_python_guidance-0.3.3/bench/score_v5.py +1504 -0
- modern_python_guidance-0.3.3/docs/benchmark-v5.md +107 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/docs/design.md +1 -1
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/pyproject.toml +4 -1
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/SKILL.md +3 -3
- modern_python_guidance-0.3.3/skills/modern-python-guidance/guides/stdlib/template-strings.md +70 -0
- modern_python_guidance-0.3.3/skills/modern-python-guidance/guides/typing/deferred-annotations.md +63 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/src/modern_python_guidance/__init__.py +1 -1
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/src/modern_python_guidance/mcp_server.py +4 -4
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/src/modern_python_guidance/setup_cmd.py +3 -0
- modern_python_guidance-0.3.3/tests/test_scorer_v5.py +992 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/tests/test_setup.py +16 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/tests/test_skill_sync.py +6 -6
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/.github/workflows/ci.yml +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/.github/workflows/publish.yml +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/.gitignore +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/LICENSE +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/LICENSE-MIT +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/SECURITY.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/fixtures/variant-a-modern/pyproject.toml +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/fixtures/variant-a-modern/src/app.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/fixtures/variant-a-modern/src/config.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/fixtures/variant-a-modern/src/crawler.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/fixtures/variant-a-modern/src/models.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/fixtures/variant-a-modern/src/scanner.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/fixtures/variant-a-modern/src/utils.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/fixtures/variant-a-outdated/pyproject.toml +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/fixtures/variant-a-outdated/setup.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/fixtures/variant-a-outdated/src/app.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/fixtures/variant-a-outdated/src/config.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/fixtures/variant-a-outdated/src/crawler.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/fixtures/variant-a-outdated/src/models.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/fixtures/variant-a-outdated/src/scanner.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/fixtures/variant-a-outdated/src/utils.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/fixtures/variant-b-modern/myapp/models.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/fixtures/variant-b-modern/myapp/views.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/fixtures/variant-b-outdated/myapp/models.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/fixtures/variant-b-outdated/myapp/views.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/fixtures/variant-c-modern/tests/test_calculator.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/fixtures/variant-c-outdated/tests/test_calculator.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/mcp-config.json +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/prompt-v2.txt +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/prompt-v3-mcp.txt +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/prompt-v3.txt +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/prompt-v4-a.txt +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/prompt-v4-b.txt +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/prompt-v4-c.txt +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/prompt.txt +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/run-mcp.sh +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/run.sh +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/score-v2.sh +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/score-v3.sh +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/score-v4.sh +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/score.sh +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/bench/test-scorer.sh +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/docs/benchmark-evaluation.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/docs/benchmark-procedure.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/async/async-timeout-context.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/async/exception-groups.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/async/taskgroup-over-gather.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/data-structures/dataclass-modern.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/data-structures/dict-merge-operator.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/data-structures/match-case-patterns.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/django/django-async-views.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/django/django-check-constraints.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/django/django-json-field.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/fastapi/fastapi-annotated-depends.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/fastapi/fastapi-lifespan.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/fastapi/fastapi-typed-state.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/httpx/httpx-async-client-reuse.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/httpx/httpx-streaming.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/pydantic/pydantic-v2-config.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/pydantic/pydantic-v2-model-api.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/pydantic/pydantic-v2-serialization.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/pydantic/pydantic-v2-validators.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/pytest/pytest-parametrize.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/pytest/pytest-raises-match.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/pytest/pytest-tmp-path.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/sqlalchemy/sqlalchemy-2-style.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/sqlalchemy/sqlalchemy-async-session.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/sqlalchemy/sqlalchemy-mapped-column.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/stdlib/datetime-utc.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/stdlib/pathlib-over-os-path.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/stdlib/removeprefix-removesuffix.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/stdlib/tomllib-builtin.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/toolchain/no-pickle.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/toolchain/pyproject-toml-over-setup.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/toolchain/ruff-over-flake8.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/toolchain/safe-subprocess.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/toolchain/uv-over-pip.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/typing/override-decorator.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/typing/paramspec-decorators.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/typing/type-parameter-syntax.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/typing/typeis-vs-typeguard.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/typing/union-syntax.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/skills/modern-python-guidance/guides/typing/use-builtin-generics.md +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/src/modern_python_guidance/__main__.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/src/modern_python_guidance/cli.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/src/modern_python_guidance/compat.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/src/modern_python_guidance/frontmatter.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/src/modern_python_guidance/guide_index.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/src/modern_python_guidance/retrieve.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/src/modern_python_guidance/search.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/src/modern_python_guidance/uninstall_cmd.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/src/modern_python_guidance/version_detect.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/tests/test_cli_integration.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/tests/test_frontmatter.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/tests/test_mcp_server.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/tests/test_retrieve.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/tests/test_search.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/tests/test_uninstall.py +0 -0
- {modern_python_guidance-0.3.1 → modern_python_guidance-0.3.3}/tests/test_version_detect.py +0 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
name: Check for new Python releases
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
schedule:
|
|
5
|
+
- cron: '0 9 * * 1' # Every Monday at 09:00 UTC
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
issues: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
check:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Detect latest stable Python minor version
|
|
18
|
+
id: detect
|
|
19
|
+
run: |
|
|
20
|
+
# Fetch all releases from python.org, filter stable only via pre_release field
|
|
21
|
+
LATEST=$(curl -s 'https://www.python.org/api/v2/downloads/release/?limit=500' \
|
|
22
|
+
| jq -r '[.[] | select(.pre_release == false) | .name
|
|
23
|
+
| capture("Python (?<v>3\\.[0-9]+)\\.[0-9]+$") | .v]
|
|
24
|
+
| unique | sort_by(split(".") | map(tonumber)) | last')
|
|
25
|
+
|
|
26
|
+
echo "latest_minor=$LATEST"
|
|
27
|
+
|
|
28
|
+
# Find the highest minor version covered in our guides
|
|
29
|
+
KNOWN=$(grep -roh 'python: ">=3\.[0-9]*"' skills/modern-python-guidance/guides/ \
|
|
30
|
+
| grep -oP '3\.\d+' \
|
|
31
|
+
| sort -V | tail -1)
|
|
32
|
+
|
|
33
|
+
echo "known_minor=$KNOWN"
|
|
34
|
+
|
|
35
|
+
if [ -z "$LATEST" ] || [ -z "$KNOWN" ]; then
|
|
36
|
+
echo "skip=true" >> "$GITHUB_OUTPUT"
|
|
37
|
+
echo "Could not determine versions (latest=$LATEST, known=$KNOWN)"
|
|
38
|
+
exit 0
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
LATEST_NUM=${LATEST##3.}
|
|
42
|
+
KNOWN_NUM=${KNOWN##3.}
|
|
43
|
+
|
|
44
|
+
if [ "$LATEST_NUM" -gt "$KNOWN_NUM" ]; then
|
|
45
|
+
echo "new_version=$LATEST" >> "$GITHUB_OUTPUT"
|
|
46
|
+
echo "skip=false" >> "$GITHUB_OUTPUT"
|
|
47
|
+
echo "New Python version detected: $LATEST (guides cover up to $KNOWN)"
|
|
48
|
+
else
|
|
49
|
+
echo "skip=true" >> "$GITHUB_OUTPUT"
|
|
50
|
+
echo "Up to date: guides cover $KNOWN, latest stable is $LATEST"
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
- name: Check for existing issue
|
|
54
|
+
if: steps.detect.outputs.skip != 'true'
|
|
55
|
+
id: existing
|
|
56
|
+
env:
|
|
57
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
58
|
+
run: |
|
|
59
|
+
VERSION="${{ steps.detect.outputs.new_version }}"
|
|
60
|
+
FOUND=$(gh issue list --search "\"Add Python ${VERSION} guides\" in:title" \
|
|
61
|
+
--state all --json number --jq '.[0].number // empty')
|
|
62
|
+
if [ -n "$FOUND" ]; then
|
|
63
|
+
echo "exists=true" >> "$GITHUB_OUTPUT"
|
|
64
|
+
echo "Issue #${FOUND} already exists"
|
|
65
|
+
else
|
|
66
|
+
echo "exists=false" >> "$GITHUB_OUTPUT"
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
- name: Create issue
|
|
70
|
+
if: steps.detect.outputs.skip != 'true' && steps.existing.outputs.exists != 'true'
|
|
71
|
+
env:
|
|
72
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
73
|
+
VERSION: ${{ steps.detect.outputs.new_version }}
|
|
74
|
+
run: |
|
|
75
|
+
SLUG="${VERSION//.}"
|
|
76
|
+
gh issue create \
|
|
77
|
+
--title "Add Python ${VERSION} guides" \
|
|
78
|
+
--label "enhancement" \
|
|
79
|
+
--body "## Python ${VERSION} stable released
|
|
80
|
+
|
|
81
|
+
Evaluate new PEPs for BAD/GOOD pattern guides.
|
|
82
|
+
|
|
83
|
+
### References
|
|
84
|
+
- [What's New in Python ${VERSION}](https://docs.python.org/${VERSION}/whatsnew/${VERSION}.html)
|
|
85
|
+
- [Python ${VERSION}.0 Release](https://www.python.org/downloads/release/python-${SLUG}0/)
|
|
86
|
+
|
|
87
|
+
### Checklist
|
|
88
|
+
- [ ] Review What's New for pattern-worthy changes
|
|
89
|
+
- [ ] Check candidates against quality bar (modern + meaningful)
|
|
90
|
+
- [ ] Write guides for accepted patterns
|
|
91
|
+
- [ ] Update SKILL.md embedded patterns if high-frequency
|
|
92
|
+
- [ ] Update benchmark scorer if new items added
|
|
93
|
+
- [ ] Release new version"
|
|
@@ -2,6 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.3.3] — 2026-05-30
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- AST-based benchmark scorer (`bench/score_v5.py`): replaces grep-based V4 scorer with Python AST detection for structurally correct pattern matching — fixes 3 false-flag bugs on Opus 4.8 output (multiline code, docstring keywords, .venv contamination) (closes #59)
|
|
10
|
+
- VALID_ALT classification for SA2 (sync SQLAlchemy 2.0), TY6 (TypeGuard), AS3 (per-task except) — tracks valid alternatives separately from recommended patterns
|
|
11
|
+
- Benchmark prompt granularity testing (terse/normal/detailed) with V5 runner using isolated tmpdir for workspace safety
|
|
12
|
+
- V5 benchmark results on Opus 4.8: terse prompts +19pp, normal prompts +7pp strict modern rate ([details](docs/benchmark-v5.md))
|
|
13
|
+
- 83 new scorer tests (fixture parity, per-item golden tests, edge cases, import alias handling)
|
|
14
|
+
- Weekly GitHub Actions workflow to detect new Python stable releases and auto-create tracking issues (closes #70)
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- README benchmark highlight updated from V4 (+14.7pp) to V5 (79% → 98% on vague prompts, Opus 4.8)
|
|
19
|
+
- Ruff config: added per-file-ignores for `bench/*.py` (SIM102/SIM110)
|
|
20
|
+
|
|
21
|
+
## [0.3.2] — 2026-05-29
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- `deferred-annotations` guide (PEP 649): drop unnecessary `from __future__ import annotations` on Python 3.14+ projects where annotations are lazily evaluated by default (closes #28)
|
|
26
|
+
- `template-strings` guide (PEP 750): use t-strings with processing functions for safe SQL/HTML parameterization instead of f-string interpolation (closes #28)
|
|
27
|
+
- Guide count: 39 → 41. Layer 1 coverage: 16 → 18
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- `setup_mcp` now catches `OSError` from `subprocess.run`, matching `uninstall_mcp` behavior — an unexecutable `claude` binary produces a clean error message instead of a traceback (closes #65)
|
|
32
|
+
- MCP `retrieve_guides` schema `maxItems` and runtime guard updated from 39 to 41 to allow retrieval of all guides
|
|
33
|
+
|
|
5
34
|
## [0.3.1] — 2026-05-29
|
|
6
35
|
|
|
7
36
|
### Added
|
|
@@ -117,6 +146,8 @@ Initial release.
|
|
|
117
146
|
- Strict YAML-subset frontmatter parser (no PyYAML dependency)
|
|
118
147
|
- GitHub Actions CI (pytest + ruff on Python 3.11, 3.12, 3.13)
|
|
119
148
|
|
|
149
|
+
[0.3.2]: https://github.com/yottayoshida/modern-python-guidance/releases/tag/v0.3.2
|
|
150
|
+
[0.3.1]: https://github.com/yottayoshida/modern-python-guidance/releases/tag/v0.3.1
|
|
120
151
|
[0.3.0]: https://github.com/yottayoshida/modern-python-guidance/releases/tag/v0.3.0
|
|
121
152
|
[0.2.3]: https://github.com/yottayoshida/modern-python-guidance/releases/tag/v0.2.3
|
|
122
153
|
[0.2.2]: https://github.com/yottayoshida/modern-python-guidance/releases/tag/v0.2.2
|
|
@@ -15,7 +15,7 @@ src/modern_python_guidance/
|
|
|
15
15
|
|
|
16
16
|
skills/modern-python-guidance/
|
|
17
17
|
├── SKILL.md # Agent Skills plugin entry point
|
|
18
|
-
└── guides/ #
|
|
18
|
+
└── guides/ # 41 guide files by category
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
See [docs/design.md](docs/design.md) for the full design document.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: modern-python-guidance
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
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
|
|
@@ -36,12 +36,12 @@ Description-Content-Type: text/markdown
|
|
|
36
36
|
[](https://pypi.org/project/modern-python-guidance/)
|
|
37
37
|
[](LICENSE)
|
|
38
38
|
|
|
39
|
-
Stop your AI from writing `typing.List`, `@validator`, and `setup.py`.
|
|
39
|
+
Stop your AI from writing `typing.List`, `@validator`, and `setup.py`. 41 version-aware BAD/GOOD pattern guides that teach AI coding agents to write modern Python — delivered via MCP, CLI, or Agent Skills.
|
|
40
40
|
|
|
41
41
|
## Highlights
|
|
42
42
|
|
|
43
|
-
- **Measurable impact**:
|
|
44
|
-
- **
|
|
43
|
+
- **Measurable impact**: AI writes modern Python 98% of the time with mpg, vs 79% without — even with vague prompts (Opus 4.8, [V5 benchmark details](docs/benchmark-v5.md))
|
|
44
|
+
- **41 guides** across stdlib, Pydantic, FastAPI, Django, SQLAlchemy, pytest, and toolchain
|
|
45
45
|
- **Version-aware**: auto-detects your project's Python version and filters guides accordingly
|
|
46
46
|
- **3 delivery methods**: MCP server, CLI, Agent Skills plugin
|
|
47
47
|
- **Not Ruff**: Ruff auto-fixes syntax (`List` → `list`). mpg guides design decisions that Ruff can't touch — `TaskGroup` over `gather`, Pydantic V2 migration, SQLAlchemy 2.0 style
|
|
@@ -144,15 +144,15 @@ mpg search "typing" --format json | jq '.[0].id'
|
|
|
144
144
|
|
|
145
145
|
## Guide coverage
|
|
146
146
|
|
|
147
|
-
|
|
147
|
+
41 guides across 3 layers:
|
|
148
148
|
|
|
149
149
|
| Layer | Categories | Count | Examples |
|
|
150
150
|
|-------|-----------|-------|---------|
|
|
151
|
-
| **1 — stdlib** | typing, async, stdlib, data-structures |
|
|
151
|
+
| **1 — stdlib** | typing, async, stdlib, data-structures | 18 | `list` over `List`, `match`/`case`, `TaskGroup`, deferred annotations, t-strings |
|
|
152
152
|
| **2 — frameworks** | pydantic, fastapi, httpx, django, sqlalchemy, pytest | 18 | Pydantic V2 migration, SQLAlchemy 2.0 style, `Annotated[Depends]` |
|
|
153
153
|
| **3 — toolchain** | toolchain | 5 | `uv` over `pip`, `ruff` over flake8, `pickle` avoidance |
|
|
154
154
|
|
|
155
|
-
Run `mpg list` to see all
|
|
155
|
+
Run `mpg list` to see all 41 guides, or [browse them on GitHub](skills/modern-python-guidance/guides/).
|
|
156
156
|
|
|
157
157
|
## Version-aware filtering
|
|
158
158
|
|
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
[](https://pypi.org/project/modern-python-guidance/)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
|
|
8
|
-
Stop your AI from writing `typing.List`, `@validator`, and `setup.py`.
|
|
8
|
+
Stop your AI from writing `typing.List`, `@validator`, and `setup.py`. 41 version-aware BAD/GOOD pattern guides that teach AI coding agents to write modern Python — delivered via MCP, CLI, or Agent Skills.
|
|
9
9
|
|
|
10
10
|
## Highlights
|
|
11
11
|
|
|
12
|
-
- **Measurable impact**:
|
|
13
|
-
- **
|
|
12
|
+
- **Measurable impact**: AI writes modern Python 98% of the time with mpg, vs 79% without — even with vague prompts (Opus 4.8, [V5 benchmark details](docs/benchmark-v5.md))
|
|
13
|
+
- **41 guides** across stdlib, Pydantic, FastAPI, Django, SQLAlchemy, pytest, and toolchain
|
|
14
14
|
- **Version-aware**: auto-detects your project's Python version and filters guides accordingly
|
|
15
15
|
- **3 delivery methods**: MCP server, CLI, Agent Skills plugin
|
|
16
16
|
- **Not Ruff**: Ruff auto-fixes syntax (`List` → `list`). mpg guides design decisions that Ruff can't touch — `TaskGroup` over `gather`, Pydantic V2 migration, SQLAlchemy 2.0 style
|
|
@@ -113,15 +113,15 @@ mpg search "typing" --format json | jq '.[0].id'
|
|
|
113
113
|
|
|
114
114
|
## Guide coverage
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
41 guides across 3 layers:
|
|
117
117
|
|
|
118
118
|
| Layer | Categories | Count | Examples |
|
|
119
119
|
|-------|-----------|-------|---------|
|
|
120
|
-
| **1 — stdlib** | typing, async, stdlib, data-structures |
|
|
120
|
+
| **1 — stdlib** | typing, async, stdlib, data-structures | 18 | `list` over `List`, `match`/`case`, `TaskGroup`, deferred annotations, t-strings |
|
|
121
121
|
| **2 — frameworks** | pydantic, fastapi, httpx, django, sqlalchemy, pytest | 18 | Pydantic V2 migration, SQLAlchemy 2.0 style, `Annotated[Depends]` |
|
|
122
122
|
| **3 — toolchain** | toolchain | 5 | `uv` over `pip`, `ruff` over flake8, `pickle` avoidance |
|
|
123
123
|
|
|
124
|
-
Run `mpg list` to see all
|
|
124
|
+
Run `mpg list` to see all 41 guides, or [browse them on GitHub](skills/modern-python-guidance/guides/).
|
|
125
125
|
|
|
126
126
|
## Version-aware filtering
|
|
127
127
|
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""Opus 4.8 style: verbose, multi-line, heavily documented code.
|
|
2
|
+
|
|
3
|
+
This fixture reproduces the patterns that broke the V4 grep scorer.
|
|
4
|
+
Every function here is MODERN but would false-flag under grep-based detection.
|
|
5
|
+
"""
|
|
6
|
+
import asyncio
|
|
7
|
+
import subprocess
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import TypeGuard
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def run_command(cmd: str, *args: str) -> subprocess.CompletedProcess[str]:
|
|
13
|
+
"""Run a subprocess safely from a list of arguments.
|
|
14
|
+
|
|
15
|
+
Passing a list (not a shell string) avoids shell injection: the program name
|
|
16
|
+
and each argument are kept distinct and never re-parsed by a shell.
|
|
17
|
+
Never use shell=True on untrusted input.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
return subprocess.run(
|
|
21
|
+
[cmd, *args],
|
|
22
|
+
check=True,
|
|
23
|
+
capture_output=True,
|
|
24
|
+
text=True,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
async def scan_directory(root: str | Path) -> list[Path]:
|
|
29
|
+
"""Walk root recursively, batch files, process concurrently.
|
|
30
|
+
|
|
31
|
+
Uses TaskGroup for structured concurrency. A failure in one batch
|
|
32
|
+
is recorded while the other batches still complete.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
root_path = Path(root)
|
|
36
|
+
files = [p for p in root_path.rglob("*") if p.is_file()]
|
|
37
|
+
batches = [files[i : i + 10] for i in range(0, len(files), 10)]
|
|
38
|
+
|
|
39
|
+
errors: list[str] = []
|
|
40
|
+
results: list[Path] = []
|
|
41
|
+
|
|
42
|
+
async with asyncio.TaskGroup() as tg:
|
|
43
|
+
for batch in batches:
|
|
44
|
+
tg.create_task(_process_batch(batch, results, errors))
|
|
45
|
+
|
|
46
|
+
return results
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
async def _process_batch(
|
|
50
|
+
batch: list[Path],
|
|
51
|
+
results: list[Path],
|
|
52
|
+
errors: list[str],
|
|
53
|
+
) -> None:
|
|
54
|
+
try:
|
|
55
|
+
for path in batch:
|
|
56
|
+
results.append(path)
|
|
57
|
+
except OSError as exc:
|
|
58
|
+
errors.append(str(exc))
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def is_positive_int(val: object) -> TypeGuard[int]:
|
|
62
|
+
"""Narrow val to int when it is a positive integer.
|
|
63
|
+
|
|
64
|
+
bool is a subclass of int in Python, so it is explicitly excluded.
|
|
65
|
+
After if is_positive_int(x): a type checker treats x as int.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
return isinstance(val, int) and not isinstance(val, bool) and val > 0
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Valid alternative patterns that should score VALID_ALT, not OUTDATED.
|
|
2
|
+
|
|
3
|
+
SA2: sync SQLAlchemy 2.0 (create_engine + select() style)
|
|
4
|
+
TY6: TypeGuard (broader semantics than TypeIs, still valid)
|
|
5
|
+
AS3: TaskGroup + per-task try/except (structured concurrency without except*)
|
|
6
|
+
"""
|
|
7
|
+
import asyncio
|
|
8
|
+
|
|
9
|
+
from sqlalchemy import create_engine, select
|
|
10
|
+
from sqlalchemy.orm import Session
|
|
11
|
+
from typing import TypeGuard
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# SA2: sync SQLAlchemy 2.0 — VALID_ALT
|
|
15
|
+
engine = create_engine("sqlite:///test.db")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_users():
|
|
19
|
+
with Session(engine) as session:
|
|
20
|
+
return session.scalars(select(User)).all()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# TY6: TypeGuard — VALID_ALT
|
|
24
|
+
def is_str_list(val: list[object]) -> TypeGuard[list[str]]:
|
|
25
|
+
return all(isinstance(x, str) for x in val)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# AS3: TaskGroup + per-task try/except — VALID_ALT
|
|
29
|
+
async def fetch_all(urls: list[str]) -> list[str]:
|
|
30
|
+
results: list[str] = []
|
|
31
|
+
async with asyncio.TaskGroup() as tg:
|
|
32
|
+
for url in urls:
|
|
33
|
+
tg.create_task(_safe_fetch(url, results))
|
|
34
|
+
return results
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
async def _safe_fetch(url: str, results: list[str]) -> None:
|
|
38
|
+
try:
|
|
39
|
+
results.append(f"fetched: {url}")
|
|
40
|
+
except Exception:
|
|
41
|
+
pass
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Write the following 7 files. Write all code, no placeholders. Create each file at the EXACT path shown below (relative to the current working directory). Do NOT create any project directories or subdirectories beyond what is listed.
|
|
2
|
+
|
|
3
|
+
1. src/config.py — A typed configuration loader. Define a Settings class using Pydantic to hold app configuration (database URL, debug flag, log level, allowed origins list, and an optional description that may or may not be provided). Use `model_config = ConfigDict(...)` for Pydantic configuration (not the inner `class Config:` pattern). Validate that the database URL starts with a known scheme and that log level is one of DEBUG/INFO/WARNING/ERROR — use `@field_validator` (not the deprecated `@validator`). Write a function `load_config(path)` that reads a TOML file using `tomllib` (not third-party `toml` or `tomli`) and returns a Settings instance via `Settings.model_validate()` (not `.parse_obj()`). Include a `created_at` field that defaults to the current time in UTC using `datetime.now(UTC)` (not `.utcnow()`). Define a generic container class `Registry[T]` using PEP 695 type parameter syntax `class Registry[T]:` (not `TypeVar`). Write a decorator `with_retry(max_attempts)` that retries a decorated async function on failure, preserving the signature with `ParamSpec`.
|
|
4
|
+
|
|
5
|
+
2. src/models.py — SQLAlchemy ORM models using 2.0-style declarative mapping. Define a `User` model and an `Article` model. Use `Mapped[type] = mapped_column()` for column definitions (not `Column(Type)`). Use `select()` for queries (not `.query()`). Define a base class with common fields (id, created_at) using `Mapped` annotations. Each model should `@override` its `__repr__` method.
|
|
6
|
+
|
|
7
|
+
3. src/app.py — A FastAPI application. Use `@asynccontextmanager` lifespan (not `@app.on_event`). Use `Annotated[Session, Depends(get_db)]` for dependency injection (not bare `= Depends()`). Use lifespan `yield` dict for typed application state (not `app.state`). Define request/response schemas with Pydantic V2 API — use `@field_serializer` for custom serialization (not `json_encoders`), and `.model_dump()` / `.model_dump_json()` for output (not `.dict()` / `.json()`). Use `create_async_engine` and `AsyncSession` for the database connection.
|
|
8
|
+
|
|
9
|
+
4. src/crawler.py — An async web crawler. Write a function `crawl(urls)` that fetches a list of URLs concurrently using `httpx.AsyncClient` as a shared context manager (not per-request `httpx.get()`). Use `asyncio.TaskGroup` for structured concurrency (not `asyncio.gather`). Apply `asyncio.timeout()` to each fetch (not `asyncio.wait_for`). Handle `ExceptionGroup` with `except*` syntax. Write a `stream_large(url)` function using `client.stream()` and `resp.aiter_bytes()`.
|
|
10
|
+
|
|
11
|
+
5. src/scanner.py — A file scanner and log parser. Use `pathlib.Path` for all filesystem operations (not `os.path`). Categorize files using `match`/`case` structural pattern matching (not `if isinstance` chains). Use a frozen dataclass with `@dataclass(frozen=True, slots=True)` for `ScanResult`. Process batches concurrently with `asyncio.TaskGroup`. Parse log lines with `.removeprefix()` / `.removesuffix()` (not `.lstrip()` / string slicing with `[len():]`).
|
|
12
|
+
|
|
13
|
+
6. src/utils.py — Utility functions. (a) `merge_defaults(user_config, default_config)` using the dict `|` merge operator (not `{**a, **b}` or `.update()`). (b) `run_command(cmd, *args)` using `subprocess.run([cmd, *args], check=True)` with a list (not `shell=True` or `os.system`). (c) `is_positive_int(val)` using `TypeIs[int]` (not `TypeGuard`). (d) `save_to_json(data, path)` using `pathlib.Path` for file output. Use `X | None` union syntax (not `Optional[X]`) and `list[str]` built-in generics (not `typing.List[str]`).
|
|
14
|
+
|
|
15
|
+
7. pyproject.toml — Project config with dependencies on fastapi, sqlalchemy, httpx, uvicorn, and pydantic. Target Python 3.12+. Use `[project]` table (not setup.py). Configure `[tool.ruff]` for linting (not flake8/black/isort). Include `uv` as the package manager in scripts.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Write the following 7 files. Write all code, no placeholders. Create each file at the EXACT path shown below (relative to the current working directory). Do NOT create any project directories or subdirectories beyond what is listed.
|
|
2
|
+
|
|
3
|
+
1. src/config.py — A typed configuration loader. Define a Settings class using Pydantic to hold app configuration (database URL, debug flag, log level, allowed origins list, and an optional description that may or may not be provided). Validate that the database URL starts with a known scheme and that log level is one of DEBUG/INFO/WARNING/ERROR. Write a function `load_config(path)` that reads a TOML file and returns a Settings instance. Include a `created_at` field that defaults to the current time in UTC. Define a generic container class `Registry[T]` that stores items by name and retrieves them with type safety. Write a decorator `with_retry(max_attempts)` that retries a decorated async function on failure.
|
|
4
|
+
|
|
5
|
+
2. src/models.py — SQLAlchemy ORM models. Define a `User` model and an `Article` model. User has fields: id, email, display_name, created_at. Article has fields: id, title, body, author_id (foreign key to User), published_at. Use the declarative mapping style. Define a base class with common fields (id, created_at) and a `__repr__` method that both models inherit from. Each model should override `__repr__` to include its own specific fields.
|
|
6
|
+
|
|
7
|
+
3. src/app.py — A FastAPI application. It should have: a User model with CRUD endpoints (GET /users, GET /users/{id}, POST /users), an Article endpoint (GET /articles), proper database lifecycle management with startup/shutdown, dependency injection for the database session, and typed application state that holds the database engine. Define request/response schemas with serialization aliases (e.g., snake_case fields exposed as camelCase in JSON).
|
|
8
|
+
|
|
9
|
+
4. src/crawler.py — An async web crawler. Write a function `crawl(urls)` that fetches a list of URLs concurrently using httpx and returns their response bodies. Use an async context manager for the HTTP client to reuse connections. Handle failures gracefully — a single bad URL should not lose the other results. Use structured concurrency for the concurrent fetches with proper cancellation. Write a second function `stream_large(url)` that downloads a large response body by reading it in chunks rather than loading it all into memory at once. Apply a timeout to each individual fetch operation.
|
|
10
|
+
|
|
11
|
+
5. src/scanner.py — A file scanner and log parser. Write a function `scan_directory(root)` that walks a directory tree recursively, collects all files, groups them into batches of 10, and processes each batch concurrently with proper error handling — if one batch fails, the others should still complete. Define an enum `FileCategory` with values IMAGE, VIDEO, DOCUMENT, OTHER. Categorize each file by its extension (.jpg/.png → IMAGE, .mp4/.avi → VIDEO, .pdf/.docx → DOCUMENT, everything else → OTHER) using structured pattern matching. Define a frozen data container `ScanResult` to hold the results (total count, categorized file lists, errors). Write a function `parse_log_lines(lines)` that strips a known prefix from each log line.
|
|
12
|
+
|
|
13
|
+
6. src/utils.py — Utility functions. Write: (a) a function `merge_defaults(user_config, default_config)` that merges two dicts with user values taking precedence, (b) a function `run_command(cmd, *args)` that runs a subprocess safely with a list of arguments, (c) a function `is_positive_int(val)` that narrows an unknown value to int via a type narrowing guard, (d) a function `save_to_json(data, path)` that serializes data to a JSON file.
|
|
14
|
+
|
|
15
|
+
7. pyproject.toml — Project config with dependencies on fastapi, sqlalchemy, httpx, uvicorn, and pydantic. Target Python 3.12+. Configure a linter and formatter.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Build a FastAPI web application with an async web crawler. Use SQLAlchemy for the database, httpx for HTTP requests, Pydantic for data validation, and TOML for configuration. Include a file scanner utility with pattern matching. Target Python 3.12+. Write all code with no placeholders.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Write the following 3 files for a Django application. Write all code, no placeholders. Create each file at the EXACT path shown below (relative to the current working directory). Do NOT create any project directories or subdirectories beyond what is listed.
|
|
2
|
+
|
|
3
|
+
1. myapp/models.py — Django models. Define a `Product` model with fields: name (CharField), price (DecimalField), metadata (use `models.JSONField` — the native Django JSONField, not the old `django.contrib.postgres.fields.JSONField`). Add a database-level constraint using `models.CheckConstraint(condition=..., name=...)` syntax (not the deprecated `check=` parameter) that ensures price is non-negative.
|
|
4
|
+
|
|
5
|
+
2. myapp/views.py — Django views. Write an `async def product_list(request)` view that returns all products as JSON using native async Django ORM queries (`async for p in Product.objects.all()`, `.aget()`, `.afirst()`) — do NOT use `sync_to_async` wrappers. Write a second async view `product_detail(request, pk)` that returns a single product using `.aget()`.
|
|
6
|
+
|
|
7
|
+
3. myapp/urls.py — URL configuration. Wire up the two views above with appropriate URL patterns.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Write the following 3 files for a Django application. Write all code, no placeholders. Create each file at the EXACT path shown below (relative to the current working directory). Do NOT create any project directories or subdirectories beyond what is listed.
|
|
2
|
+
|
|
3
|
+
1. myapp/models.py — Django models. Define a `Product` model with fields: name (CharField), price (DecimalField), metadata (a field that stores arbitrary JSON data natively). Add a database-level constraint that ensures price is non-negative.
|
|
4
|
+
|
|
5
|
+
2. myapp/views.py — Django views. Write a view function `product_list(request)` that returns all products as JSON. Make it handle requests asynchronously. Write a second view `product_detail(request, pk)` that returns a single product.
|
|
6
|
+
|
|
7
|
+
3. myapp/urls.py — URL configuration. Wire up the two views above with appropriate URL patterns.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Build a Django application with a Product model that stores JSON data and has a price constraint. Include async views that return JSON responses. Target Python 3.12+. Write all code with no placeholders.
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
Write 1 file. Write all code, no placeholders. Create the file at the EXACT path shown below (relative to the current working directory).
|
|
2
|
+
|
|
3
|
+
1. tests/test_calculator.py — Tests for a calculator module. Inline a `divide(a, b)` function that divides two numbers and raises ZeroDivisionError for zero denominators. Also inline a `save_result(path, value)` function that writes a float to a file. Write tests that cover: multiple input combinations for divide (positive, zero numerator, negative, fractional results) — use `@pytest.mark.parametrize` for table-driven testing (not separate test functions per case). Test the zero-denominator error case using `pytest.raises(ZeroDivisionError, match="...")` with the `match=` parameter to verify the error message text (not bare `pytest.raises` without match). Test save_result using `tmp_path` fixture (not the deprecated `tmpdir` fixture).
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
Write 1 file. Write all code, no placeholders. Create the file at the EXACT path shown below (relative to the current working directory).
|
|
2
|
+
|
|
3
|
+
1. tests/test_calculator.py — Tests for a calculator module. Inline a `divide(a, b)` function that divides two numbers and raises ZeroDivisionError for zero denominators. Also inline a `save_result(path, value)` function that writes a float to a file. Write tests that cover: multiple input combinations for divide (positive, zero numerator, negative, fractional results) — use table-driven testing to avoid repetitive test functions. Test the zero-denominator error case and verify the error message text. Test save_result using a temporary directory provided by the test framework.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Write tests for a calculator module with divide and save_result functions. Use pytest with table-driven testing, error message verification, and temporary file handling. Target Python 3.12+. Write all code with no placeholders.
|
|
@@ -27,6 +27,14 @@ done
|
|
|
27
27
|
|
|
28
28
|
BUDGET="2.00"
|
|
29
29
|
|
|
30
|
+
# --- Optional model pin (opt-in via MODEL env; no-op when unset) ---
|
|
31
|
+
MODEL="${MODEL:-}"
|
|
32
|
+
MODEL_ARGS=()
|
|
33
|
+
if [ -n "$MODEL" ]; then
|
|
34
|
+
MODEL_ARGS=(--model "$MODEL")
|
|
35
|
+
echo "[config] Pinning model: $MODEL"
|
|
36
|
+
fi
|
|
37
|
+
|
|
30
38
|
# --- Guidance toggle: rules/ file ---
|
|
31
39
|
RULE_FILE="$WORKSPACE/.claude/rules/modern-python.md"
|
|
32
40
|
RULE_SOURCE="$REPO_DIR/skills/modern-python-guidance/SKILL.md"
|
|
@@ -113,7 +121,8 @@ run_variant_session() {
|
|
|
113
121
|
record_verify "PRE-CONTROL-V4$(echo "$variant" | tr '[:lower:]' '[:upper:]')" "$log"
|
|
114
122
|
|
|
115
123
|
echo "[running] claude -p (Control, variant $variant) from $WORKSPACE ..."
|
|
116
|
-
|
|
124
|
+
echo "MODEL=${MODEL:-<default>}" >> "$log"
|
|
125
|
+
(cd "$WORKSPACE" && claude -p ${MODEL_ARGS[@]+"${MODEL_ARGS[@]}"} --output-format json --max-budget-usd "$BUDGET" \
|
|
117
126
|
< "$prompt" > "$results_dir/session-a.json" 2>"$results_dir/session-a.stderr") || true
|
|
118
127
|
|
|
119
128
|
record_verify "POST-CONTROL-V4$(echo "$variant" | tr '[:lower:]' '[:upper:]')" "$log"
|
|
@@ -134,7 +143,8 @@ run_variant_session() {
|
|
|
134
143
|
record_verify "PRE-TREATMENT-V4$(echo "$variant" | tr '[:lower:]' '[:upper:]')" "$log"
|
|
135
144
|
|
|
136
145
|
echo "[running] claude -p (Treatment, variant $variant) from $WORKSPACE ..."
|
|
137
|
-
|
|
146
|
+
echo "MODEL=${MODEL:-<default>}" >> "$log"
|
|
147
|
+
(cd "$WORKSPACE" && claude -p ${MODEL_ARGS[@]+"${MODEL_ARGS[@]}"} --output-format json --max-budget-usd "$BUDGET" \
|
|
138
148
|
< "$prompt" > "$results_dir/session-b.json" 2>"$results_dir/session-b.stderr") || true
|
|
139
149
|
|
|
140
150
|
record_verify "POST-TREATMENT-V4$(echo "$variant" | tr '[:lower:]' '[:upper:]')" "$log"
|