Skip to content

Conversation

shenxiangzhuang
Copy link
Owner

@shenxiangzhuang shenxiangzhuang commented Aug 25, 2025

  • Add cookiecutter.json with interactive prompts for package configuration
  • Support configurable Python version ranges (min/max versions)
  • Add post-generation hooks to clean up unused files based on user choices
  • Convert all template files to use cookiecutter variables
  • Add conditional GitHub Actions workflows for testing and docs
  • Add conditional pre-commit configuration
  • Simplify documentation to minimal home page only
  • Update main README with cookiecutter usage instructions

Users can now generate customized Python packages by running: cookiecutter https://github.com/shenxiangzhuang/mppt.git

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Introduced a configurable Cookiecutter template (Python versions, license, docs, CI, Codecov, CLI).
    • Added a starter package with example code and type hints.
  • Documentation
    • Expanded Getting Started with step-by-step paths (Cookiecutter and GitHub Template).
    • Added a templated docs site, README, and CHANGELOG.
  • Tests
    • Included unit and property-based tests to validate examples.
  • Chores
    • Added pre-commit setup.
    • Added CI workflows for multi-version testing and docs build/deploy.
    • Added development dependency to support template generation.

- Add cookiecutter.json with interactive prompts for package configuration
- Support configurable Python version ranges (min/max versions)
- Add post-generation hooks to clean up unused files based on user choices
- Convert all template files to use cookiecutter variables
- Add conditional GitHub Actions workflows for testing and docs
- Add conditional pre-commit configuration
- Simplify documentation to minimal home page only
- Update main README with cookiecutter usage instructions

Users can now generate customized Python packages by running:
cookiecutter https://github.com/shenxiangzhuang/mppt.git

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
@shenxiangzhuang shenxiangzhuang self-assigned this Aug 25, 2025
Copy link

coderabbitai bot commented Aug 25, 2025

Walkthrough

Adds a Cookiecutter-based project template with post-generation hooks, CI workflows (tests, docs), template code (hello, quick_sort), tests, docs site (MkDocs), and repository configs (pyproject, pre-commit, LICENSE, README). Root README onboarding expanded; root pyproject adds cookiecutter as a dev dependency.

Changes

Cohort / File(s) Summary
Repo onboarding & tooling
README.md, pyproject.toml
Expanded getting started instructions; added cookiecutter to dev optional dependencies.
Cookiecutter scaffolding
cookiecutter.json, hooks/post_gen_project.py
Introduces template prompts/options and a post-gen hook to update Python classifiers and conditionally remove docs, pre-commit, and GitHub Actions files.
Template CI workflows
{{cookiecutter.package_name}}/.github/workflows/build_docs.yaml, {{cookiecutter.package_name}}/.github/workflows/test.yaml
Adds docs build/deploy pipeline and matrix test workflow with optional Codecov upload; uses uv.
Template repository configuration
{{cookiecutter.package_name}}/pyproject.toml, {{cookiecutter.package_name}}/.pre-commit-config.yaml, {{cookiecutter.package_name}}/LICENSE, {{cookiecutter.package_name}}/README.md, {{cookiecutter.package_name}}/mkdocs.yml
Provides project metadata, dev/docs extras, lint/type config, pre-commit hooks, MIT license, README badges and setup, and MkDocs Material configuration.
Template documentation content
{{cookiecutter.package_name}}/docs/index.md, {{cookiecutter.package_name}}/CHANGELOG.md
Adds docs landing page with install/quick start/dev notes and a multi-version changelog.
Template package code
{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/__init__.py, {{cookiecutter.package_name}}/{{cookiecutter.package_name}}/hello.py, {{cookiecutter.package_name}}/{{cookiecutter.package_name}}/sort.py
Introduces package stub, hello/add utilities, and a typed quick_sort implementation with doctests.
Template tests
{{cookiecutter.package_name}}/tests/test_hello.py, {{cookiecutter.package_name}}/tests/test_sort.py
Adds unit test for hello() and Hypothesis-based property test for quick_sort.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Cookiecutter
  participant Template as Project Template
  participant Hook as post_gen_project.py
  participant FS as Filesystem

  User->>Cookiecutter: Run with cookiecutter.json
  Cookiecutter->>Template: Render files with answers
  Template->>Hook: Invoke post-gen hook
  Hook->>Hook: Generate Python classifiers
  Hook->>FS: Update pyproject.toml classifiers
  alt include_docs == "n"
    Hook->>FS: Remove docs/ and mkdocs.yml
  end
  alt include_pre_commit == "n"
    Hook->>FS: Remove .pre-commit-config.yaml
  end
  alt include_github_actions == "n"
    Hook->>FS: Remove .github/
  end
  Hook-->>User: Generation complete
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

enhancement, github_actions, dependencies, documentation

Poem

A nibble of code, a hop through the night,
I templated paths in moon-silver light.
Hooks trim the twigs, workflows take flight,
Docs softly bloom—so tidy, so bright.
With tests snug and warm, I twitch in delight. 🐇✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch cookiecutter-template

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@shenxiangzhuang shenxiangzhuang marked this pull request as draft August 25, 2025 09:51
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
pyproject.toml (1)

49-52: Fix Bug Tracker URL (currently points to GitHub Pages, not Issues)

"Bug Tracker" should link to the repository issues, not the website.

-"Bug Tracker" = "https://shenxiangzhuang.github.io/mppt/issues"
+"Bug Tracker" = "https://github.com/shenxiangzhuang/mppt/issues"
{{cookiecutter.package_name}}/README.md (1)

67-68: Hide “refer to the documentation” when docs are excluded.

When include_docs = "n", this sentence points to non-existent docs.

-For more details on customization options, refer to the documentation.
+{%- if cookiecutter.include_docs == "y" -%}
+For more details on customization options, refer to the documentation.
+{%- endif -%}
{{cookiecutter.package_name}}/CHANGELOG.md (1)

5-85: Make the changelog template-agnostic; remove upstream mppt-specific history and links.

This file will ship into every generated project. Embedding your own repository’s historical releases (e.g., PR links to shenxiangzhuang/mppt and Sphinx entries while the template uses MkDocs) will confuse consumers and create broken links in newly generated repos.

Recommend replacing past version sections with a neutral Keep a Changelog–style skeleton that users can fill in.

Apply this diff to replace the version history with a minimal, template-agnostic scaffold:

@@
 ## [Unreleased]
- 
-## [0.7.0] - 2025-04-25
-
-### Added
-
-* add(test): add mutmut pkg by @shenxiangzhuang in [#164](https://github.com/shenxiangzhuang/mppt/pull/164)
-* add(doc): sphinx-immaterial theme by @shenxiangzhuang in [#165](https://github.com/shenxiangzhuang/mppt/pull/165)
-
-### Changed
-
-* chore(linting): update Ruff lint configuration by @shenxiangzhuang in [#157](https://github.com/shenxiangzhuang/mppt/pull/157)
-* pkg: remove lock file by @shenxiangzhuang in [#158](https://github.com/shenxiangzhuang/mppt/pull/158)
-* chore: remove dev container by @shenxiangzhuang in [#161](https://github.com/shenxiangzhuang/mppt/pull/161)
-
-### CI/CD
-
-* ci: uv cache prune by @shenxiangzhuang in [#150](https://github.com/shenxiangzhuang/mppt/pull/150)
-* ci(publish): use pypi trusted publisher by @shenxiangzhuang in [#162](https://github.com/shenxiangzhuang/mppt/pull/162)
-
-## [0.6.0] - 2025-03-06
-
-### Added
-
-- CI: add mypy and ruff in test workflow
-
-### Changed
-
-- Linter & formatter: use ruff only, remove isort, black, flake8
-
-## [0.5.0] - 2025-01-03
-
-### Changed
-
-- Use uv to manage the package rather than poetry (#106)
-- Drop the support for Python 3.8 (#110)
-
-## [0.4.0] - 2024-06-11
-
-### Added
-
-- Poetry: Add commitizen, a conventional commit tool (#71)
-- Add `py.typed` file to support type hinting (#70)
-- Add sql linter: SQLFluff (#58)
-- Add task runner just (#50)
-- Add package management tool uv (#49)
-- Add Locust as a load testing tool (#39)
-
-## [0.3.0] - 2023-12-14
-
-### Added
-
-- Add ruff as extra linter and formatter: both in dependency and pre-commit (#31)
-- Add more details about the linters and formatters with the resource from Google SRE book
-- Add doctest (#34)
-
-## [0.2.0] - 2023-12-01
-
-### Added
-
-- Documentations for all the features (#20)
-- Quick sort implementation for hypothesis testing (#22)
-
-### Changed
-
-- Change package management from Rye to Poetry (#15, #19)
-
-### Removed
-
-- Sweep AI App
-
-## [0.1.1] - 2023-08-15
-
-### Changed
-
-- reorg this package template
-
-## [0.1.0] - 2023-08-15
-
-### Added
-
-- add this package template
+ 
+### Added
+* ...
+
+### Changed
+* ...
+
+### Deprecated
+* ...
+
+### Removed
+* ...
+
+### Fixed
+* ...
+
+### Security
+* ...

I can push a follow-up commit that also adds a short header explaining “Keep a Changelog” and SemVer, if you want.

🧹 Nitpick comments (29)
{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/__init__.py (1)

1-1: Template the docstring to the generated package name

Hard-coding "MPPT" leaks the template’s own name into generated projects. Prefer a cookiecutter variable so every new package has an accurate docstring.

-"""MPPT Module."""
+"""{{ cookiecutter.package_name }} package."""

If you’d like, we can also expose a resilient version in generated packages (optional):

from importlib.metadata import PackageNotFoundError, version

try:
    __version__ = version("{{ cookiecutter.package_name }}")
except PackageNotFoundError:
    # Package is not installed (e.g., working from a checkout)
    __version__ = "0.0.0"
pyproject.toml (2)

110-112: Consider deriving Ruff target-version from the template’s min Python

You advertise configurable Python version ranges in the template; keeping Ruff’s target-version aligned with the minimum supported Python for the generated project will avoid false positives/negatives.

For example (adjust variable name to your cookiecutter.json schema):

# If min Python is e.g. "3.9", becomes "py39"
target-version = "py{{ cookiecutter.min_python|replace('.', '') }}"

34-35: Update Cookiecutter version pin to latest 2.x

Please pin Cookiecutter to the most recent 2.x release to avoid untracked breaking changes:

• In pyproject.toml, change:

-    "cookiecutter",
+    "cookiecutter>=2.6.0,<3",

Cookiecutter 2.6.0 is the latest stable release (published Feb 21 2024) and adds support for Python 3.12, ensuring full compatibility with Python 3.9+ (github.com, cookiecutter.readthedocs.io)

You can revisit this pin when a 3.x release becomes stable.

{{cookiecutter.package_name}}/.pre-commit-config.yaml (1)

17-21: Avoid heavy "types-all" in mypy hook; pass config and let deps drive stubs

Installing types-all pulls many stubs and slows hooks. Prefer using your project’s dependencies (and explicit type stubs when needed) and point mypy to pyproject config.

-  - repo: https://github.com/pre-commit/mirrors-mypy
-    rev: v1.11.2
-    hooks:
-      - id: mypy
-        additional_dependencies: [types-all]
+  - repo: https://github.com/pre-commit/mirrors-mypy
+    rev: v1.11.2
+    hooks:
+      - id: mypy
+        args: [--config-file=pyproject.toml]

If you really want broad stubs for the template, consider a smaller curated list (e.g., types-requests) instead of types-all.

{{cookiecutter.package_name}}/.github/workflows/build_docs.yaml (2)

20-31: Optional: cache dependencies and add concurrency to avoid redundant builds

setup-uv supports caching; concurrency prevents duplicate runs on rapid pushes.

Add after "on" (or at job level if you prefer):

+concurrency:
+  group: docs-${{ github.ref }}
+  cancel-in-progress: true

And enable caching (example; verify exact inputs for setup-uv):

- name: Install uv
  uses: astral-sh/setup-uv@v3
  with:
    version: "latest"
+   enable-cache: true

15-18: Upgrade GitHub Action to setup-python v5 for improved caching and compatibility

  • File: {{cookiecutter.package_name}}/.github/workflows/build_docs.yaml (lines 15–18)
-      uses: actions/setup-python@v4
+      uses: actions/setup-python@v5

v5 is the current stable major release (e.g. v5.2.0 as of Aug 29, 2025) and includes several non-breaking enhancements over v4:

  • Adds architecture (arch) to cache keys, preventing cross-arch cache collisions on runners (github.com)
  • Includes explicit “.zip” extension for Windows package downloads to ensure compatibility with Expand-Archive on self-hosted Windows runners (github.com)
  • Supports newer Python versions (including experimental “free-threaded” builds like 3.13t) and reading from .tool-versions files (github.com)

Since this workflow pins Python 3.9, there are no breaking changes—upgrade and validate via CI to take advantage of these fixes and improvements.

{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/hello.py (1)

17-33: Consider widening accepted numeric types for add().

Right now the signature forces float; ints work at runtime but can be flagged by strict type checkers. If you want this template to be friendlier for beginners, accept real numbers and document coercion to float.

Apply this minimal change to the signature and return type while keeping behavior identical:

-def add(a: float, b: float) -> float:
+from numbers import Real
+
+def add(a: Real, b: Real) -> float:
     """Adds two numbers together.

Optionally add a quick doctest showing mixed int/float usage:

>>> add(1, 2.5)
3.5
{{cookiecutter.package_name}}/tests/test_hello.py (1)

4-6: Strengthen the assertion to catch regressions.

Asserting only the type could miss errors like changed content. Assert the exact greeting as well.

 def test_hello() -> None:
-    msg = hello()
-    assert isinstance(msg, str)
+    msg = hello()
+    assert isinstance(msg, str)
+    assert msg == "Hello, World!"

Optional: add a small test for add() to exercise the second public function:

def test_add() -> None:
    from {{ cookiecutter.package_name }}.hello import add
    assert add(1, 2) == 3
    assert add(1.5, 2.5) == 4.0
{{cookiecutter.package_name}}/docs/index.md (1)

21-36: Nice, concise dev workflow; minor consistency nit.

You promote uv for env and dev flows—great. Consider mirroring that in the Installation section (e.g., uv pip install ...) or add a note that pip is fine for end users while uv is recommended for contributors.

{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/sort.py (1)

4-21: Readable, correct quick sort; small clarity refactor recommended.

Function is correct for floats and aligns with tests. For readability and debug-ability, split the one-liner into named parts without changing behavior. Also, consider the worst-case O(n^2) due to first-element pivot; acceptable for teaching, but call it out in the docstring.

 def quick_sort(xs: list[float]) -> list[float]:
-    """Quick sort: A very radical implementation.
+    """Quick sort: simple recursive implementation (demonstration).
@@
-    if len(xs) <= 1:
-        return xs
-    return [*quick_sort([x for x in xs[1:] if x <= xs[0]]), xs[0], *quick_sort([x for x in xs[1:] if x > xs[0]])]
+    if len(xs) <= 1:
+        return xs
+    pivot, rest = xs[0], xs[1:]
+    left = [x for x in rest if x <= pivot]
+    right = [x for x in rest if x > pivot]
+    return [*quick_sort(left), pivot, *quick_sort(right)]

If you want better average/worst-case behavior without losing clarity, sample a pivot (or use median-of-three). I can provide a version gated by a small threshold to fall back to built-in sorted() for very long inputs.

{{cookiecutter.package_name}}/tests/test_sort.py (1)

10-13: Good property test; add non-mutating and idempotency checks.

Two lightweight properties help catch accidental in-place mutation and ensure stability of results across repeated calls.

Additional tests (can be added below):

from copy import copy

class TestQuickSort:
    @given(items=float_list)
    @settings(max_examples=300)
    def test_non_mutating(self, items: list[float]) -> None:
        original = copy(items)
        quick_sort(items)
        assert items == original

    @given(items=float_list)
    @settings(max_examples=200)
    def test_idempotent(self, items: list[float]) -> None:
        once = quick_sort(items)
        twice = quick_sort(once)
        assert once == twice
{{cookiecutter.package_name}}/README.md (1)

31-39: Windows activation hint (PowerShell) — minor usability tweak.

PowerShell users typically run .venv\Scripts\Activate.ps1. Consider mentioning it.

-   source .venv/bin/activate  # On Windows: .venv\Scripts\activate
+   source .venv/bin/activate  # Windows (CMD): .venv\Scripts\activate  |  Windows (PowerShell): .venv\Scripts\Activate.ps1
README.md (2)

25-35: Recommend pinning to a tag/commit for reproducible template generation.

Unpinned cookiecutter pulls can change behavior over time. Suggest a --checkout to a released tag.

-   cookiecutter https://github.com/shenxiangzhuang/mppt.git
+   # Pin to a release tag (recommended) for reproducibility
+   cookiecutter https://github.com/shenxiangzhuang/mppt.git --checkout vX.Y.Z

When you cut the next release, consider updating the README to the latest tag.


49-51: Optional: Note pre-commit step for Cookiecutter path when enabled.

You already show pre-commit in Option 2. Adding a short “if enabled” note here keeps both paths consistent.

    uv sync --all-extras --dev
+   # If you enabled pre-commit during generation:
+   uv run pre-commit install
cookiecutter.json (2)

10-14: License set could include MPL-2.0 and LGPL-3.0.

Consider broadening choices; MIT/Apache/MPL cover most needs. If you prefer minimalism, ignore.

-    "license": ["MIT", "Apache-2.0", "BSD-3-Clause", "GPL-3.0", "LGPL-2.1"],
+    "license": ["MIT", "Apache-2.0", "BSD-3-Clause", "MPL-2.0", "GPL-3.0", "LGPL-3.0"],

24-33: Verify assets in _copy_without_render exist.

docs/overrides/main.html may not be present. If not, remove it to avoid cookiecutter warnings.

{{cookiecutter.package_name}}/mkdocs.yml (3)

33-54: Remove duplicate feature entry.

content.code.annotate appears twice.

   features:
     - content.action.edit
     - content.action.view
     - announce.dismiss
-    - content.code.annotate
     - content.tabs.link
     - content.tooltips
     - header.autohide
     - navigation.instant
     - navigation.tracking
     - navigation.tabs
     - navigation.tabs.sticky
     - navigation.indexes
     - navigation.prune
     - navigation.sections
     - navigation.top
     - search.highlight
     - search.share
     - search.suggest
     - toc.follow
     # - toc.integrate
-    - content.code.annotate
+    - content.code.annotate

1-7: Add edit_uri and site_url; make links resilient to default branch changes.

content.action.edit/view works best with edit_uri. Also consider site_url for canonical links.

 site_name: {{ cookiecutter.package_name }}
 repo_url: https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }}
 repo_name: {{ cookiecutter.github_username }}/{{ cookiecutter.package_name }}
+edit_uri: edit/{{ cookiecutter.default_branch }}/docs/
+site_url: https://{{ cookiecutter.github_username }}.github.io/{{ cookiecutter.package_name }}/
 site_description: {{ cookiecutter.project_short_description }}
 site_author: {{ cookiecutter.author_name }}
 copyright: Copyright &copy; {% now 'utc', '%Y' %} {{ cookiecutter.author_name }}

57-61: Point social link to the repository rather than the user (clearer CTA).

 extra:
   social:
     - icon: fontawesome/brands/github
-      link: https://github.com/{{ cookiecutter.github_username }}/
+      link: https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }}
{{cookiecutter.package_name}}/.github/workflows/test.yaml (4)

19-20: Update setup-python to v5.

actions/setup-python@v5 is current and has improvements over v4.

-      uses: actions/setup-python@v4
+      uses: actions/setup-python@v5

1-8: Tighten default workflow permissions.

Set least-privilege permissions at the workflow level.

 name: Test

 on:
   push:
     branches: [ master, main ]
   pull_request:
     branches: [ master, main ]
+
+permissions:
+  contents: read

9-16: Optional: add concurrency to cancel redundant runs on the same PR/branch.

 jobs:
   test:
+    concurrency:
+      group: ${{ github.workflow }}-${{ github.ref }}
+      cancel-in-progress: true
     runs-on: ubuntu-latest

37-44: Bump Codecov action to v4 and simplify config.

v3 is legacy. v4 reduces maintenance; keep fail_ci_if_error as you prefer.

-    - name: Upload coverage to Codecov
-      uses: codecov/codecov-action@v3
+    - name: Upload coverage to Codecov
+      uses: codecov/codecov-action@v4
       with:
         file: ./coverage.xml
         flags: unittests
         name: codecov-umbrella
         fail_ci_if_error: false
{{cookiecutter.package_name}}/CHANGELOG.md (1)

27-49: Unify unordered list marker to satisfy markdownlint (MD004).

You mix “” and “-” for bullets. Pick one (you already use “” earlier). If you decide to keep the historical sections, apply:

- - CI: add mypy and ruff in test workflow
+ * CI: add mypy and Ruff in test workflow

Repeat similarly for other “- ” bullets in the file.

If you run markdownlint locally, MD004 should pass after normalizing list markers. Want me to open a small PR to standardize linting across docs?

Also applies to: 55-57, 63-64, 68-68, 72-72, 78-78, 84-84

hooks/post_gen_project.py (3)

18-35: Validate inputs and handle edge cases in generate_python_versions.

Right now, invalid inputs (e.g., min > max) silently yield odd results, and only 3.x is supported. Add basic validation and clearer behavior.

 def generate_python_versions(min_version, max_version):
     """Generate list of Python versions between min and max (inclusive)."""
-    # Extract major and minor versions
-    min_parts = min_version.split(".")
-    max_parts = max_version.split(".")
-
-    min_major, min_minor = int(min_parts[0]), int(min_parts[1])
-    max_major, max_minor = int(max_parts[0]), int(max_parts[1])
-
-    versions = []
-
-    # For now, assume all versions are 3.x
-    if min_major == 3 and max_major == 3:
-        for minor in range(min_minor, max_minor + 1):
-            versions.append(f"3.{minor}")
-
-    return versions
+    try:
+        min_major, min_minor = map(int, min_version.split(".")[:2])
+        max_major, max_minor = map(int, max_version.split(".")[:2])
+    except (ValueError, AttributeError) as e:
+        raise ValueError(f"Invalid python version(s): {min_version!r}, {max_version!r}") from e
+
+    if (min_major, min_minor) > (max_major, max_minor):
+        raise ValueError(f"Min version {min_version} cannot be greater than max version {max_version}.")
+
+    # For now, support only Python 3.x explicitly.
+    if min_major != 3 or max_major != 3:
+        # Return just the single version when majors differ or are unsupported.
+        return [f"{min_major}.{min_minor}"] if (min_major, min_minor) == (max_major, max_minor) else []
+
+    return [f"3.{minor}" for minor in range(min_minor, max_minor + 1)]

49-56: Avoid trailing comma in the last classifier entry.

Some TOML parsers are strict about trailing commas in arrays. Build the list with join so only inter-item commas are emitted.

-    # Generate classifiers
-    classifiers = []
-    for version in python_versions:
-        classifiers.append(f'    "Programming Language :: Python :: {version}",')
-
-    # Replace the Python version classifiers placeholder
-    replacement = "\n".join(classifiers)
+    # Generate classifiers
+    classifiers = [f'    "Programming Language :: Python :: {version}"' for version in python_versions]
+
+    # Replace the Python version classifiers placeholder
+    replacement = ",\n".join(classifiers)

If you want to be fully robust, we can switch to editing with a TOML library; but for a one-shot post-gen hook, join-based string replace is fine.


9-16: Make remove_file_or_dir symlink-safe.

rmtree on a directory symlink can raise errors. Treat symlinks like files.

 def remove_file_or_dir(file_path):
     """Remove file or directory if it exists."""
     if os.path.exists(file_path):
-        if os.path.isdir(file_path):
+        if os.path.islink(file_path) or os.path.isfile(file_path):
+            os.remove(file_path)
+        elif os.path.isdir(file_path):
             shutil.rmtree(file_path)
-        else:
-            os.remove(file_path)
{{cookiecutter.package_name}}/pyproject.toml (2)

10-16: Optional: add the generic “Python :: 3” classifier for discoverability.

Helps PyPI search and aligns with Trove classifier guidance.

 classifiers = [
     "Development Status :: {{ cookiecutter.development_status }}",
     "Intended Audience :: Science/Research",
     "Intended Audience :: Developers",
     "Operating System :: OS Independent",
+    "Programming Language :: Python :: 3",
     # PYTHON_VERSION_CLASSIFIERS_PLACEHOLDER
 ]

109-111: Make Ruff target-version dynamic to the chosen minimum Python version.

This keeps linting and autofixes aligned with supported syntax.

-# Assume Python 3.9
-target-version = "py39"
+# Match the chosen minimum Python version
+target-version = "py{{ cookiecutter.python_min_version | replace('.', '') }}"

If min is 3.12, Ruff should print “Target version: py312” in its verbose output.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 68ee1aa and cb3b779.

📒 Files selected for processing (18)
  • README.md (1 hunks)
  • cookiecutter.json (1 hunks)
  • hooks/post_gen_project.py (1 hunks)
  • pyproject.toml (1 hunks)
  • {{cookiecutter.package_name}}/.github/workflows/build_docs.yaml (1 hunks)
  • {{cookiecutter.package_name}}/.github/workflows/test.yaml (1 hunks)
  • {{cookiecutter.package_name}}/.pre-commit-config.yaml (1 hunks)
  • {{cookiecutter.package_name}}/CHANGELOG.md (1 hunks)
  • {{cookiecutter.package_name}}/LICENSE (1 hunks)
  • {{cookiecutter.package_name}}/README.md (1 hunks)
  • {{cookiecutter.package_name}}/docs/index.md (1 hunks)
  • {{cookiecutter.package_name}}/mkdocs.yml (1 hunks)
  • {{cookiecutter.package_name}}/pyproject.toml (1 hunks)
  • {{cookiecutter.package_name}}/tests/test_hello.py (1 hunks)
  • {{cookiecutter.package_name}}/tests/test_sort.py (1 hunks)
  • {{cookiecutter.package_name}}/{{cookiecutter.package_name}}/__init__.py (1 hunks)
  • {{cookiecutter.package_name}}/{{cookiecutter.package_name}}/hello.py (1 hunks)
  • {{cookiecutter.package_name}}/{{cookiecutter.package_name}}/sort.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
{{cookiecutter.package_name}}/tests/test_sort.py (1)
{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/sort.py (1)
  • quick_sort (4-21)
{{cookiecutter.package_name}}/tests/test_hello.py (1)
{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/hello.py (1)
  • hello (4-14)
{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/sort.py (1)
tests/test_sort.py (2)
  • TestQuickSort (9-13)
  • test_float (12-13)
🪛 LanguageTool
{{cookiecutter.package_name}}/README.md

[style] ~8-~8: Using many exclamation marks might seem excessive (in this case: 9 exclamation marks for a text that’s 2459 characters long)
Context: ...on)](https://github.com/astral-sh/ruff) Checked with mypy {%- if cookiecutter.include_pre_commit == "y" %} pre-commit {%- endif %} {%- if cookiecutter.include_github_actions == "y" %} {%- if cookiecutter.include_docs == "y" %} [![Build Docs](https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }}/actions/workflows/build_docs.yaml/badge.svg)](https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }}/actions/workflows/build_docs.yaml) {%- endif %} [![Test](https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }}/actions/workflows/test.yaml/badge.svg)](https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }}/actions/workflows/test.yaml) {%- endif %} {%- if cookiecutter.include_codecov == "y" %} [![Codecov](https://codecov.io/gh/{{ cooki...

(EN_EXCESSIVE_EXCLAMATION)

{{cookiecutter.package_name}}/CHANGELOG.md

[grammar] ~14-~14: There might be a mistake here.
Context: ...nfiguration by @shenxiangzhuang in #157 * pkg: remove lock file by @shenxiangzhuan...

(QB_NEW_EN)


[grammar] ~15-~15: There might be a mistake here.
Context: ...e lock file by @shenxiangzhuang in #158 * chore: remove dev container by @shenxian...

(QB_NEW_EN)


[grammar] ~55-~55: There might be a mistake here.
Context: ....0] - 2023-12-14 ### Added - Add ruff as extra linter and formatter: both in dep...

(QB_NEW_EN)


[grammar] ~56-~56: There might be a mistake here.
Context: ...inters and formatters with the resource from Google SRE book - Add doctest (#34) ##...

(QB_NEW_EN)


[grammar] ~63-~63: There might be a mistake here.
Context: ...ocumentations for all the features (#20) - Quick sort implementation for hypothesis...

(QB_NEW_EN)

README.md

[grammar] ~37-~37: There might be a mistake here.
Context: ...r the prompts to customize your project: - Project name - Author information ...

(QB_NEW_EN)


[grammar] ~38-~38: There might be a mistake here.
Context: ...ustomize your project: - Project name - Author information - GitHub username ...

(QB_NEW_EN)


[grammar] ~39-~39: There might be a mistake here.
Context: ... - Project name - Author information - GitHub username - Package description...

(QB_NEW_EN)


[grammar] ~40-~40: There might be a mistake here.
Context: ... Author information - GitHub username - Package description - Python versions...

(QB_NEW_EN)


[grammar] ~41-~41: There might be a mistake here.
Context: ...GitHub username - Package description - Python versions to support - Optional...

(QB_NEW_EN)


[grammar] ~42-~42: There might be a mistake here.
Context: ...cription - Python versions to support - Optional features (docs, pre-commit, Git...

(QB_NEW_EN)

🪛 markdownlint-cli2 (0.17.2)
{{cookiecutter.package_name}}/README.md

3-3: Bare URL used

(MD034, no-bare-urls)


4-4: Bare URL used

(MD034, no-bare-urls)


5-5: Bare URL used

(MD034, no-bare-urls)


6-6: Bare URL used

(MD034, no-bare-urls)


15-15: Bare URL used

(MD034, no-bare-urls)


17-17: Bare URL used

(MD034, no-bare-urls)


20-20: Bare URL used

(MD034, no-bare-urls)

{{cookiecutter.package_name}}/CHANGELOG.md

27-27: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


31-31: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


37-37: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


38-38: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


44-44: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


45-45: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


46-46: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


47-47: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


48-48: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


49-49: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


55-55: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


56-56: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


57-57: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


63-63: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


64-64: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


68-68: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


72-72: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


78-78: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)


84-84: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

🪛 YAMLlint (1.37.1)
{{cookiecutter.package_name}}/mkdocs.yml

[error] 3-3: syntax error: expected , but found ''

(syntax)

{{cookiecutter.package_name}}/.github/workflows/test.yaml

[error] 35-35: syntax error: found character '%' that cannot start any token

(syntax)

🔇 Additional comments (8)
{{cookiecutter.package_name}}/.pre-commit-config.yaml (2)

1-9: Pre-commit base hooks look good

Solid baseline: YAML checks, EOL/trailing whitespace fixes, large-file guard. Good choices for a template.


10-16: Ruff hook configuration looks good

Running ruff with --fix followed by ruff-format is a sensible default for generated repos.

{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/hello.py (1)

4-14: LGTM: Simple, pure function with clear doctest.

The hello() implementation and documentation look good and are easy to test and maintain.

{{cookiecutter.package_name}}/mkdocs.yml (1)

1-73: FYI on YAML lints against templated files.

The reported YAML syntax error is from Jinja control blocks and placeholders prior to rendering. Consider excluding cookiecutter templates from YAML linting or adding a pre-generation lint step.

{{cookiecutter.package_name}}/.github/workflows/test.yaml (2)

18-22: Good use of Jinja raw to preserve GitHub expressions.

The $ + raw-wrapped {{ matrix.python-version }} approach will render correctly post-cookiecutter.


35-35: About the YAMLlint error on line 35.

This error is due to Jinja control structures ({%- if ... %}) in a yet-to-be-rendered template. It’s safe to ignore at template time; consider excluding template files from YAML linting.

hooks/post_gen_project.py (1)

63-81: Verify documentation URLs in pyproject.toml template

Before merging, please confirm that your template’s pyproject.toml indeed has unconditional “Documentation” and “Release Notes” URL entries, and update it so they only appear when cookiecutter.include_docs is "y".

• Locate your pyproject.toml Jinja template (e.g. {{cookiecutter.project_slug}}/pyproject.toml or similar)
• Wrap any documentation = "…" or release-notes = "…" keys in a Jinja conditional:

{% if cookiecutter.include_docs == "y" %}
documentation = "https://{{ cookiecutter.github_username }}.github.io/{{ cookiecutter.repo_name }}/"
release-notes = "https://{{ cookiecutter.github_username }}.github.io/{{ cookiecutter.repo_name }}/CHANGELOG.html"
{% endif %}

• Ensure update_pyproject_toml() does not reinsert those keys unconditionally.

By gating those entries, you’ll avoid generating broken URLs when include_docs == "n".

{{cookiecutter.package_name}}/pyproject.toml (1)

47-53: Gate docs-related URLs behind include_docs

To prevent broken links when documentation isn’t generated, wrap the “Documentation” and “Release Notes” entries in a Jinja conditional that checks cookiecutter.include_docs.

• File: {{cookiecutter.package_name}}/pyproject.toml
• Section: [project.urls] (around lines 47–53)

Suggested diff:

 [project.urls]
 "Homepage"     = "https://{{ cookiecutter.github_username }}.github.io/{{ cookiecutter.package_name }}"
 "Bug Tracker"  = "https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }}/issues"
-"Documentation" = "https://{{ cookiecutter.github_username }}.github.io/{{ cookiecutter.package_name }}"
-"Release Notes" = "https://{{ cookiecutter.github_username }}.github.io/{{ cookiecutter.package_name }}/changelog/"
+{%- if cookiecutter.include_docs == "y" %}
+  "Documentation" = "https://{{ cookiecutter.github_username }}.github.io/{{ cookiecutter.package_name }}"
+  "Release Notes"  = "https://{{ cookiecutter.github_username }}.github.io/{{ cookiecutter.package_name }}/changelog/"
+{%- endif %}
 "Source Code"   = "https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }}"

Please test by generating the project with include_docs = "n" to confirm those entries are omitted.

Comment on lines +1 to +9
name: Build Docs

on:
push:
branches: [ master, main ]
pull_request:
branches: [ master, main ]

jobs:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Set explicit token permissions (write needed to push gh-pages)

MkDocs gh-deploy pushes to gh-pages. Without contents: write, this can fail depending on repo defaults. Recommend read by default, write only for deploy job.

 name: Build Docs

 on:
   push:
     branches: [ master, main ]
   pull_request:
     branches: [ master, main ]
+
+permissions:
+  contents: read

And in deploy job:

   deploy-docs:
     runs-on: ubuntu-latest
     if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main'
     needs: build-docs
+    permissions:
+      contents: write
🤖 Prompt for AI Agents
{{cookiecutter.package_name}}/.github/workflows/build_docs.yaml lines 1-9: the
workflow does not declare explicit token permissions so gh-deploy may fail to
push to gh-pages; add a top-level minimal permissions block (e.g., contents:
read) to restrict default scope, and in the deploy job override permissions to
allow pushing (e.g., permissions: contents: write) so the job that runs MkDocs
gh-deploy can push to gh-pages; ensure the deploy job uses the default
GITHUB_TOKEN (or the appropriate token) so these permissions apply.

Comment on lines +13 to +31
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.9"

- name: Install uv
uses: astral-sh/setup-uv@v3
with:
version: "latest"

- name: Install dependencies
run: |
uv sync --extra docs --dev

- name: Build documentation
run: |
uv run mkdocs build --strict
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix YAML indentation for steps (workflow will fail to parse)

List items under "steps:" must be indented beneath it. Currently they are at the same indent level as "steps:" which breaks parsing.

-    steps:
-    - uses: actions/checkout@v4
-    - name: Set up Python
-      uses: actions/setup-python@v4
-      with:
-        python-version: "3.9"
-    - name: Install uv
-      uses: astral-sh/setup-uv@v3
-      with:
-        version: "latest"
-    - name: Install dependencies
-      run: |
-        uv sync --extra docs --dev
-    - name: Build documentation
-      run: |
-        uv run mkdocs build --strict
+    steps:
+      - uses: actions/checkout@v4
+      - name: Set up Python
+        uses: actions/setup-python@v4
+        with:
+          python-version: "3.9"
+      - name: Install uv
+        uses: astral-sh/setup-uv@v3
+        with:
+          version: "latest"
+      - name: Install dependencies
+        run: |
+          uv sync --extra docs --dev
+      - name: Build documentation
+        run: |
+          uv run mkdocs build --strict
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.9"
- name: Install uv
uses: astral-sh/setup-uv@v3
with:
version: "latest"
- name: Install dependencies
run: |
uv sync --extra docs --dev
- name: Build documentation
run: |
uv run mkdocs build --strict
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.9"
- name: Install uv
uses: astral-sh/setup-uv@v3
with:
version: "latest"
- name: Install dependencies
run: |
uv sync --extra docs --dev
- name: Build documentation
run: |
uv run mkdocs build --strict
🤖 Prompt for AI Agents
In {{cookiecutter.package_name}}/.github/workflows/build_docs.yaml around lines
13 to 31, the top-level list items for the job steps are not indented under
"steps:" which makes the YAML invalid; fix by indenting every step entry (the
lines starting with "- uses:" and "- name:") one additional indentation level
under "steps:", and ensure their nested keys (uses, name, with, run, and the
block contents) are consistently indented further beneath each step so the steps
list and their inner mappings form valid YAML.

Comment on lines +38 to +58
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.9"

- name: Install uv
uses: astral-sh/setup-uv@v3
with:
version: "latest"

- name: Install dependencies
run: |
uv sync --extra docs --dev

- name: Deploy documentation
run: |
uv run mkdocs gh-deploy --force
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix indentation for deploy steps as well

Same indentation issue in the deploy-docs job.

-    steps:
-    - uses: actions/checkout@v4
-      with:
-        fetch-depth: 0
-    - name: Set up Python
-      uses: actions/setup-python@v4
-      with:
-        python-version: "3.9"
-    - name: Install uv
-      uses: astral-sh/setup-uv@v3
-      with:
-        version: "latest"
-    - name: Install dependencies
-      run: |
-        uv sync --extra docs --dev
-    - name: Deploy documentation
-      run: |
-        uv run mkdocs gh-deploy --force
+    steps:
+      - uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+      - name: Set up Python
+        uses: actions/setup-python@v4
+        with:
+          python-version: "3.9"
+      - name: Install uv
+        uses: astral-sh/setup-uv@v3
+        with:
+          version: "latest"
+      - name: Install dependencies
+        run: |
+          uv sync --extra docs --dev
+      - name: Deploy documentation
+        run: |
+          uv run mkdocs gh-deploy --force
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.9"
- name: Install uv
uses: astral-sh/setup-uv@v3
with:
version: "latest"
- name: Install dependencies
run: |
uv sync --extra docs --dev
- name: Deploy documentation
run: |
uv run mkdocs gh-deploy --force
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.9"
- name: Install uv
uses: astral-sh/setup-uv@v3
with:
version: "latest"
- name: Install dependencies
run: |
uv sync --extra docs --dev
- name: Deploy documentation
run: |
uv run mkdocs gh-deploy --force
🤖 Prompt for AI Agents
In {{cookiecutter.package_name}}/.github/workflows/build_docs.yaml around lines
38 to 58, the deploy steps under the job share incorrect indentation (deploy
steps aligned with other top-level steps) which can break the workflow; fix by
indenting the "Install uv", "Install dependencies", and "Deploy documentation"
step blocks so they are nested under the correct job/steps mapping (match the
same indentation level as the preceding steps in that job), ensuring each step
uses the same two-space/consistent indentation as the rest of the YAML file.

Comment on lines +13 to +17
```python
from {{ cookiecutter.package_name }} import hello

print(hello())
```
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Quick Start import is incorrect and will raise a TypeError.

from {{ cookiecutter.package_name }} import hello imports the hello module, not the hello() function; calling hello() will fail. Either re-export hello in init.py or import the function from the submodule here. Since init.py (in this template) doesn’t re-export, fix the snippet.

-```python
-from {{ cookiecutter.package_name }} import hello
-
-print(hello())
-```
+```python
+from {{ cookiecutter.package_name }}.hello import hello
+
+print(hello())
+```

Alternative (if you prefer the original import style): export hello in {{cookiecutter.package_name}}/init.py by adding:

from .hello import hello  # re-export for convenience
__all__ = ["hello"]
🤖 Prompt for AI Agents
In {{cookiecutter.package_name}}/docs/index.md around lines 13 to 17 the Quick
Start uses "from {{ cookiecutter.package_name }} import hello" which imports the
module, causing a TypeError when calling hello(); update the snippet to import
the function from the submodule (i.e., import hello from
{{cookiecutter.package_name}}.hello) so print(hello()) works, or alternatively
add a re-export in {{cookiecutter.package_name}}/__init__.py by importing and
exposing hello (and adding it to __all__) so the original import style becomes
valid.

@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2023 Mathew Shen
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Template the copyright header (year and name)

Hard-coded “2023 Mathew Shen” will be wrong for most users and over time. Use cookiecutter vars (and a dynamic year).

-Copyright (c) 2023 Mathew Shen
+Copyright (c) {{ cookiecutter.year }} {{ cookiecutter.full_name }}

If not already present, add to cookiecutter.json:

{
  "full_name": "Your Name",
  "year": "{% now 'utc', '%Y' %}"
}
🤖 Prompt for AI Agents
In {{cookiecutter.package_name}}/LICENSE around line 3, the copyright header is
hard-coded as “2023 Mathew Shen”; replace it with cookiecutter template
variables so it renders the package author and current year (e.g. use
{{cookiecutter.year}} and {{cookiecutter.full_name}} in the LICENSE file), and
if cookiecutter.json does not already include these keys add "full_name" and
"year" (with year set to "{% now 'utc', '%Y' %}") so new projects populate
correctly.

"Operating System :: OS Independent",
# PYTHON_VERSION_CLASSIFIERS_PLACEHOLDER
]
requires-python = ">= {{ cookiecutter.python_min_version }}"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Honor max Python version in requires-python.

PR goal includes configurable min/max versions. Encode the upper bound using the “next minor” convention.

-requires-python = ">= {{ cookiecutter.python_min_version }}"
+requires-python = ">= {{ cookiecutter.python_min_version }}, < {{ cookiecutter.python_max_version.split('.')[0] }}.{{ (cookiecutter.python_max_version.split('.')[1] | int) + 1 }}"

After generation, ensure pip shows the correct Requires-Python in metadata: python -m build && twine check dist/*.


🏁 Script executed:

#!/usr/bin/env bash
set -e
# Locate cookiecutter.json
found=$(fd -e json cookiecutter.json || true)
echo "cookiecutter.json path: $found"
if [ -n "$found" ]; then
  echo "---- cookiecutter.json content ----"
  cat "$found"
  echo "---- Searching for python_max_version ----"
  rg -n "python_max_version" -C2 "$found" || true
fi

# Locate all pyproject.toml files
pyfiles=$(fd -e toml pyproject.toml)
echo "pyproject.toml files found:"
echo "$pyfiles"

for file in $pyfiles; do
  echo "==== $file ===="
  echo "---- First 30 lines ----"
  head -n 30 "$file"
  echo "---- Searching for requires-python ----"
  rg -n "requires-python" -C2 "$file" || true
done

Length of output: 4654


Add an upper bound to requires-python
To honor the configurable max‐version, the requires-python field must include an upper bound using the “next minor” convention.

• File: {{cookiecutter.package_name}}/pyproject.toml (around line 17)
• Replace:

- requires-python = ">= {{ cookiecutter.python_min_version }}"

with:

requires-python = ">= {{ cookiecutter.python_min_version }}, < {{ cookiecutter.python_max_version.split('.')[0] }}.{{ (cookiecutter.python_max_version.split('.')[1] | int) + 1 }}"

After making this change, build and inspect the wheel metadata to confirm the upper bound is applied:

python -m build && twine check dist/*
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
requires-python = ">= {{ cookiecutter.python_min_version }}"
requires-python = ">= {{ cookiecutter.python_min_version }}, < {{ cookiecutter.python_max_version.split('.')[0] }}.{{ (cookiecutter.python_max_version.split('.')[1] | int) + 1 }}"
🤖 Prompt for AI Agents
In {{cookiecutter.package_name}}/pyproject.toml around line 17, the
requires-python field only sets a lower bound and must include an upper bound
using the “next minor” convention (e.g., if python_min_version is X.Y, require
">= X.Y, < X.(Y+1)"); update the requires-python entry accordingly to compute
and include that next-minor upper bound, save the file, then build and verify
the wheel metadata with: python -m build && twine check dist/* to confirm the
upper bound appears.

[![Python](https://img.shields.io/pypi/pyversions/{{ cookiecutter.package_name }}.svg?color=%2334D058)](https://pypi.org/project/{{ cookiecutter.package_name }}/)
[![PyPI](https://img.shields.io/pypi/v/{{ cookiecutter.package_name }}?color=%2334D058&label=pypi%20package)](https://pypi.org/project/{{ cookiecutter.package_name }}/)
[![PyPI Downloads](https://static.pepy.tech/badge/{{ cookiecutter.package_name }})](https://pepy.tech/projects/{{ cookiecutter.package_name }})
[![GitHub License](https://img.shields.io/github/license/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }})](https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }}/blob/master/LICENSE)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Make license badge branch-agnostic (master → HEAD).

Hardcoding master will 404 if the default branch is main. Use HEAD to work for both.

-[![GitHub License](https://img.shields.io/github/license/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }})](https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }}/blob/master/LICENSE)
+[![GitHub License](https://img.shields.io/github/license/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }})](https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }}/blob/HEAD/LICENSE)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[![GitHub License](https://img.shields.io/github/license/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }})](https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }}/blob/master/LICENSE)
[![GitHub License](https://img.shields.io/github/license/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }})](https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }}/blob/HEAD/LICENSE)
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

6-6: Bare URL used

(MD034, no-bare-urls)

🤖 Prompt for AI Agents
In {{cookiecutter.package_name}}/README.md around line 6, the GitHub license
badge link hardcodes the "master" branch which breaks repositories using "main";
update the badge URL to use "HEAD" instead of "master" so the badge points to
the repository's default branch and won't 404.

Comment on lines +20 to +21
[![Codecov](https://codecov.io/gh/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }}/branch/master/graph/badge.svg)](https://codecov.io/gh/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }})
{%- endif %}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Parametrize Codecov badge branch (avoid master-only).

If the repo default branch is main, the badge may be wrong. Either omit the branch or template it. Recommended: add a cookiecutter default_branch variable (see cookiecutter.json comment) and use it here.

-{%- if cookiecutter.include_codecov == "y" %}
-[![Codecov](https://codecov.io/gh/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }}/branch/master/graph/badge.svg)](https://codecov.io/gh/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }})
+{%- if cookiecutter.include_codecov == "y" %}
+[![Codecov](https://codecov.io/gh/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }}/branch/{{ cookiecutter.default_branch }}/graph/badge.svg)](https://codecov.io/gh/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }})
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[![Codecov](https://codecov.io/gh/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }}/branch/master/graph/badge.svg)](https://codecov.io/gh/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }})
{%- endif %}
{%- if cookiecutter.include_codecov == "y" %}
[![Codecov](https://codecov.io/gh/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }}/branch/{{ cookiecutter.default_branch }}/graph/badge.svg)](https://codecov.io/gh/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }})
{%- endif %}
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

20-20: Bare URL used

(MD034, no-bare-urls)

🤖 Prompt for AI Agents
In {{cookiecutter.package_name}}/README.md around lines 20-21 the Codecov badge
hardcodes the branch as "master", which breaks projects whose default branch is
"main"; add a cookiecutter variable (e.g., default_branch) in cookiecutter.json
with a sensible default and replace the hardcoded "master" in the badge URL with
that templated variable (or remove the /branch/... segment entirely to avoid
pinning a branch), and also update cookiecutter.json comments to document the
new default_branch option.

Comment on lines +2 to +7
"package_name": "my_python_package",
"project_short_description": "A modern Python package",
"author_name": "Your Name",
"author_email": "[email protected]",
"github_username": "yourusername",
"version": "0.1.0",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Separate distribution name from import package name to avoid invalid module names.

Using a single package_name for both the PyPI distribution and the Python import package breaks when users include hyphens (valid on PyPI, invalid for Python modules). Introduce package_import_name and use it for the inner Python package directory and import paths.

 {
     "package_name": "my_python_package",
+    "package_import_name": "my_python_package",
     "project_short_description": "A modern Python package",
     "author_name": "Your Name",
     "author_email": "[email protected]",
     "github_username": "yourusername",
     "version": "0.1.0",
+    "default_branch": ["main", "master"],

Follow-ups (outside this file):

  • Rename the inner package directory to {{ cookiecutter.package_import_name }}/
  • In pyproject.toml: [project].name = "{{ cookiecutter.package_name }}", but source layout, package includes, and any module imports should reference package_import_name.
  • Replace occurrences of {{ cookiecutter.package_name }} in import paths with {{ cookiecutter.package_import_name }}.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"package_name": "my_python_package",
"project_short_description": "A modern Python package",
"author_name": "Your Name",
"author_email": "[email protected]",
"github_username": "yourusername",
"version": "0.1.0",
"package_name": "my_python_package",
"package_import_name": "my_python_package",
"project_short_description": "A modern Python package",
"author_name": "Your Name",
"author_email": "[email protected]",
"github_username": "yourusername",
"version": "0.1.0",
"default_branch": ["main", "master"],
🤖 Prompt for AI Agents
In cookiecutter.json around lines 2 to 7, the template uses a single
package_name for both the PyPI distribution and the Python import package which
fails when distribution names contain hyphens; add a new key package_import_name
(e.g. a sanitized Python identifier) and update the template so package_name
remains the distribution name while package_import_name is used for the inner
package directory and import paths; follow up by renaming the inner package
directory to {{ cookiecutter.package_import_name }}, setting [project].name =
"{{ cookiecutter.package_name }}" in pyproject.toml, and replacing any
occurrences of {{ cookiecutter.package_name }} in imports or package includes
with {{ cookiecutter.package_import_name }}.

Comment on lines +8 to +9
"python_min_version": ["3.9", "3.10", "3.11", "3.12", "3.13"],
"python_max_version": ["3.9", "3.10", "3.11", "3.12", "3.13"],
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Guard against selecting an invalid Python version range.

It’s possible to pick min > max. Add a post_gen validation to fail fast or auto-correct.

Proposed post_gen snippet (in hooks/post_gen_project.py):

min_v = tuple(map(int, "{{ cookiecutter.python_min_version }}".split(".")))
max_v = tuple(map(int, "{{ cookiecutter.python_max_version }}".split(".")))
if min_v > max_v:
    raise SystemExit(f"python_min_version ({min_v}) cannot exceed python_max_version ({max_v}).")
🤖 Prompt for AI Agents
In cookiecutter.json around lines 8-9 the template allows selecting a
python_min_version that can be greater than python_max_version; add a
hooks/post_gen_project.py that parses both selected version strings into tuples
of integers, compares them, and either raises a SystemExit with a clear message
if min_v > max_v or swaps/auto-corrects them before continuing (choose one
behavior and implement consistently); ensure the validation runs immediately
after project generation and uses the cookiecutter context values "{{
cookiecutter.python_min_version }}" and "{{ cookiecutter.python_max_version }}"
when building the tuples.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant